diff --git a/.nvmrc b/.nvmrc index 68c98aa7a7..16a4acdae1 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18.18.2 \ No newline at end of file +v22.15.1 \ No newline at end of file diff --git a/.version b/.version index 7c69a55dbb..1eeac129c5 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -3.7.0 +3.16.0 diff --git a/.vscode/settings.json b/.vscode/settings.json index 35cab5fefc..ac6e6079cc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "[javascript, typescript]": { - "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "eslint.validate": [ "javascript", diff --git a/CODEOWNERS b/CODEOWNERS index 63a9ee034f..d2c5181abc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -17,6 +17,9 @@ /package.json @shah21 @gsmithun4 @adishm98 /package-lock.json @shah21 @gsmithun4 @adishm98 -# Server service files -/server/src/services/email.service.ts @shah21 @gsmithun4 -/server/src/mails @shah21 @gsmithun4 +# Code owners for all module.ts files +**/module.ts @shah21 @gsmithun4 + +# Server migration directories +/server/migrations/* @shah21 @gsmithun4 +/server/data-migrations/* @shah21 @gsmithun4 diff --git a/README.md b/README.md index fe93fc4dd9..aec5952469 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ -ToolJet is an **open-source low-code** platform for building and deploying internal tools with minimal engineering effort. With its drag-and-drop app-builder, you can create complex, responsive applications in minutes. ToolJet supports integration with a wide range of data sources, including databases like PostgreSQL, MongoDB, and Elasticsearch; API endpoints with OpenAPI spec and OAuth2 support; SaaS tools such as Stripe, Slack, Google Sheets, Airtable, and Notion; and object storage services like S3, Google Cloud Storage, and MinIO — enabling you to fetch, transform, and write data with ease. +ToolJet is an **open-source low-code framework** to build and deploy internal tools with minimal engineering effort. ToolJet's drag-and-drop frontend builder allows you to create complex, responsive frontends within minutes. Additionally, you can integrate various data sources, including databases like PostgreSQL, MongoDB, and Elasticsearch; API endpoints with OpenAPI spec and OAuth2 support; SaaS tools such as Stripe, Slack, Google Sheets, Airtable, and Notion; as well as object storage services like S3, GCS, and Minio, to fetch and write data. -ToolJet supports **AI integrations** with services like OpenAI, Hugging Face, Mistral, and more — allowing you to build secure AI-powered applications such as chat assistants, document analyzers, or content generators, all within the same low-code environment. + :star: If you find ToolJet useful, please consider giving us a star on GitHub! Your support helps us continue to innovate and deliver exciting features. -:star: If you find ToolJet useful, please consider giving us a star on GitHub! Your support helps us continue to innovate and deliver exciting features. - -![Docker Cloud Build Status](https://img.shields.io/docker/automated/tooljet/tooljet-ce) +![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/tooljet/tooljet-ce) ![Number of GitHub contributors](https://img.shields.io/github/contributors/tooljet/tooljet) [![Number of GitHub issues that are open](https://img.shields.io/github/issues/ToolJet/ToolJet)](https://github.com/ToolJet/ToolJet/issues) [![Number of GitHub stars](https://img.shields.io/github/stars/ToolJet/ToolJet)](https://github.com/ToolJet/ToolJet/stargazers) @@ -16,30 +14,31 @@ ToolJet supports **AI integrations** with services like OpenAI, Hugging Face, Mi [![Follow us on X, formerly Twitter](https://img.shields.io/twitter/follow/ToolJet?style=social)](https://twitter.com/ToolJet)

- Tooljet dashboard + Tooljet dashboard showing inventory and orders

- Platform overview +

## All features -- **Visual App Builder:** 60+ built-in responsive components, including Tables, Charts, Lists, Forms, and Progress Bars. +- **Visual App Builder:** 45+ built-in responsive components, including Tables, Charts, Lists, Forms, and Progress Bars. - **ToolJet Database:** Built-in no-code database. - **Multi-Page:** Build an application with multiple pages. - **Multiplayer editing:** Allows simultaneous app building by multiple developers. -- **65+ data sources:** Integrate with external databases, cloud storage, and APIs. -- **AI-powered applications:** Connect with OpenAI, Hugging Face, Mistral, and more to build intelligent apps powered by state-of-the-art language models. +- **50+ data sources:** Integrate with external databases, cloud storage, and APIs. - **Desktop & mobile:** Customize layout widths to fit various screen sizes. -- **Self-host:** Supports Docker, Kubernetes, AWS AMI, Google Cloud Run, and more. +- **Self-host:** Supports Docker, Kubernetes, AWS EC2, Google Cloud Run, and more. - **Collaborate:** Add comments anywhere on the canvas and tag your team members. - **Extend with plugins:** Use our [command-line tool](https://www.npmjs.com/package/@tooljet/cli) to easily bootstrap new connectors. - **Version control:** Manage multiple application versions with a structured release cycle. - **Run JS & Python code:** Execute custom JavaScript and Python snippets. - **Granular access control:** Set permissions at both group and app levels. +- **Low-code:** Use JS code almost anywhere within the builder, such as setting text color based on status with +`status === 'success' ? 'green' : 'red'`. - **No-code query editors:** Query Editors are available for all supported data sources. -- **Transform data:** Process and transform complex data using JavaScript or Python code. +- **Join and transform data:** Transform query results using JavaScript or Python code. - **Secure:** All the credentials are securely encrypted using `aes-256-gcm`. - **Data Privacy:** ToolJet serves solely as a proxy and does not store data. - **SSO:** Supports multiple Single Sign-On providers. @@ -47,13 +46,12 @@ ToolJet supports **AI integrations** with services like OpenAI, Hugging Face, Mi
## Quickstart - The easiest way to get started with ToolJet is by creating a [ToolJet Cloud](https://tooljet.ai) account. ToolJet Cloud offers a hosted solution of ToolJet. If you want to self-host ToolJet, kindly proceed to [deployment documentation](https://docs.tooljet.ai/docs/setup/). ### Try using Docker - Want to give ToolJet a quick spin on your local machine? You can run the following command from your terminal to have ToolJet up and running right away. + ```bash docker run \ --name tooljet \ @@ -64,16 +62,15 @@ docker run \ tooljet/try:ee-lts-latest ``` -_For users upgrading their ToolJet version, we recommend choosing the LTS version over the latest version. The LTS version ensures stability with production bug fixes, security patches, and performance enhancements._ +*For users upgrading their ToolJet version, we recommend choosing the LTS version over the latest version. The LTS version ensures stability with production bug fixes, security patches, and performance enhancements.* ## Tutorials and examples -[Build an Employee Directory](https://docs.tooljet.ai/docs/#quickstart-guide/)
-[Build your own Ed Tech CRM](https://blog.tooljet.ai/building-an-ed-tech-sales-crm-using-tooljet/)
-[Build an Employee Engagement Survey Dashboard](https://blog.tooljet.ai/build-an-employee-engagement-survey-dashboard-using-tooljet/)
+[Time Tracker Application](https://docs.tooljet.ai/docs/#quickstart-guide)
+[Build your own CMS using low-code](https://blog.tooljet.ai/build-cms-using-lowcode-and-mongodb/)
+[AWS S3 Browser](https://blog.tooljet.ai/build-an-aws-s3-broswer-with-tooljet/)
## Documentation - Documentation is available at https://docs.tooljet.ai. - [Getting Started](https://docs.tooljet.ai)
@@ -81,33 +78,30 @@ Documentation is available at https://docs.tooljet.ai. - [Component Reference](https://docs.tooljet.ai/docs/widgets/button) ## Self-hosted +You can use ToolJet Cloud for a fully managed solution. If you want to self-host ToolJet, we have guides on deploying ToolJet on Kubernetes, AWS EC2, Docker, and more. -You can use ToolJet Cloud for a fully managed solution. If you want to self-host ToolJet, we have guides on deploying ToolJet on Kubernetes, AWS AMI, Docker, and more. - -| Provider | Documentation | -| :----------------------------- | :---------------------------------------------------------- | -| Digital Ocean | [Link](https://docs.tooljet.ai/docs/setup/digitalocean) | -| Docker | [Link](https://docs.tooljet.ai/docs/setup/docker) | -| AWS AMI | [Link](https://docs.tooljet.ai/docs/setup/ami) | -| AWS ECS | [Link](https://docs.tooljet.ai/docs/setup/ecs) | -| OpenShift | [Link](https://docs.tooljet.ai/docs/setup/openshift) | -| Helm | [Link](https://docs.tooljet.ai/docs/setup/helm) | -| AWS EKS (Kubernetes) | [Link](https://docs.tooljet.ai/docs/setup/kubernetes) | -| GCP GKE (Kubernetes) | [Link](https://docs.tooljet.ai/docs/setup/kubernetes-gke) | -| Azure AKS (Kubernetes) | [Link](https://docs.tooljet.ai/docs/setup/kubernetes-aks) | -| Azure Container | [Link](https://docs.tooljet.ai/docs/setup/azure-container) | -| Google Cloud Run | [Link](https://docs.tooljet.ai/docs/setup/google-cloud-run) | -| Deploying ToolJet client | [Link](https://docs.tooljet.ai/docs/setup/client) | -| Deploying ToolJet on a Subpath | [Link](https://docs.tooljet.ai/docs/setup/tooljet-subpath/) | - -## Marketplace +| Provider | Documentation | +| :------------- | :------------- | +| Digital Ocean | [Link](https://docs.tooljet.ai/docs/setup/digitalocean) | +| Docker | [Link](https://docs.tooljet.ai/docs/setup/docker) | +| AWS EC2 | [Link](https://docs.tooljet.ai/docs/setup/ec2) | +| AWS ECS | [Link](https://docs.tooljet.ai/docs/setup/ecs) | +| OpenShift | [Link](https://docs.tooljet.ai/docs/setup/openshift) | +| Helm | [Link](https://docs.tooljet.ai/docs/setup/helm) | +| AWS EKS (Kubernetes) | [Link](https://docs.tooljet.ai/docs/setup/kubernetes) | +| GCP GKE (Kubernetes) | [Link](https://docs.tooljet.ai/docs/setup/kubernetes-gke) | +| Azure AKS (Kubernetes) | [Link](https://docs.tooljet.ai/docs/setup/kubernetes-aks) | +| Azure Container | [Link](https://docs.tooljet.ai/docs/setup/azure-container) | +| Google Cloud Run | [Link](https://docs.tooljet.ai/docs/setup/google-cloud-run) | +| Deploying ToolJet client | [Link](https://docs.tooljet.ai/docs/setup/client) | +| Deploying ToolJet on a Subpath | [Link](https://docs.tooljet.ai/docs/setup/tooljet-subpath/) | +## Marketplace ToolJet can now be found on both AWS and Azure Marketplaces, making it simpler than ever to access and deploy our app-building platform. Find ToolJet on AWS Marketplace [here](https://aws.amazon.com/marketplace/pp/prodview-fxjto27jkpqfg?sr=0-1&ref_=beagle&applicationId=AWSMPContessa) and explore seamless integration on Azure Marketplace [here](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/tooljetsolutioninc1679496832216.tooljet?tab=Overview). ## Community support - For general help using ToolJet, please refer to the official [documentation](https://docs.tooljet.ai/docs/). For additional help, you can use one of these channels to ask a question: - [Slack](https://tooljet.ai/slack) - Discussions with the community and the team. @@ -115,24 +109,19 @@ For general help using ToolJet, please refer to the official [documentation](htt - [𝕏 (Twitter)](https://twitter.com/ToolJet) - Get the product updates quickly. ## Roadmap - Check out our [roadmap](https://github.com/orgs/ToolJet/projects/15) to stay updated on recently released features and learn about what's coming next. ## Branching model - We use the git-flow branching model. The base branch is `develop`. If you are looking for a stable version, please use the main branch or tags labeled as v1.x.x. ## Contributing - Kindly read our [Contributing Guide](CONTRIBUTING.md) to familiarize yourself with ToolJet's development process, how to suggest bug fixes and improvements, and the steps for building and testing your changes.
## Contributors - ## License - ToolJet © 2023, ToolJet Solutions Inc - Released under the GNU Affero General Public License v3.0. diff --git a/cypress-tests/cypress-app-builder.config.js b/cypress-tests/cypress-app-builder.config.js index 5d291f59c1..88b9927d46 100644 --- a/cypress-tests/cypress-app-builder.config.js +++ b/cypress-tests/cypress-app-builder.config.js @@ -62,7 +62,7 @@ module.exports = defineConfig({ }); on("task", { - updateId({ dbconfig, sql }) { + dbConnection({ dbconfig, sql }) { const client = new pg.Pool(dbconfig); return client.query(sql); }, @@ -76,8 +76,9 @@ module.exports = defineConfig({ experimentalRunAllSpecs: true, baseUrl: "http://localhost:8082", specPattern: [ - "cypress/e2e/happyPath/appbuilder/commonTestcases/**/*.cy.js", - "cypress/e2e/happyPath/appbuilder/ceTestcases/**/*.cy.js" + // "cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/**/*.cy.js", + // "cypress/e2e/happyPath/appbuilder/ceTestcases/**/*.cy.js", + "cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/globalSetingsHappyPath.cy.js" ], numTestsKeptInMemory: 1, redirectionLimit: 7, diff --git a/cypress-tests/cypress-ee-platform.config.js b/cypress-tests/cypress-ee-platform.config.js new file mode 100644 index 0000000000..fb4f45faeb --- /dev/null +++ b/cypress-tests/cypress-ee-platform.config.js @@ -0,0 +1,116 @@ +const { defineConfig } = require("cypress"); +const { rmdir } = require("fs"); +const fs = require("fs"); +const XLSX = require("node-xlsx"); +const pg = require("pg"); +const path = require("path"); +const pdf = require("pdf-parse"); + +const environments = { + 'run-cypress-platform': { + baseUrl: "http://localhost:3000", + configFile: "cypress-platform.config.js" + }, + 'run-cypress-platform-subpath': { + baseUrl: "http://localhost:3000/apps", + configFile: "cypress-platform.config.js" + }, + 'run-cypress-platform-proxy': { + baseUrl: "http://localhost:4001", + configFile: "cypress-platform.config.js" + }, + 'run-cypress-platform-proxy-subpath': { + baseUrl: "http://localhost:4001/apps", + configFile: "cypress-platform.config.js" + } +}; + +const githubLabel = process.env.GITHUB_LABEL || 'run-cypress-platform'; +const environment = environments[githubLabel]; + +module.exports = defineConfig({ + execTimeout: 1800000, + defaultCommandTimeout: 30000, + requestTimeout: 30000, + pageLoadTimeout: 30000, + responseTimeout: 30000, + viewportWidth: 1440, + viewportHeight: 960, + chromeWebSecurity: false, + trashAssetsBeforeRuns: true, + e2e: { + setupNodeEvents (on, config) { + config.baseUrl = environment.baseUrl; + + on("task", { + readPdf (pathToPdf) { + return new Promise((resolve) => { + const pdfPath = path.resolve(pathToPdf); + let dataBuffer = fs.readFileSync(pdfPath); + pdf(dataBuffer).then(function ({ text }) { + resolve(text); + }); + }); + }, + }); + + on("task", { + readXlsx (filePath) { + return new Promise((resolve, reject) => { + try { + let dataBuffer = fs.readFileSync(filePath); + const jsonData = XLSX.parse(dataBuffer); + resolve(jsonData[0]["data"].toString()); + } catch (e) { + reject(e); + } + }); + }, + }); + + on("task", { + deleteFolder (folderName) { + return new Promise((resolve, reject) => { + rmdir(folderName, { maxRetries: 10, recursive: true }, (err) => { + if (err) { + console.error(err); + return reject(err); + } + resolve(null); + }); + }); + }, + }); + + on("task", { + dbConnection ({ dbconfig, sql }) { + const client = new pg.Pool(dbconfig); + return client.query(sql); + }, + }); + + return require("./cypress/plugins/index.js")(on, config); + }, + downloadsFolder: "cypress/downloads", + experimentalRunAllSpecs: true, + experimentalModfyObstructiveThirdPartyCode: true, + baseUrl: environment.baseUrl, + configFile: environment.configFile, + specPattern: [ + "cypress/e2e/happyPath/platform/firstUser/firstUserOnboarding.cy.js", + "cypress/e2e/happyPath/platform/ceTestcases/apps/appSlug.cy.js", + "cypress/e2e/happyPath/platform/ceTestcases/apps/!(*appSlug).cy.js", + "cypress/e2e/happyPath/platform/commonTestcases/userManagment/*.cy.js", + "cypress/e2e/happyPath/platform/eeTestcases/workspace/*.cy.js", + ], + numTestsKeptInMemory: 1, + redirectionLimit: 15, + experimentalMemoryManagement: true, + video: false, + videoUploadOnPasses: false, + retries: { + runMode: 2, + openMode: 0, + }, + }, +}); \ No newline at end of file diff --git a/cypress-tests/cypress-marketplace.config.js b/cypress-tests/cypress-marketplace.config.js index 7536a87482..ce955b3c66 100644 --- a/cypress-tests/cypress-marketplace.config.js +++ b/cypress-tests/cypress-marketplace.config.js @@ -19,9 +19,9 @@ module.exports = defineConfig({ trashAssetsBeforeRuns: true, e2e: { - setupNodeEvents(on, config) { + setupNodeEvents (on, config) { on("task", { - readPdf(pathToPdf) { + readPdf (pathToPdf) { return new Promise((resolve) => { const pdfPath = path.resolve(pathToPdf); let dataBuffer = fs.readFileSync(pdfPath); @@ -33,7 +33,7 @@ module.exports = defineConfig({ }); on("task", { - readXlsx(filePath) { + readXlsx (filePath) { return new Promise((resolve, reject) => { try { let dataBuffer = fs.readFileSync(filePath); @@ -48,7 +48,7 @@ module.exports = defineConfig({ }); on("task", { - deleteFolder(folderName) { + deleteFolder (folderName) { return new Promise((resolve, reject) => { rmdir(folderName, { maxRetries: 10, recursive: true }, (err) => { if (err) { @@ -62,7 +62,7 @@ module.exports = defineConfig({ }); on("task", { - updateId({ dbconfig, sql }) { + dbConnection ({ dbconfig, sql }) { const client = new pg.Pool(dbconfig); return client.query(sql); }, @@ -77,7 +77,7 @@ module.exports = defineConfig({ baseUrl: "http://localhost:8082", specPattern: [ "cypress/e2e/happyPath/marketplace/commonTestcases/**/*.cy.js", - ], + ] numTestsKeptInMemory: 1, redirectionLimit: 7, experimentalRunAllSpecs: true, diff --git a/cypress-tests/cypress-platform.config.js b/cypress-tests/cypress-platform.config.js index 465c3c6c0e..6b1954140a 100644 --- a/cypress-tests/cypress-platform.config.js +++ b/cypress-tests/cypress-platform.config.js @@ -8,7 +8,7 @@ const pdf = require("pdf-parse"); const environments = { 'run-cypress-platform': { - baseUrl: "http://localhost:8082", + baseUrl: "http://localhost:3000", configFile: "cypress-platform.config.js" }, 'run-cypress-platform-subpath': { @@ -83,7 +83,7 @@ module.exports = defineConfig({ }); on("task", { - updateId ({ dbconfig, sql }) { + dbConnection ({ dbconfig, sql }) { const client = new pg.Pool(dbconfig); return client.query(sql); }, @@ -97,8 +97,9 @@ module.exports = defineConfig({ baseUrl: environment.baseUrl, configFile: environment.configFile, specPattern: [ - "cypress/e2e/happyPath/platform/ceTestcases/userFlow/firstUserOnboarding.cy.js", - "cypress/e2e/happyPath/platform/ceTestcases/!(userFlow)/**/*.cy.js", + "cypress/e2e/happyPath/platform/firstUser/firstUserOnboarding.cy.js", + "cypress/e2e/happyPath/platform/ceTestcases/apps/appSlug.cy.js", + "cypress/e2e/happyPath/platform/ceTestcases/**/!(*appSlug).cy.js", "cypress/e2e/happyPath/platform/commonTestcases/**/*.cy.js", ], numTestsKeptInMemory: 1, diff --git a/cypress-tests/cypress-run.config.js b/cypress-tests/cypress-run.config.js index 7a09e7218a..10445c10b2 100644 --- a/cypress-tests/cypress-run.config.js +++ b/cypress-tests/cypress-run.config.js @@ -19,9 +19,9 @@ module.exports = defineConfig({ trashAssetsBeforeRuns: true, e2e: { - setupNodeEvents(on, config) { + setupNodeEvents (on, config) { on("task", { - readPdf(pathToPdf) { + readPdf (pathToPdf) { return new Promise((resolve) => { const pdfPath = path.resolve(pathToPdf); let dataBuffer = fs.readFileSync(pdfPath); @@ -33,7 +33,7 @@ module.exports = defineConfig({ }); on("task", { - readXlsx(filePath) { + readXlsx (filePath) { return new Promise((resolve, reject) => { try { let dataBuffer = fs.readFileSync(filePath); @@ -48,7 +48,7 @@ module.exports = defineConfig({ }); on("task", { - deleteFolder(folderName) { + deleteFolder (folderName) { return new Promise((resolve, reject) => { rmdir(folderName, { maxRetries: 10, recursive: true }, (err) => { if (err) { @@ -62,7 +62,7 @@ module.exports = defineConfig({ }); on("task", { - updateId({ dbconfig, sql }) { + dbConnection ({ dbconfig, sql }) { const client = new pg.Pool(dbconfig); return client.query(sql); }, diff --git a/cypress-tests/cypress.Dockerfile b/cypress-tests/cypress.Dockerfile new file mode 100644 index 0000000000..608852e04c --- /dev/null +++ b/cypress-tests/cypress.Dockerfile @@ -0,0 +1,198 @@ +FROM node:22.15.1 AS builder +# Fix for JS heap limit allocation issue +ENV NODE_OPTIONS="--max-old-space-size=4096" + +RUN mkdir -p /app + +WORKDIR /app + +# Set GitHub token and branch as build arguments +ARG CUSTOM_GITHUB_TOKEN +ARG BRANCH_NAME + +# Clone and checkout the frontend repository +RUN git config --global url."https://x-access-token:${CUSTOM_GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" + +RUN git config --global http.version HTTP/1.1 +RUN git config --global http.postBuffer 524288000 +RUN git clone https://github.com/ToolJet/ToolJet.git . + +# The branch name needs to be changed the branch with modularisation in CE repo +RUN git checkout ${BRANCH_NAME} + +RUN git submodule update --init --recursive + +# Checkout the same branch in submodules if it exists, otherwise fallback to main +RUN git submodule foreach " \ + if git show-ref --verify --quiet refs/heads/${BRANCH_NAME} || \ + git ls-remote --exit-code --heads origin ${BRANCH_NAME}; then \ + git checkout ${BRANCH_NAME}; \ + else \ + echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to main'; \ + git checkout main; \ + fi" + +# Scripts for building +COPY ./package.json ./package.json + +# Build plugins +COPY ./plugins/package.json ./plugins/package-lock.json ./plugins/ +RUN npm --prefix plugins install +COPY ./plugins/ ./plugins/ +RUN NODE_ENV=production npm --prefix plugins run build +RUN npm --prefix plugins prune --production + +ENV TOOLJET_EDITION=ee + +# Build frontend +COPY ./frontend/package.json ./frontend/package-lock.json ./frontend/ +RUN npm --prefix frontend install +COPY ./frontend/ ./frontend/ +RUN npm --prefix frontend run build --production +RUN npm --prefix frontend prune --production + +ENV NODE_ENV=production +ENV TOOLJET_EDITION=ee + +# Build server +COPY ./server/package.json ./server/package-lock.json ./server/ +RUN npm --prefix server install +COPY ./server/ ./server/ +RUN npm install -g @nestjs/cli +RUN npm install -g copyfiles +RUN npm --prefix server run build + +FROM node:22.15.1-bullseye + +RUN apt-get update -yq \ + && apt-get install curl wget gnupg zip -yq \ + && apt-get install -yq build-essential \ + && apt -y install redis \ + && apt-get clean -y + +# copy postgrest executable +COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin + +ENV NODE_ENV=production +ENV TOOLJET_EDITION=ee +ENV NODE_OPTIONS="--max-old-space-size=4096" +RUN apt-get update && apt-get install -y freetds-dev libaio1 wget supervisor + +# Install Instantclient Basic Light Oracle and Dependencies +WORKDIR /opt/oracle +RUN wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linuxx64.zip && \ + wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \ + unzip instantclient-basiclite-linuxx64.zip && rm -f instantclient-basiclite-linuxx64.zip && \ + unzip instantclient-basiclite-linux.x64-11.2.0.4.0.zip && rm -f instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \ + cd /opt/oracle/instantclient_21_10 && rm -f *jdbc* *occi* *mysql* *mql1* *ipc1* *jar uidrvci genezi adrci && \ + cd /opt/oracle/instantclient_11_2 && rm -f *jdbc* *occi* *mysql* *mql1* *ipc1* *jar uidrvci genezi adrci && \ + echo /opt/oracle/instantclient* > /etc/ld.so.conf.d/oracle-instantclient.conf && ldconfig +# Set the Instant Client library paths +ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21_10:${LD_LIBRARY_PATH}" + +WORKDIR / + +# copy npm scripts +COPY --from=builder /app/package.json ./app/package.json +# copy plugins dependencies +COPY --from=builder /app/plugins/dist ./app/plugins/dist +COPY --from=builder /app/plugins/client.js ./app/plugins/client.js +COPY --from=builder /app/plugins/node_modules ./app/plugins/node_modules +COPY --from=builder /app/plugins/packages/common ./app/plugins/packages/common +COPY --from=builder /app/plugins/package.json ./app/plugins/package.json +# copy frontend build +COPY --from=builder /app/frontend/build ./app/frontend/build +# copy server build +COPY --from=builder /app/server/package.json ./app/server/package.json +COPY --from=builder /app/server/.version ./app/server/.version +COPY --from=builder /app/server/ee/keys ./app/server/ee/keys +COPY --from=builder /app/server/node_modules ./app/server/node_modules +COPY --from=builder /app/server/templates ./app/server/templates +COPY --from=builder /app/server/scripts ./app/server/scripts +COPY --from=builder /app/server/dist ./app/server/dist + +WORKDIR /app + +# Install PostgreSQL +USER root +RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - +RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list +RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor --fix-missing + + +# Explicitly create PG main directory with correct ownership +RUN mkdir -p /var/lib/postgresql/13/main && \ + chown -R postgres:postgres /var/lib/postgresql + +RUN mkdir -p /var/log/supervisor /var/run/postgresql && \ + chown -R postgres:postgres /var/run/postgresql /var/log/supervisor + +# Remove existing data and create directory with proper ownership +RUN rm -rf /var/lib/postgresql/13/main && \ + mkdir -p /var/lib/postgresql/13/main && \ + chown -R postgres:postgres /var/lib/postgresql + +# Initialize PostgreSQL +RUN su - postgres -c "/usr/lib/postgresql/13/bin/initdb -D /var/lib/postgresql/13/main" + +# Configure Supervisor to manage PostgREST, ToolJet, and Redis +RUN echo "[supervisord] \n" \ + "nodaemon=true \n" \ + "user=root \n" \ + "\n" \ + "[program:redis] \n" \ + "command=redis-server /etc/redis/redis.conf \n" \ + "user=redis \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/var/log/redis/redis-server.log \n" \ + "stdout_logfile=/var/log/redis/redis-server.log \n" \ + "\n" \ + "[program:postgrest] \n" \ + "command=/bin/postgrest \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "\n" \ + "[program:tooljet] \n" \ + "user=root \n" \ + "command=/bin/bash -c '/app/server/scripts/boot.sh' \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/dev/stdout \n" \ + "stderr_logfile_maxbytes=0 \n" \ + "stdout_logfile=/dev/stdout \n" \ + "stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf + +# ENV defaults +ENV TOOLJET_HOST=http://localhost \ + PORT=3000 \ + NODE_ENV=production \ + LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \ + SECRET_KEY_BASE=replace_with_secret_key_base \ + PG_DB=tooljet_production \ + PG_USER=postgres \ + PG_PASS=postgres \ + PG_HOST=localhost \ + ENABLE_TOOLJET_DB=true \ + TOOLJET_DB_HOST=localhost \ + TOOLJET_DB_USER=postgres \ + TOOLJET_DB_PASS=postgres \ + TOOLJET_DB=tooljet_db \ + PGRST_HOST=http://localhost:3001 \ + PGRST_SERVER_PORT=3001 \ + PGRST_DB_URI=postgres://postgres:postgres@localhost/tooljet_db \ + PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \ + PGRST_DB_PRE_CONFIG=postgrest.pre_config \ + REDIS_HOST=localhost \ + REDIS_PORT=6379 \ + REDIS_USER= \ + REDIS_PASSWORD= \ + ORM_LOGGING=true \ + DEPLOYMENT_PLATFORM=docker:local \ + HOME=/home/appuser \ + TERM=xterm + + +RUN chmod +x ./server/scripts/preview.sh +# Set the entrypoint +ENTRYPOINT ["./server/scripts/preview.sh"] diff --git a/cypress-tests/cypress.config.js b/cypress-tests/cypress.config.js index 5fa2e2a56d..f44a6c9c72 100644 --- a/cypress-tests/cypress.config.js +++ b/cypress-tests/cypress.config.js @@ -66,7 +66,7 @@ module.exports = defineConfig({ }); on("task", { - updateId ({ dbconfig, sql }) { + dbConnection ({ dbconfig, sql }) { const client = new pg.Pool(dbconfig); return client.query(sql); }, diff --git a/cypress-tests/cypress/commands/apiCommands.js b/cypress-tests/cypress/commands/apiCommands.js index 0a0dc58e3e..60e747a48c 100644 --- a/cypress-tests/cypress/commands/apiCommands.js +++ b/cypress-tests/cypress/commands/apiCommands.js @@ -1,3 +1,5 @@ +const envVar = Cypress.env("environment"); + Cypress.Commands.add( "apiLogin", ( @@ -50,7 +52,7 @@ Cypress.Commands.add("apiCreateGDS", (url, name, kind, options) => { log: false; } expect(response.status).to.equal(201); - Cypress.env(`${name}-id`, response.body.id); + Cypress.env(`${name}`, response.body.id); Cypress.log({ name: "Create Data Source", @@ -61,6 +63,31 @@ Cypress.Commands.add("apiCreateGDS", (url, name, kind, options) => { }); }); +Cypress.Commands.add("apiFetchDataSourcesId", () => { + cy.getAuthHeaders().then((headers) => { + cy.request({ + method: "GET", + url: `${Cypress.env("server_host")}/api/data-sources/${Cypress.env("workspaceId")}/environments/${Cypress.env("environmentId")}/versions/${Cypress.env("editingVersionId")}`, + headers, + }).then((response) => { + expect(response.status).to.equal(200); + const dataSources = response.body?.data_sources || []; + + dataSources.forEach((item) => { + Cypress.env(`${item.kind}`, `${item.id}`); + }); + + Cypress.log({ + name: "DS Fetch", + displayName: "Data Sources Fetched", + message: dataSources + .map((ds) => `\nKind: '${ds.kind}', Name: '${ds.id}'`) + .join(","), + }); + }); + }); +}); + Cypress.Commands.add("apiCreateApp", (appName = "testApp") => { cy.window({ log: false }).then((win) => { win.localStorage.setItem("walkthroughCompleted", "true"); @@ -75,14 +102,17 @@ Cypress.Commands.add("apiCreateApp", (appName = "testApp") => { Cookie: `tj_auth_token = ${cookie.value}`, }, body: { - created_at: "", - id: "", - is_maintenance_on: false, - is_public: null, + type: "front-end", name: appName, + is_maintenance_on: false, organization_id: "", - updated_at: "", user_id: "", + created_at: "", + updated_at: "", + id: "", + is_public: null, + workflow_enabled: false, + creation_mode: "DEFAULT", }, }).then((response) => { { @@ -128,21 +158,17 @@ Cypress.Commands.add( appId = Cypress.env("appId"), componentSelector = "[data-cy='empty-editor-text']" ) => { - cy.intercept("GET", "/api/v2/apps/*").as("getAppData"); + cy.intercept("GET", "/api/apps/*").as("getAppData"); cy.window({ log: false }).then((win) => { win.localStorage.setItem("walkthroughCompleted", "true"); }); cy.visit(`/${workspaceId}/apps/${appId}/${slug}`); cy.wait("@getAppData").then((interception) => { - // Assuming the response body is a JSON object const responseData = interception.response.body; - // Set the response data as an environment variable - Cypress.env("apiResponseData", responseData); - - // You can log it to check if the env var is set correctly - cy.log(Cypress.env("apiResponseData")); + Cypress.env("editingVersionId", responseData.editing_version.id); + Cypress.env("environmentId", responseData.editorEnvironment.id); }); cy.get(componentSelector, { timeout: 10000 }); } @@ -166,6 +192,7 @@ Cypress.Commands.add("apiCreateWorkspace", (workspaceName, workspaceSlug) => { { log: false } ).then((response) => { expect(response.status).to.equal(201); + return response; }); }); }); @@ -175,7 +202,7 @@ Cypress.Commands.add("apiLogout", () => { cy.request( { method: "GET", - url: `${Cypress.env("server_host")}/api/logout`, + url: `${Cypress.env("server_host")}/api/session/logout`, headers: { "Tj-Workspace-Id": Cypress.env("workspaceId"), Cookie: `tj_auth_token=${cookie.value}`, @@ -190,22 +217,36 @@ Cypress.Commands.add("apiLogout", () => { Cypress.Commands.add( "apiUserInvite", - (userName, userEmail, userRole = "end-user") => { + (userName, userEmail, userRole = "end-user", metaData = {}) => { + const requestBody = + envVar === "Enterprise" + ? { + email: userEmail, + firstName: userName, + groups: [], + lastName: "", + role: userRole, + userMetadata: metaData, + } + : { + email: userEmail, + firstName: userName, + groups: [], + lastName: "", + role: userRole, + userMetadata: metaData, + }; + cy.getCookie("tj_auth_token").then((cookie) => { cy.request( { method: "POST", - url: `${Cypress.env("server_host")}/api/organization_users`, + url: `${Cypress.env("server_host")}/api/organization-users`, headers: { "Tj-Workspace-Id": Cypress.env("workspaceId"), Cookie: `tj_auth_token=${cookie.value}`, }, - body: { - first_name: userName, - email: userEmail, - groups: [], - role: userRole, - }, + body: requestBody, }, { log: false } ).then((response) => { @@ -221,21 +262,26 @@ Cypress.Commands.add("apiAddQuery", (queryName, query, dataQueryId) => { "Tj-Workspace-Id": Cypress.env("workspaceId"), Cookie: `tj_auth_token=${cookie.value}`, }; - cy.request({ - method: "PATCH", - url: `${Cypress.env("server_host")}/api/data_queries/${dataQueryId}`, - headers: headers, - body: { - name: queryName, - options: { - mode: "sql", - transformationLanguage: "javascript", - enableTransformation: false, - query: query, + + cy.apiGetAppData(Cypress.env("appId")).then((appData) => { + const editingVersionId = appData.editing_version.id; + + cy.request({ + method: "PATCH", + url: `${Cypress.env("server_host")}/api/data-queries/${dataQueryId}/versions/${editingVersionId}`, + headers: headers, + body: { + name: queryName, + options: { + mode: "sql", + transformationLanguage: "javascript", + enableTransformation: false, + query: query, + }, }, - }, - }).then((patchResponse) => { - expect(patchResponse.status).to.equal(200); + }).then((patchResponse) => { + expect(patchResponse.status).to.equal(200); + }); }); }); }); @@ -243,6 +289,9 @@ Cypress.Commands.add("apiAddQuery", (queryName, query, dataQueryId) => { Cypress.Commands.add( "apiAddQueryToApp", (queryName, options, dsName, dsKind) => { + cy.log( + `${Cypress.env("server_host")}/api/data-queries/data-sources/${Cypress.env(dsKind)}/versions/${Cypress.env("editingVersionId")}` + ); cy.getCookie("tj_auth_token", { log: false }).then((cookie) => { const authToken = `tj_auth_token=${cookie.value}`; const workspaceId = Cypress.env("workspaceId"); @@ -262,7 +311,7 @@ Cypress.Commands.add( cy.request({ method: "POST", - url: `${Cypress.env("server_host")}/api/data_queries`, + url: `${Cypress.env("server_host")}/api/data-queries/data-sources/${Cypress.env(dsKind)}/versions/${Cypress.env("editingVersionId")}`, headers: { "Content-Type": "application/json", Cookie: authToken, @@ -315,7 +364,7 @@ Cypress.Commands.add( cy.request({ method: "GET", - url: `${Cypress.env("server_host")}/api/v2/apps/${appId}`, + url: `${Cypress.env("server_host")}/api/apps/${appId}`, headers: { "Tj-Workspace-Id": Cypress.env("workspaceId"), Cookie: `tj_auth_token=${cookie.value}`, @@ -430,48 +479,54 @@ Cypress.Commands.add("apiMakeAppPublic", (appId = Cypress.env("appId")) => { }); }); -Cypress.Commands.add("apiDeleteGranularPermission", (groupName) => { +Cypress.Commands.add("apiDeleteGranularPermission", (groupName, typesToDelete = []) => { cy.getAuthHeaders().then((headers) => { - - // Fetch group permissions + // Step 1: Get the group by name cy.request({ method: "GET", - url: `${Cypress.env("server_host")}/api/v2/group_permissions`, - headers: headers, + url: `${Cypress.env("server_host")}/api/v2/group-permissions`, + headers, log: false, }).then((response) => { expect(response.status).to.equal(200); - const group = response.body.groupPermissions.find( - (g) => g.name === groupName - ); + const group = response.body.groupPermissions.find((g) => g.name === groupName); if (!group) throw new Error(`Group with name ${groupName} not found`); const groupId = group.id; - // Fetch granular permissions for the specific group + // Step 2: Get all granular permissions for the group cy.request({ method: "GET", - url: `${Cypress.env("server_host")}/api/v2/group_permissions/${groupId}/granular-permissions`, + url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/granular-permissions`, headers, log: false, }).then((granularResponse) => { expect(granularResponse.status).to.equal(200); - const granularPermissionId = granularResponse.body[0].id; + const granularPermissions = granularResponse.body; - // Delete the granular permission - cy.request({ - method: "DELETE", - url: `${Cypress.env("server_host")}/api/v2/group_permissions/granular-permissions/${granularPermissionId}`, - headers, - log: false, - }).then((deleteResponse) => { - expect(deleteResponse.status).to.equal(200); + // Step 3: Filter if typesToDelete is specified + const permissionsToDelete = typesToDelete.length + ? granularPermissions.filter((perm) => typesToDelete.includes(perm.type)) + : granularPermissions; + + // Step 4: Delete each granular permission + permissionsToDelete.forEach((permission) => { + cy.request({ + method: "DELETE", + url: `${Cypress.env("server_host")}/api/v2/group-permissions/granular-permissions/app/${permission.id}`, + headers, + log: false, + }).then((deleteResponse) => { + expect(deleteResponse.status).to.equal(200); + cy.log(`Deleted granular permission: ${permission.name}`); + }); }); }); }); }); }); + Cypress.Commands.add( "apiCreateGranularPermission", ( @@ -483,11 +538,10 @@ Cypress.Commands.add( resourcesToAdd = [] ) => { cy.getAuthHeaders().then((headers) => { - // Fetch group permissions cy.request({ method: "GET", - url: `${Cypress.env("server_host")}/api/v2/group_permissions`, + url: `${Cypress.env("server_host")}/api/v2/group-permissions`, headers: headers, log: false, }).then((response) => { @@ -502,7 +556,7 @@ Cypress.Commands.add( // Create granular permission cy.request({ method: "POST", - url: `${Cypress.env("server_host")}/api/v2/group_permissions/granular-permissions`, + url: `${Cypress.env("server_host")}/api/v2/group-permissions/granular-permissions`, headers: headers, body: { name, @@ -528,10 +582,9 @@ Cypress.Commands.add( Cypress.Commands.add("apiReleaseApp", (appName) => { cy.getAppId(appName).then((appId) => { cy.getAuthHeaders().then((headers) => { - cy.request({ method: "GET", - url: `${Cypress.env("server_host")}/api/v2/apps/${appId}`, + url: `${Cypress.env("server_host")}/api/apps/${appId}`, headers, }) .then((response) => { @@ -539,7 +592,7 @@ Cypress.Commands.add("apiReleaseApp", (appName) => { const editingVersionId = response.body.editing_version.id; cy.request({ method: "PUT", - url: `${Cypress.env("server_host")}/api/v2/apps/${appId}/release`, + url: `${Cypress.env("server_host")}/api/apps/${appId}/release`, headers: headers, body: { versionToBeReleased: editingVersionId, @@ -556,7 +609,6 @@ Cypress.Commands.add("apiReleaseApp", (appName) => { Cypress.Commands.add("apiAddAppSlug", (appName, slug) => { cy.getAppId(appName).then((appId) => { cy.getAuthHeaders().then((headers) => { - cy.request({ method: "PUT", url: `${Cypress.env("server_host")}/api/apps/${appId}`, @@ -576,7 +628,6 @@ Cypress.Commands.add("apiAddAppSlug", (appName, slug) => { Cypress.Commands.add("apiGetTableIdByName", (tableName) => { cy.getAuthHeaders().then((headers) => { - cy.request({ method: "GET", url: `${Cypress.env("server_host")}/api/tooljet-db/organizations/${Cypress.env("workspaceId")}/tables`, @@ -594,7 +645,6 @@ Cypress.Commands.add("apiGetTableIdByName", (tableName) => { Cypress.Commands.add("apiAddDataToTable", (tableName, data) => { cy.apiGetTableIdByName(tableName).then((tableId) => { cy.getAuthHeaders().then((headers) => { - cy.request({ method: "POST", url: `${Cypress.env("server_host")}/api/tooljet-db/proxy/${tableId}`, @@ -609,10 +659,11 @@ Cypress.Commands.add("apiAddDataToTable", (tableName, data) => { }); Cypress.Commands.add("apiGetDataSourceIdByName", (dataSourceName) => { + const workspaceId = Cypress.env("workspaceId"); cy.getAuthHeaders().then((headers) => { cy.request({ method: "GET", - url: `${Cypress.env("server_host")}/api/v2/data_sources`, + url: `${Cypress.env("server_host")}/api/data-sources/${workspaceId}`, headers: headers, }).then((response) => { expect(response.status).to.equal(200); @@ -647,7 +698,7 @@ Cypress.Commands.add( name: dataSourceName, options: [ { key: "connection_type", value: "manual", encrypted: false }, - { key: "host", value: "35.202.183.199" }, + { key: "host", value: "9.234.17.31" }, { key: "port", value: 5432 }, { key: "database", value: "student" }, { key: "username", value: "postgres" }, @@ -670,7 +721,7 @@ Cypress.Commands.add( cy.request({ method: "PUT", - url: `${Cypress.env("server_host")}/api/v2/data_sources/${dataSourceId}?environment_id=${environmentId}`, + url: `${Cypress.env("server_host")}/api/data-sources/${dataSourceId}?environment_id=${environmentId}`, headers: headers, body: mergedData, }).then((updateResponse) => { @@ -683,3 +734,67 @@ Cypress.Commands.add( } ); +Cypress.Commands.add("apiGetAppData", (appId = Cypress.env("appId")) => { + cy.getAuthHeaders().then((headers) => { + cy.request({ + method: "GET", + url: `${Cypress.env("server_host")}/api/apps/${appId}`, + headers: headers, + }).then((response) => { + expect(response.status).to.equal(200); + return response.body; + }); + }); +}); + +Cypress.Commands.add("apiDeleteGDS", (name) => { + const dataSourceId = Cypress.env(`${name}`); + + cy.getCookie("tj_auth_token").then((cookie) => { + cy.request({ + method: "DELETE", + url: `${Cypress.env("server_host")}/api/data-sources/${dataSourceId}`, + headers: { + "Tj-Workspace-Id": Cypress.env("workspaceId"), + Cookie: `tj_auth_token=${cookie.value}`, + }, + failOnStatusCode: false, + }).then((response) => { + console.log("Delete response:", response); + + expect(response.status, "Delete status code").to.eq(200); + + Cypress.log({ + name: "Delete Data Source", + displayName: "Data source deleted", + message: `Name: '${name}' | ID: '${dataSourceId}'`, + }); + }); + }); +}); + +Cypress.Commands.add( + "apiUpdateGDS", + ({ name, options, envName = "development" }) => { + cy.getAuthHeaders().then((headers) => { + cy.apiGetEnvironments().then((environments) => { + const environment = environments.find((env) => env.name === envName); + const environmentId = environment.id; + const dataSourceId = Cypress.env(`${name}`); + + cy.request({ + method: "PUT", + url: `${Cypress.env("server_host")}/api/data-sources/${dataSourceId}?environment_id=${environmentId}`, + headers: headers, + body: { + name: name, + options: options, + }, + }).then((response) => { + expect(response.status).to.equal(200); + cy.log(`Datasource "${name}" updated successfully.`); + }); + }); + }); + } +); diff --git a/cypress-tests/cypress/commands/commands.js b/cypress-tests/cypress/commands/commands.js index 0f9a6c9996..eee9e988eb 100644 --- a/cypress-tests/cypress/commands/commands.js +++ b/cypress-tests/cypress/commands/commands.js @@ -6,23 +6,21 @@ import { passwordInputText } from "Texts/passwordInput"; import { importSelectors } from "Selectors/exportImport"; import { importText } from "Texts/exportImport"; import { onboardingSelectors } from "Selectors/onboarding"; +import { selectAppCardOption } from "Support/utils/common"; + +const API_ENDPOINT = + Cypress.env("environment") === "Community" + ? "/api/library_apps" + : "/api/library_apps"; Cypress.Commands.add( "appUILogin", (email = "dev@tooljet.io", password = "password") => { - const API_ENDPOINT = - Cypress.env("environment") === "Community" - ? "/api/library_apps/" - : "/api/library_apps"; - cy.visit("/"); - cy.wait(1000); cy.clearAndType(onboardingSelectors.loginEmailInput, email); cy.clearAndType(onboardingSelectors.loginPasswordInput, password); cy.get(onboardingSelectors.signInButton).click(); - - cy.intercept("GET", API_ENDPOINT).as("library_apps"); - cy.get(commonSelectors.homePageLogo, { timeout: 10000 }); - cy.wait("@library_apps"); + cy.wait(2000); + cy.get('[data-cy="main-wrapper"]', { timeout: 10000 }).should("be.visible"); } ); @@ -86,7 +84,20 @@ Cypress.Commands.add( const dataTransfer = new DataTransfer(); cy.forceClickOnCanvas(); - cy.clearAndType(commonSelectors.searchField, widgetName); + cy.get("body") + .then(($body) => { + const isSearchVisible = $body + .find(commonSelectors.searchField) + .is(":visible"); + + if (!isSearchVisible) { + cy.get('[data-cy="right-sidebar-plus-button"]').click(); + } + }) + .then(() => { + cy.clearAndType(commonSelectors.searchField, widgetName); + }); + cy.get(commonWidgetSelector.widgetBox(widgetName2)).trigger( "dragstart", { dataTransfer }, @@ -142,13 +153,11 @@ Cypress.Commands.add( }; if (Array.isArray(value)) { - cy.wrap(subject) - .last() - .realType(value, { - parseSpecialCharSequences: false, - delay: 0, - force: true, - }); + cy.wrap(subject).last().realType(value, { + parseSpecialCharSequences: false, + delay: 0, + force: true, + }); } else { splitIntoFlatArray(value).forEach((i) => { cy.wrap(subject) @@ -165,13 +174,15 @@ Cypress.Commands.add( Cypress.Commands.add("deleteApp", (appName) => { cy.intercept("DELETE", "/api/apps/*").as("appDeleted"); - cy.get(commonSelectors.appCard(appName)) - .realHover() - .find(commonSelectors.appCardOptionsButton) - .realHover() - .click(); - cy.get(commonSelectors.deleteAppOption).click(); + selectAppCardOption( + appName, + commonSelectors.appCardOptions(commonText.deleteAppOption) + ); cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + commonText.appDeletedToast + ); cy.wait("@appDeleted"); }); @@ -305,19 +316,19 @@ Cypress.Commands.add("skipEditorPopover", () => { }); Cypress.Commands.add("waitForAppLoad", () => { - const API_ENDPOINT = - Cypress.env("environment") === "Community" - ? "/api/v2/data_sources" - : "/api/app-environments**"; + // const API_ENDPOINT = + // Cypress.env("environment") === "Community" + // ? "/api/v2/data_sources" + // : "/api/app-environments**"; - const TIMEOUT = 15000; + // const TIMEOUT = 15000; - cy.intercept("GET", API_ENDPOINT).as("appDs"); - cy.wait("@appDs", { timeout: TIMEOUT }); + cy.intercept("GET", "/api/data-queries/**").as("appDs"); + cy.wait("@appDs", { timeout: 15000 }); }); Cypress.Commands.add("visitTheWorkspace", (workspaceName) => { - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select id from organizations where name='${workspaceName}';`, }).then((resp) => { @@ -399,44 +410,37 @@ Cypress.Commands.add("getPosition", (componentName) => { }); Cypress.Commands.add("defaultWorkspaceLogin", () => { - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: ` - SELECT id FROM organizations WHERE name = 'My workspace'; - `, + SELECT id FROM organizations WHERE name = 'My workspace';`, }).then((resp) => { const workspaceId = resp.rows[0].id; - cy.apiLogin("dev@tooljet.io", "password", workspaceId, "/my-workspace"); - cy.visit("/"); - cy.intercept("GET", "/api/library_apps").as("library_apps"); - cy.get(commonSelectors.homePageLogo, { timeout: 10000 }); - cy.wait("@library_apps"); + cy.apiLogin( + "dev@tooljet.io", + "password", + workspaceId, + "/my-workspace" + ).then(() => { + cy.visit("/"); + cy.wait(2000); + cy.get(commonSelectors.homePageLogo, { timeout: 10000 }); + }); }); }); -Cypress.Commands.add( - "visitSlug", - ({ - actualUrl, - currentUrl = `${Cypress.config("baseUrl")}/error/unknown`, - }) => { - // Ensure actualUrl is provided - if (!actualUrl) { - throw new Error("actualUrl is required for visitSlug command."); +Cypress.Commands.add("visitSlug", ({ actualUrl }) => { + cy.visit(actualUrl); + cy.wait(1000); + + cy.url().then((currentUrl) => { + if (currentUrl !== actualUrl) { + cy.visit(actualUrl); + cy.wait(1000); } - - cy.visit(actualUrl); - - // Dynamically wait for the correct URL or handle navigation errors - cy.url().then((url) => { - if (url === currentUrl) { - cy.log(`Navigation resulted in unexpected URL: ${url}. Retrying...`); - cy.visit(actualUrl); - } - }); - } -); + }); +}); Cypress.Commands.add("releaseApp", () => { if (Cypress.env("environment") !== "Community") { @@ -458,13 +462,13 @@ Cypress.Commands.add("releaseApp", () => { Cypress.Commands.add("backToApps", () => { cy.get(commonSelectors.editorPageLogo).click(); cy.get(commonSelectors.backToAppOption).click(); - cy.intercept("GET", "/api/library_apps/").as("library_apps"); + cy.intercept("GET", API_ENDPOINT).as("library_apps"); cy.get(commonSelectors.homePageLogo, { timeout: 10000 }); cy.wait("@library_apps"); }); Cypress.Commands.add("removeAssignedApps", () => { - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `DELETE FROM app_group_permissions;`, }); @@ -503,7 +507,7 @@ Cypress.Commands.add("skipWalkthrough", () => { Cypress.Commands.add("appPrivacy", (appName, isPublic) => { const isPublicValue = isPublic ? "true" : "false"; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `UPDATE apps SET is_public = ${isPublicValue} WHERE name = '${appName}';`, }); @@ -522,23 +526,60 @@ Cypress.Commands.overwrite( } ); +Cypress.Commands.add("installMarketplacePlugin", (pluginName) => { + const MARKETPLACE_URL = `${Cypress.config("baseUrl")}/integrations/marketplace`; + + cy.visit(MARKETPLACE_URL); + cy.wait(1000); + + cy.get('[data-cy="-list-item"]').eq(0).click(); + cy.wait(1000); + + cy.get("body").then(($body) => { + if ($body.find(".plugins-card").length === 0) { + cy.log("No plugins found, proceeding to install..."); + installPlugin(pluginName); + } else { + cy.get(".plugins-card").then(($cards) => { + const isInstalled = $cards.toArray().some((card) => { + return ( + Cypress.$(card) + .find(".font-weight-medium.text-capitalize") + .text() + .trim() === pluginName + ); + }); + + if (isInstalled) { + cy.log(`${pluginName} is already installed. Skipping installation.`); + cy.get(commonSelectors.globalDataSourceIcon).click(); + } else { + installPlugin(pluginName); + cy.get(commonSelectors.globalDataSourceIcon).click(); + } + }); + } + }); + + function installPlugin (pluginName) { + cy.get('[data-cy="-list-item"]').eq(1).click(); + cy.wait(1000); + + cy.contains(".plugins-card", pluginName).within(() => { + cy.get(".marketplace-install").click(); + cy.wait(1000); + }); + } +}); + Cypress.Commands.add("verifyElement", (selector, text, eqValue) => { const element = eqValue !== undefined ? cy.get(selector).eq(eqValue) : cy.get(selector); element.should("be.visible").and("have.text", text); }); -Cypress.Commands.add("loginWithCredentials", (email, password) => { - cy.get(onboardingSelectors.loginEmailInput, { timeout: 20000 }).should( - "be.visible" - ); - cy.clearAndType(onboardingSelectors.loginEmailInput, email); - cy.clearAndType(onboardingSelectors.loginPasswordInput, password); - cy.get(onboardingSelectors.signInButton).click(); -}); - Cypress.Commands.add("getAppId", (appName) => { - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select id from apps where name='${appName}';`, }).then((resp) => { @@ -546,3 +587,73 @@ Cypress.Commands.add("getAppId", (appName) => { return appId; }); }); + +Cypress.Commands.add("uninstallMarketplacePlugin", (pluginName) => { + const MARKETPLACE_URL = `${Cypress.config("baseUrl")}/integrations/marketplace`; + + cy.visit(MARKETPLACE_URL); + cy.wait(1000); + + cy.get('[data-cy="-list-item"]').eq(0).click(); + cy.wait(1000); + + cy.get(".plugins-card").each(($card) => { + cy.wrap($card) + .find(".font-weight-medium.text-capitalize") + .invoke("text") + .then((text) => { + if (text.trim() === pluginName) { + cy.wrap($card).find(".link-primary").contains("Remove").click(); + cy.wait(1000); + + cy.get('[data-cy="delete-plugin-title"]').should("be.visible"); + cy.get('[data-cy="yes-button"]').click(); + cy.wait(2000); + + cy.log(`${pluginName} has been successfully uninstalled.`); + } else { + cy.log(`${pluginName} is not installed. Skipping uninstallation.`); + } + }); + }); +}); + +Cypress.Commands.add( + "verifyRequiredFieldValidation", + (fieldName, expectedColor) => { + cy.get(commonSelectors.textField(fieldName)).type("some text").clear(); + cy.get(commonSelectors.textField(fieldName)).should( + "have.css", + "border-color", + expectedColor + ); + cy.get(commonSelectors.labelFieldValidation(fieldName)) + .should("be.visible") + .and("have.text", `${fieldName} is required`); + cy.get(commonSelectors.labelFieldAlert(fieldName)) + .should("be.visible") + .and("have.text", `${fieldName} is required`); + } +); + +Cypress.Commands.add("ifEnv", (expectedEnvs, callback) => { + const actualEnv = Cypress.env("environment"); + const envArray = Array.isArray(expectedEnvs) ? expectedEnvs : [expectedEnvs]; + + if (envArray.includes(actualEnv)) { + callback(); + } +}); + +Cypress.Commands.add("openComponentSidebar", (selector, value) => { + cy.get("body") + .then(($body) => { + const isSearchVisible = $body + .find(commonSelectors.searchField) + .is(":visible"); + + if (!isSearchVisible) { + cy.get('[data-cy="right-sidebar-plus-button"]').click(); + } + }) +}); \ No newline at end of file diff --git a/cypress-tests/cypress/constants/selectors/Plugins.js b/cypress-tests/cypress/constants/selectors/Plugins.js new file mode 100644 index 0000000000..8d41dee2db --- /dev/null +++ b/cypress-tests/cypress/constants/selectors/Plugins.js @@ -0,0 +1,62 @@ +export const pluginSelectors = { + regionField: '[data-cy="region-section"] .react-select__control', + regionFieldValue: '[data-cy="region-section"] .react-select__single-value', + amazonsesAccesKey: '[data-cy="access-key-text-field"]', + operationDropdown: '[data-cy="operation-select-dropdown"]', + sendEmailInputField: '[data-cy="send-mail-to-input-field"]', + ccEmailInputField: '[data-cy="cc-to-input-field"]', + bccEmailInputField: '[data-cy="bcc-to-input-field"]', + sendEmailFromInputField: '[data-cy="send-mail-from-input-field"]', + emailSubjetInputField: '[data-cy="subject-input-field"]', + emailbodyInputField: '[data-cy="body-input-field"]', + amazonAthenaDbName: '[data-cy="database-text-field"]', +}; + +export const baserowSelectors = { + hostField: '[data-cy="host-select-dropdown"]', + baserowApiKey: '[data-cy="api-token-text-field"]', + table: '[data-cy="table-id-input-field"]', + rowIdinputfield: '[data-cy="row-id-input-field"]', +}; + +export const appWriteSelectors = { + projectID: '[data-cy="project-id-text-field"]', + collectionId: '[data-cy="collectionid-input-field"]', + documentId: '[data-cy="documentid-input-field"]', + bodyInput: '[data-cy="body-input-field"]', +}; + +export const twilioSelectors = { + toNumberInputField: '[data-cy="to-number-input-field"]', + bodyInput: '[data-cy="body-input-field"]', +}; + +export const minioSelectors = { + sslToggle: 'data-cy="ssl-enabled-toggle-input"', + bucketNameInputField: '[data-cy="bucket-input-field"]', + objectNameInputField: '[data-cy="objectname-input-field"]', + contentTypeInputField: '[data-cy="contenttype-input-field"]', + dataInput: '[data-cy="data-input-field"]', +}; + +export const harperDbSelectors = { + recordsInputField: '[data-cy="records-input-field"]', + hashValueInputField: '[data-cy="hash-values-input-field"]', + attributesInputField: '[data-cy="attributes-input-field"]', + searchValueInputField: '[data-cy="search-value-input-field"]', + searchAttributeInputField: '[data-cy="search-attribute-input-field"]', + conditionInputField: '[data-cy="conditions-input-field"]', + sqlQueryInputField: '[data-cy="sql-query-input-field"]', + schemaInputField: '[data-cy="schema-input-field"]', + TableInputField: '[data-cy="table-input-field"]', +}; + +export const awsTextractSelectors = { + documentInputField: '[data-cy="document-input-field"]', + bucketNameInputField: '[data-cy="bucket-input-field"]', + keyNameInputField: '[data-cy="key-input-field"]', +}; + +export const graphQLSelectors = { + urlInputField: '[data-cy="url-text-field"]', +}; diff --git a/cypress-tests/cypress/constants/selectors/awss3.js b/cypress-tests/cypress/constants/selectors/awss3.js index 59bdebacff..81f1a6efe8 100644 --- a/cypress-tests/cypress/constants/selectors/awss3.js +++ b/cypress-tests/cypress/constants/selectors/awss3.js @@ -5,5 +5,5 @@ export const s3Selector = { regionLabel: '[data-cy="label-region"]', customEndpointLabel: '[data-cy="label-custom-endpoint"]', customEndpointInput: '[data-cy="undefined-text-field"]', - dataSourceNameInput: '[data-cy="data-source-name-input-filed"]', + dataSourceNameInput: '[data-cy="data-source-name-input-field"]', }; diff --git a/cypress-tests/cypress/constants/selectors/common.js b/cypress-tests/cypress/constants/selectors/common.js index 547928ab33..464d0c3cf1 100644 --- a/cypress-tests/cypress/constants/selectors/common.js +++ b/cypress-tests/cypress/constants/selectors/common.js @@ -1,12 +1,13 @@ export const cyParamName = (paramName = "") => { - return paramName.toLowerCase().replace(/\s+/g, "-"); + return String(paramName).toLowerCase().replace(/\s+/g, "-"); }; export const commonSelectors = { toastMessage: ".go3958317564", oldToastMessage: ".go318386747", appSlugAccept: '[data-cy="app-slug-accepted-label"]', - newToastMessage: '.drawer-container > [style="position: fixed; z-index: 9999; inset: 16px; pointer-events: none;"] > .go4109123758 > .go2072408551 > .go3958317564', + newToastMessage: + '.drawer-container > [style="position: fixed; z-index: 9999; inset: 16px; pointer-events: none;"] > .go4109123758 > .go2072408551 > .go3958317564', toastCloseButton: '[data-cy="toast-close-button"]', editButton: "[data-cy=edit-button]", workspaceConstantNameInput: '[data-cy="name-input-field"]', @@ -17,8 +18,8 @@ export const commonSelectors = { canvas: "[data-cy=real-canvas]", appCardOptionsButton: "[data-cy=app-card-menu-icon]", autoSave: "[data-cy=autosave-indicator]", - nameInputFieldd: "[data-cy=name-input-field]", - valueInputFieldd: '[data-cy=value-input-field]', + inputFieldName: "[data-cy=name-input-field]", + valueInputFieldd: "[data-cy=value-input-field]", skipButton: ".driver-close-btn", skipInstallationModal: "[data-cy=skip-button]", homePageLogo: "[data-cy=home-page-logo]", @@ -176,7 +177,7 @@ export const commonSelectors = { breadcrumbPageTitle: '[data-cy="breadcrumb-page-title"]', labelFullNameInput: '[data-cy="name-label"]', duplicateOption: '[data-cy="duplicate-group-card-option"]', - confirmDuplicateButton: '[data-cy="confim-button"]', + confirmDuplicateButton: '[data-cy="confirm-button"]', inputFieldFullName: '[data-cy="name-input"]', labelEmailInput: '[data-cy="email-label"]', inputFieldEmailAddress: '[data-cy="email-input"]', @@ -259,7 +260,7 @@ export const commonSelectors = { cloneAppTitle: '[data-cy="clone-app-title"]', cloneAppButton: '[data-cy="clone-app"]', appNameErrorLabel: '[data-cy="app-name-error-label"]', - importAppTitle: '[data-cy="import-app-title"]', + importAppTitle: '[data-cy="import-an-app"]', importAppButton: '[data-cy="import-app"]', chooseFromTemplateButton: '[data-cy="choose-from-template-button"]', CreateAppFromTemplateButton: '[data-cy="create-new-app-from-template-title"]', @@ -277,6 +278,17 @@ export const commonSelectors = { defaultModalTitle: '[data-cy="modal-title"]', workspaceConstantsIcon: '[data-cy="icon-workspace-constants"]', confirmationButton: '[data-cy="confirmation-button"]', + + textField: (fieldName) => { + return `[data-cy="${cyParamName(fieldName)}-text-field"]`; + }, + labelFieldValidation: (fieldName) => { + return `[data-cy="${cyParamName(fieldName)}-is-required-validation-label"]`; + }, + labelFieldAlert: (fieldName) => { + return `[data-cy="${cyParamName(fieldName)}-is-required-field-alert-text"]`; + }, + pageLogo: '[data-cy="page-logo"]', }; export const commonWidgetSelector = { @@ -342,7 +354,7 @@ export const commonWidgetSelector = { buttonCloseEditorSideBar: "[data-cy='inspector-close-icon']", buttonStylesEditorSideBar: "#inspector-tab-styles", WidgetNameInputField: "[data-cy=edit-widget-name]", - constantInspectorIcon: '[data-cy="inspector-node-constants"] > .node-key', + constantInspectorIcon: '[data-cy="inspector-constants-expand-button"]', inspectorIcon: '[data-cy="left-sidebar-inspect-button"]', tooltipInputField: "[data-cy='tooltip-input-field']", tooltipLabel: "[id=button-tooltip]", @@ -395,7 +407,7 @@ export const commonWidgetSelector = { modalCloseButton: '[data-cy="modal-close-button"]', iframeLinkLabel: '[data-cy="iframe-link-label"]', ifameLinkCopyButton: '[data-cy="iframe-link-copy-button"]', - appSlugLabel: '[data-cy="input-field-label"]', + appSlugLabel: '[data-cy="unique-app-slug-field-label"]', appSlugInput: '[data-cy="app-slug-input-field"]', appSlugInfoLabel: '[data-cy="helper-text"]', appLinkLabel: '[data-cy="app-link-label"]', diff --git a/cypress-tests/cypress/constants/selectors/dataSource.js b/cypress-tests/cypress/constants/selectors/dataSource.js index bdf1677d91..86f5a24c58 100644 --- a/cypress-tests/cypress/constants/selectors/dataSource.js +++ b/cypress-tests/cypress/constants/selectors/dataSource.js @@ -14,7 +14,7 @@ export const dataSourceSelector = { dataSourceSearchInputField: '[data-cy="home-page-search-bar"]', postgresDataSource: "[data-cy='data-source-postgresql']", - dataSourceNameInputField: '[data-cy="data-source-name-input-filed"]', + dataSourceNameInputField: '[data-cy="added-ds-search-bar"]', labelHost: '[data-cy="label-host"]', labelPort: '[data-cy="label-port"]', labelSsl: '[data-cy="label-ssl"]', @@ -28,7 +28,7 @@ export const dataSourceSelector = { buttonTestConnection: '[data-cy="test-connection-button"]', connectionFailedText: '[data-cy="test-connection-failed-text"]', buttonSave: '[data-cy="db-connection-save-button"] > .tj-base-btn', - dangerAlertNotSupportSSL: '.go3958317564', + dangerAlertNotSupportSSL: ".go3958317564", passwordTextField: '[data-cy="password-text-field"]', textConnectionVerified: '[data-cy="test-connection-verified-text"]', @@ -97,11 +97,52 @@ export const dataSourceSelector = { eventQuerySelectionField: '[data-cy="query-selection-field"]', addedDsSearchIcon: '[data-cy="added-ds-search-icon"]', AddedDsSearchBar: '[data-cy="added-ds-search-bar"]', - dsNameInputField: '[data-cy="data-source-name-input-filed"]', + dsNameInputField: '[data-cy="data-source-name-input-field"]', unSavedModalTitle: '[data-cy="unsaved-changes-title"]', eventQuerySelectionField: '[data-cy="query-selection-field"]', connectionAlertText: '[data-cy="connection-alert-text"]', + requiredIndicator: '[data-cy="required-indicator"]', + informationIcon: '[data-cy="information-icon"]', deleteDSButton: (datasourceName) => { - return `[data-cy="${cyParamName(datasourceName)}-delete-button"]` + return `[data-cy="${cyParamName(datasourceName)}-delete-button"]`; + }, + labelFieldName: (fieldName) => { + return `[data-cy="${cyParamName(fieldName)}-field-label"]`; + }, + dataSourceNameButton: (dataSourceName) => { + return `[data-cy="${cyParamName(dataSourceName)}-button"]`; + }, + dropdownLabel: (label) => { + return `[data-cy="${cyParamName(label)}-dropdown-label"]`; + }, + textField: (fieldName) => { + return `[data-cy="${cyParamName(fieldName)}-text-field"]`; + }, + subSection: (header) => { + return `[data-cy="${cyParamName(header)}-section"]`; + }, + toggleInput: (toggleName) => { + return `[data-cy="${cyParamName(toggleName)}-toggle-input"]`; + }, + button: (buttonName) => { + return `[data-cy="button-${cyParamName(buttonName)}"]`; + }, + keyInputField: (header, index) => { + return `[data-cy="${cyParamName(header)}-key-input-field-${cyParamName(index)}"]`; + }, + valueInputField: (header, index) => { + return `[data-cy="${cyParamName(header)}-value-input-field-${cyParamName(index)}"]`; + }, + deleteButton: (header, index) => { + return `[data-cy="${cyParamName(header)}-delete-button-${cyParamName(index)}"]`; + }, + addMoreButton: (header) => { + return `[data-cy="${cyParamName(header)}-add-button"]`; + }, + dropdownField: (fieldName) => { + return `[data-cy="${cyParamName(fieldName)}-select-dropdown"]`; + }, + labelFieldValidation: (fieldName) => { + return `[data-cy="${cyParamName(fieldName)}-is-required-validation-label"]`; }, }; diff --git a/cypress-tests/cypress/constants/selectors/eeCommon.js b/cypress-tests/cypress/constants/selectors/eeCommon.js new file mode 100644 index 0000000000..de54858f45 --- /dev/null +++ b/cypress-tests/cypress/constants/selectors/eeCommon.js @@ -0,0 +1,202 @@ +import { cyParamName } from "./common"; + +export const commonEeSelectors = { + instanceSettingIcon: '[data-cy="instance-settings-option"]', + auditLogIcon: '[data-cy="audit-log-option"]', + cancelButton: '[data-cy="cancel-button"]', + saveButton: '[data-cy="save-button"]', + pageTitle: '[data-cy="dashboard-section-header"]', + modalTitle: '[data-cy="modal-title"]', + modalCloseButton: '[data-cy="modal-close-button"]', + saveButton: '[data-cy="save-button"]', + cardTitle: '[data-cy="card-title"]', + AddQueryButton: '[data-cy="show-ds-popover-button"]', + promoteButton: '[data-cy="promote-button"]', + settingsIcon: '[data-cy="icon-settings"]', + gitSyncIcon: '[data-cy="git-sync-icon"]', + confirmButton: '[data-cy="confirm-button"]', + importFromGit: '[data-cy="import-from-git-button"]', + searchBar: '[data-cy="query-manager-search-bar"]', + nameHeader: '[data-cy="name-header"]', + modalMessage: '[data-cy="modal-message"]', + paginationSection: '[data-cy="pagination-section"]', + +}; + +export const ssoEeSelector = { + oidc: '[data-cy="openid-connect-sso-card"]', + statusLabel: '[data-cy="status-label"]', + oidcToggle: '[data-cy="openid-toggle-input"] > .slider', + oidcPageElements: { + oidcToggleLabel: '[data-cy="openid-toggle-label"]', + nameLabel: '[data-cy="name-label"]', + clientIdLabel: '[data-cy="client-id-label"]', + clientSecretLabel: '[data-cy="client-secret-label"]', + encryptedLabel: '[data-cy="encripted-label"]', + WellKnownUrlLabel: '[data-cy="well-known-url-label"]', + // redirectUrlLabel: '[data-cy="redirect-url-label"]', + }, + nameInput: '[data-cy="name-input"]', + clientIdInput: '[data-cy="client-id-input"]', + clientSecretInput: '[data-cy="client-secret-input"]', + WellKnownUrlInput: '[data-cy="well-known-url-input"]', + redirectUrl: '[data-cy="redirect-url"]', + copyIcon: '[data-cy="copy-icon]', + oidcSSOText: '[data-cy="oidc-sso-button-text"]', + oidcSSOIcon: '[data-cy="oidc-so-icon"]', + ldapPageElements: { + ldapToggleLabel: '[data-cy="ldap-toggle-label"]', + nameLabel: '[data-cy="name-label"]', + hostLabel: '[data-cy="host-label"]', + portLabel: '[data-cy="port-label"]', + baseDnLabel: '[data-cy="base-dn-label"]', + baseDnHelperText: '[data-cy="base-dn-helper-text"]', + sslLabel: '[data-cy="ssl-label"]', + }, + ldapToggle: '[data-cy="ldap-toggle-input"] > .slider', + hostInput: '[data-cy="host-input"]', + portInput: '[data-cy="port-input"]', + baseDnInput: '[data-cy="base-dn-input"]', + sslToggleInput: '[data-cy="ssl-toggle-input"]', + ldapSSOText: '[data-cy="ldap-sso-button-text"]', + userNameInputLabel: '[data-cy="user-name-input-label"]', + passwordInputLabel: '[data-cy="password-label"]', + passwordInputField: '[data-cy="password-input-field"]', + + samlModalElements: { + toggleLabel: '[data-cy="saml-toggle-label"]', + NameLabel: '[data-cy="name-label"]', + metaDataLabel: '[data-cy="idp-metadata-label"]', + baseDNHelperText: '[data-cy="base-dn-helper-text"]', + groupAttributeLabel: '[data-cy="group-attribute-label"]', + groupAttributeHelperText: '[data-cy="group-attribute-helper-text"]', + } +}; + +export const eeGroupsSelector = { + resourceDs: '[data-cy="resource-datasources"]', + dsCreateCheck: '[data-cy="checkbox-create-ds"]', + dsDeleteCheck: '[data-cy="checkbox-delete-ds"]', + datasourceLink: '[data-cy="datasource-link"]', + dsSearch: '[data-cy="datasource-select-search"]', + AddDsButton: '[data-cy="datasource-add-button"]', + dsNameHeader: '[data-cy="datasource-name-header"]', +}; + +export const instanceSettingsSelector = { + allUsersTab: '[data-cy="all-users-list-item"]', + manageInstanceSettings: '[data-cy="manage-instance-settings-list-item"]', + typeColumnHeader: '[data-cy="users-table-type-column-header"]', + workspaceColumnHeader: '[data-cy="users-table-workspaces-column-header"]', + userName: (userName) => { + return `[data-cy="${cyParamName(userName)}-user-name"]`; + }, + userEmail: (userName) => { + return `[data-cy="${cyParamName(userName)}-user-email"]`; + }, + userType: (userName) => { + return `[data-cy="${cyParamName(userName)}-user-type"]`; + }, + userStatus: (userName) => { + return `[data-cy="${cyParamName(userName)}-user-status"]`; + }, + viewButton: (userName) => { + return `[data-cy="${cyParamName(userName)}-user-view-button"]`; + }, + editButton: (userName) => { + return `[data-cy="${cyParamName(userName)}-user-edit-button"]`; + }, + viewModalNoColumnHeader: '[data-cy="number-column-header"]', + viewModalNameColumnHeader: '[data-cy="name-column-header"]', + viewModalStatusColumnHeader: '[data-cy="status-column-header"]', + archiveAllButton: '[data-cy="archive-all-button"]', + viewModalRow: (workspaceName) => { + return `[data-cy="${cyParamName(workspaceName)}-workspace-row"]>`; + }, + + workspaceName: (workspaceName) => { + return `[data-cy="${cyParamName(workspaceName)}-workspace-name"]`; + }, + userStatusChangeButton: '[data-cy="user-state-change-button"]', + superAdminToggle: '[data-cy="super-admin-form-check-input"]', + superAdminToggleLabel: '[data-cy="super-admin-form-check-label"]', + allowWorkspaceToggle: '[data-cy="form-check-input"]', + allowWorkspaceToggleLabel: '[data-cy="form-check-label"]', + allowWorkspaceHelperText: '[data-cy="instance-settings-help-text"]', + allWorkspaceTab: '[data-cy="all-workspaces-list-item"]', +}; + + +export const multiEnvSelector = { + envContainer: '[data-cy="env-container"]', + currentEnvName: '[data-cy="list-current-env-name"]', + envArrow: '[data-cy="env-arrow"]', + selectedEnvName: '[data-cy="selected-current-env-name"]', + envNameList: '[data-cy="env-name-list"]', + appVersionLabel: '[data-cy="app-version-label"]', + currentVersion: '[data-cy="current-version"]', + createNewVersionButton: '[data-cy="create-new-version-button"]', + fromLabel: '[data-cy="from-label"]', + toLabel: '[data-cy="to-label"]', + currEnvName: '[data-cy="current-env-name"]', + targetEnvName: '[data-cy="target-env-name"]', + stagingLabel: '[data-cy="staging-label"]', + productionLabel: '[data-cy="production-label"]', +}; + +export const whiteLabellingSelectors = { + whiteLabelList: '[data-cy="white-labelling-list-item"]', + appLogoLabel: '[data-cy="app-logo-label"]', + appLogoInput: '[data-cy="input-field-app-logo"]', + appLogoHelpText: '[data-cy="app-logo-help-text"]', + pageTitleLabel: '[data-cy="page-title-label"]', + pageTitleInput: '[data-cy="input-field-page-title"]', + pageTitleHelpText: '[data-cy="page-title-help-text"]', + favIconLabel: '[data-cy="fav-icon-label"]', + favIconInput: '[data-cy="input-field-fav-icon"]', + favIconHelpText: '[data-cy="fav-icon-help-text"]', +}; + +export const gitSyncSelector = { + gitCommitInput: '[data-cy="git-commit-input"]', + commitHelperText: '[data-cy="commit-helper-text"]', + gitRepoInput: '[data-cy="git-repo-input"]', + commitMessageInput: '[data-cy="commit-message-input"]', + lastCommitInput: '[data-cy="las-commit-message"]', + lastCommitVersion: '[data-cy="last-commit-version"]', + autherInfo: '[data-cy="auther-info"]', + commitButton: '[data-cy="commit-button"]', + gitSyncToggleInput: '[data-cy="git-sync-toggle-input"]', + gitSyncApphelperText: '[data-cy="sync-app-helper-text"]', + connectRepoButton: '[data-cy="connect-repo-button"]', + toggleMessage: '[data-cy="toggle-message"]', + sshInput: '[data-cy="git-ssh-input"]', + generateSshButton: '[data-cy="generate-ssh-key-button"', + sshInputHelperText: '[data-cy="git-ssh-input-helper-text"]', + configDeleteButton: '[data-cy="button-config-delete"]', + testConnectionButton: '[data-cy="test-connection-button"]', + sshKey: '[data-cy="ssh-key"]', + deployKeyHelperText: '[data-cy="deploy-key-helper-text"]', + gitRepoLink: '[data-cy="git-repo-link"]', + appNameField: '[data-cy="app-name-field"]', + gitRepoInfo: '[data-cy="git-repo-info"]', + pullButton: '[data-cy="pull-button"]' + + +} + +export const workspaceSelector = { + activelink: '[data-cy="active-link"]', + archivedLik: '[data-cy="archived-link"]', + userStatusChange: '[data-cy="button-user-status-change"]', + workspaceStatusChange: '[data-cy="button-ws-status-change"]', + switchWsModalTitle: '[data-cy="switch-modal-title"]', + switchWsModalMessage: '[data-cy="switch-modal-message"]', + workspaceName: (workspaceName) => { + return `[data-cy="${workspaceName}-workspace-name"]` + }, + workspaceInput: (workspaceName) => { + return `[data-cy="${workspaceName}-workspace-input"]` + }, + +} \ No newline at end of file diff --git a/cypress-tests/cypress/constants/selectors/manageGroups.js b/cypress-tests/cypress/constants/selectors/manageGroups.js index 1a83a90ed2..9ed4273ba5 100644 --- a/cypress-tests/cypress/constants/selectors/manageGroups.js +++ b/cypress-tests/cypress/constants/selectors/manageGroups.js @@ -6,7 +6,7 @@ export const groupsSelector = { createNewGroupButton: "[data-cy=create-new-group-button]", tableHeader: "[data-cy=table-header]", groupName: "[data-cy=group-name]", - addNewGroupModalTitle: '[data-cy="create-new-group-title"]', + addNewGroupModalTitle: '[data-cy="add-new-group-title"]', groupNameInput: "[data-cy=group-name-input]", cancelButton: "[data-cy=cancel-button]", workspaceVarCreateLabel: '[data-cy="workspace-variable-create-label"]', @@ -40,7 +40,7 @@ export const groupsSelector = { resourceLabel: '[data-cy="resource-label"]', allAppsRadio: '[data-cy="all-apps-radio"]', allAppsLabel: '[data-cy="all-apps-label"]', - allAppsHelperText: '[data-cy="all-apps-info-text"]', + allAppsHelperText: '[data-cy="this-will-select-all-apps-in-the-workspace-including-any-new-apps-created-info-text"]', customradio: '[data-cy="custom-radio"]', customLabel: '[data-cy="custom-label"]', customHelperText: '[data-cy="custom-info-text"]', @@ -133,7 +133,7 @@ export const groupsSelector = { usersCheckInput: '[data-cy="users-check-input"]', permissionCheckInput: '[data-cy="permissions-check-input"]', appsCheckInput: '[data-cy="apps-check-input"]', - confimButton: '[data-cy="confim-button"]', + confimButton: '[data-cy="confirm-button"]', duplicatedGroupLink: (groupName) => { return `[data-cy="${cyParamName(groupName)}_copy-list-item"]` }, diff --git a/cypress-tests/cypress/constants/selectors/multipage.js b/cypress-tests/cypress/constants/selectors/multipage.js index 87ab84b141..cdb2e21113 100644 --- a/cypress-tests/cypress/constants/selectors/multipage.js +++ b/cypress-tests/cypress/constants/selectors/multipage.js @@ -1,7 +1,7 @@ export const multipageSelector = { sidebarPageButton: '[data-cy="left-sidebar-page-button"]', pagesLabel: '[data-cy="label-pages"]', - addPageIcon: '[title="Add Page"]', + addPageIcon: '[data-cy="add-page-button"]', searchPageIcon: '[title="Search"]', pagesPinIcon: '[title="Pin"]', diff --git a/cypress-tests/cypress/constants/selectors/onboarding.js b/cypress-tests/cypress/constants/selectors/onboarding.js index a7839afa74..cf2404f6f3 100644 --- a/cypress-tests/cypress/constants/selectors/onboarding.js +++ b/cypress-tests/cypress/constants/selectors/onboarding.js @@ -54,8 +54,8 @@ export const onboardingSelectors = { basicPlanTitle: '[data-cy="basic-plan-title"]', planPrice: '[data-cy="plan-price"]', pricePeriod: '[data-cy="price-period"]', - flexibleTitle: '[data-cy="flexible-title"]', - businessTitle: '[data-cy="business-title"]', + flexibleTitle: '[data-cy="pro-title"]', + businessTitle: '[data-cy="team-title"]', enterpriseTitle: '[data-cy="enterprise-title"]', customPricingHeader: '[data-cy="custom-pricing-header"]', noCreditCardBanner: '[data-cy="no-credit-card-banner"]', diff --git a/cypress-tests/cypress/constants/selectors/postgreSql.js b/cypress-tests/cypress/constants/selectors/postgreSql.js index 49e0351656..112da90779 100644 --- a/cypress-tests/cypress/constants/selectors/postgreSql.js +++ b/cypress-tests/cypress/constants/selectors/postgreSql.js @@ -12,7 +12,7 @@ export const postgreSqlSelector = { dataSourceSearchInputField: '[data-cy="home-page-search-bar"]', postgresDataSource: "[data-cy='data-source-postgresql']", - dataSourceNameInputField: '[data-cy="data-source-name-input-filed"]', + dataSourceNameInputField: '[data-cy="data-source-name-input-field"]', labelHost: '[data-cy="label-host"]', labelPort: '[data-cy="label-port"]', labelSsl: '[data-cy="label-ssl"]', @@ -87,4 +87,14 @@ export const postgreSqlSelector = { recordsInputField: '[data-cy="records-input-field"]', eventQuerySelectionField: '[data-cy="query-selection-field"]', + sslToggleInput: '[data-cy="ssl-enabled-toggle-input"]', + labelEncryptedText: '[data-cy="encrypted-text"]', +}; + +export const airTableSelector = { + operationSelectDropdown: '[data-cy="operation-select-dropdown"]', + baseIdInputField: '[data-cy="base-id-input-field"]', + tableNameInputField: '[data-cy="table-name-input-field"]', + recordIdInputField: '[data-cy="record-id-input-field"]', + bodyInputField: '[data-cy="body-input-field"]', }; diff --git a/cypress-tests/cypress/constants/selectors/restAPI.js b/cypress-tests/cypress/constants/selectors/restAPI.js new file mode 100644 index 0000000000..5ec2d73954 --- /dev/null +++ b/cypress-tests/cypress/constants/selectors/restAPI.js @@ -0,0 +1,44 @@ +export const cyParamName = (paramName = "") => { + return String(paramName) + .toLowerCase() + .replace(/\(s\)/g, "") + .replace(/\s+/g, "-"); +}; +export const restAPISelector = { + accordionHeader: (header) => { + return `[data-cy="widget-accordion-${cyParamName(header)}"]`; + }, + subHeaderLabel: (header) => { + return `[data-cy="label-${cyParamName(header)}"]`; + }, + subSection: (header) => { + return `[data-cy="${cyParamName(header)}-section"]`; + }, + keyInputField: (header, index) => { + return `[data-cy="${cyParamName(header)}-key-input-field-${cyParamName(index)}"]`; + }, + valueInputField: (header, index) => { + return `[data-cy="${cyParamName(header)}-value-input-field-${cyParamName(index)}"]`; + }, + deleteButton: (header, index) => { + return `[data-cy="${cyParamName(header)}-delete-button-${cyParamName(index)}"]`; + }, + addMoreButton: (header) => { + return `[data-cy="${cyParamName(header)}-add-button"]`; + }, + dropdownLabel: (label) => { + return `[data-cy="${cyParamName(label)}-dropdown-label"]`; + }, + inputField: (fieldName) => { + return `[data-cy="${cyParamName(fieldName)}-input-field"]`; + }, + button: (buttonName) => { + return `[data-cy="button-${cyParamName(buttonName)}"]`; + }, + authenticationAllUsersToggleSwitch: + '[data-cy="authentication-required-for-all-users-toggle-switch"]', + retryNetworkToggleSwitch: '[data-cy="retry-network-errors-toggle-input"]', + retryNetworkToggleText: '[data-cy="retry-network-errors-toggle-text"]', + retryNetworkToggleSubtext: '[data-cy="retry-network-errors-toggle-subtext"]', + readDocumentationLinkText: '[data-cy="link-read-documentation"]', +}; diff --git a/cypress-tests/cypress/constants/texts/airTable.js b/cypress-tests/cypress/constants/texts/airTable.js new file mode 100644 index 0000000000..1604fd7590 --- /dev/null +++ b/cypress-tests/cypress/constants/texts/airTable.js @@ -0,0 +1,7 @@ +export const airtableText = { + airtable: "Airtable", + cypressairtable: "cypress-Airtable", + ApiKey: "Personal access token", + apikeyPlaceholder: "**************", + invalidAccessToken: "Authentication failed: Invalid personal access token", +}; diff --git a/cypress-tests/cypress/constants/texts/amazonAthena.js b/cypress-tests/cypress/constants/texts/amazonAthena.js new file mode 100644 index 0000000000..794815d4a7 --- /dev/null +++ b/cypress-tests/cypress/constants/texts/amazonAthena.js @@ -0,0 +1,8 @@ +export const amazonAthenaText = { + AmazonAthena: "Amazon Athena", + cypressAmazonAthena: "cypress-Amazon Athena", + labelAccesskey: "Access key", + labelSecretKey: "Secret key", + placeholderEnteraAccessKey: "Enter access key", + placeholderSecretKey:"**************", + }; \ No newline at end of file diff --git a/cypress-tests/cypress/constants/texts/amazonSes.js b/cypress-tests/cypress/constants/texts/amazonSes.js new file mode 100644 index 0000000000..2abaed71ab --- /dev/null +++ b/cypress-tests/cypress/constants/texts/amazonSes.js @@ -0,0 +1,8 @@ +export const amazonSesText = { + AmazonSES: "Amazon SES", + cypressAmazonSES: "cypress-Amazon SES", + labelAccesskey: "Access key", + labelSecretKey: "Secret key", + placeholderAccessKey: "Enter access key", + placeholderSecretKey:"**************", + }; \ No newline at end of file diff --git a/cypress-tests/cypress/constants/texts/appwrite.js b/cypress-tests/cypress/constants/texts/appwrite.js new file mode 100644 index 0000000000..15b723d1cc --- /dev/null +++ b/cypress-tests/cypress/constants/texts/appwrite.js @@ -0,0 +1,12 @@ +export const appwriteText = { + appwrite: "Appwrite", + cypressAppwrite: "cypress-Appwrite", + host: "Host", + ProjectID: "Project ID", + DatabaseID: "Database ID", + SecretKey: "Secret Key", + SecretKeyPlaceholder: "**************", + hostPlaceholder: "Appwrite database host/endpoint", + projectIdPlaceholder: "Appwrite project id", + databaseIdPlaceholder: "Appwrite Database id", +}; diff --git a/cypress-tests/cypress/constants/texts/awsLambda.js b/cypress-tests/cypress/constants/texts/awsLambda.js new file mode 100644 index 0000000000..fea1428598 --- /dev/null +++ b/cypress-tests/cypress/constants/texts/awsLambda.js @@ -0,0 +1,8 @@ +export const awsLambdaText = { + awsLambda: "AWS Lambda", + cypressawsLambda: "cypress-aws-lambda", + labelAccesskey: "Access key", + labelSecretKey: "Secret key", + placeholderAccessKey: "Enter access key", + placeholderSecretKey: "**************", +}; diff --git a/cypress-tests/cypress/constants/texts/awsTextract.js b/cypress-tests/cypress/constants/texts/awsTextract.js new file mode 100644 index 0000000000..36c752ec4c --- /dev/null +++ b/cypress-tests/cypress/constants/texts/awsTextract.js @@ -0,0 +1,12 @@ +export const awsTextractText = { + awsTextract: "AWS Textract", + cypressawsLambda: "cypress-aws-textract", + labelAccesskey: "Access key", + labelSecretKey: "Secret key", + placeholderAccessKey: "Enter access key", + placeholderSecretKey: "**************", + documentName: + "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDw8IC9Gb250IDw8IC9GMSA0IDAgUiA+PiA+PiAvQ29udGVudHMgNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhLUJvbGQgPj4KZW5kb2JqCjUgMCBvYmoKPDwgL0xlbmd0aCAxMjUgPj4Kc3RyZWFtCkJUIC9GMSAxMiBUZiAxMDAgNzAwIFRkICgoSGVsbG8sIEFtYXpvbiBUZXh0cmFjdCEpIFRqIEVUCmVuZHN0cmVhbQplbmRvYmoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDExIDAwMDAwIG4gCjAwMDAwMDAwNTQgMDAwMDAgbgAKMDAwMDAwMDEwMyAwMDAwMCBuIAowMDAwMDAwMTcyIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMjIzCiUlRU9G", + bucketName: "reimbursement-receipt-files", + keyName: "reimbursement_receipt_1718364944018.png", +}; diff --git a/cypress-tests/cypress/constants/texts/baseRow.js b/cypress-tests/cypress/constants/texts/baseRow.js new file mode 100644 index 0000000000..6f9958d979 --- /dev/null +++ b/cypress-tests/cypress/constants/texts/baseRow.js @@ -0,0 +1,6 @@ +export const baseRowText = { + baserow: "baserow", + cypressBaseRow: "cypress-baserow", + lableApiToken: "API token", + placeholderApiToken:"**************", + }; \ No newline at end of file diff --git a/cypress-tests/cypress/constants/texts/common.js b/cypress-tests/cypress/constants/texts/common.js index 5447e20bd9..bc3f566129 100644 --- a/cypress-tests/cypress/constants/texts/common.js +++ b/cypress-tests/cypress/constants/texts/common.js @@ -173,7 +173,7 @@ export const commonText = { // iframeLinkLabel: "Get embeddable link for this application", // ifameLinkCopyButton: "copy", }, - groupInputFieldLabel: "Select Group", + groupInputFieldLabel: "Select groups", documentationLink: "Read Documentation", constantsNameError: "Constant name should start with a letter or underscore and can only contain letters, numbers and underscores", @@ -181,7 +181,7 @@ export const commonText = { "Value should be less than 10000 characters and cannot be empty", createApp: "Create app", - appName: "App Name", + appName: "App name", enterAppName: "Enter app name", appNameInfoLabel: "App name must be unique and max 50 characters", renameApp: "Rename app", diff --git a/cypress-tests/cypress/constants/texts/dashboard.js b/cypress-tests/cypress/constants/texts/dashboard.js index 3e8f1ab913..09b73d3711 100644 --- a/cypress-tests/cypress/constants/texts/dashboard.js +++ b/cypress-tests/cypress/constants/texts/dashboard.js @@ -40,7 +40,7 @@ export const dashboardText = { appClonedToast: "App cloned successfully!", darkModeText: "Dark Mode", lightModeText: "Light Mode", - dashboardAppsHeaderLabel: " All apps", + dashboardAppsHeaderLabel: "All apps", moveAppText: (appName) => { return `Move "${appName}" to`; diff --git a/cypress-tests/cypress/constants/texts/dataSource.js b/cypress-tests/cypress/constants/texts/dataSource.js index 6a97093d87..ab1f8702d2 100644 --- a/cypress-tests/cypress/constants/texts/dataSource.js +++ b/cypress-tests/cypress/constants/texts/dataSource.js @@ -13,7 +13,7 @@ export const dataSourceText = { ? "Databases (20)" : "Databases (18)"; }, - allApis: "APIs (20)", + allApis: "APIs (21)", allCloudStorage: "Cloud Storages (4)", pluginsLabelAndCount: "Plugins (0)", @@ -80,4 +80,11 @@ export const dataSourceText = { labelNoEventhandler: "No event handlers", toastDSSaved: "Data Source Saved", unSavedModalTitle: "Unsaved Changes", + + sslCertificateLabel: "SSL Certificate", + caCertificateOption: "CA certificate", + clientCertificateOption: "Client certificate", + clientKeyLabel: "Client Key", + clientCertLabel: "Client Cert", + caCertLabel: "CA Cert", }; diff --git a/cypress-tests/cypress/constants/texts/eeCommon.js b/cypress-tests/cypress/constants/texts/eeCommon.js new file mode 100644 index 0000000000..b112762f65 --- /dev/null +++ b/cypress-tests/cypress/constants/texts/eeCommon.js @@ -0,0 +1,75 @@ +export const commonEeText = { + cancelButton: "Cancel", + saveButton: "Save changes", + closeButton: "Close", + defaultWorkspace: "My workspace", +}; + +export const ssoEeText = { + statusLabel: "Disabled", + enabledLabel: "Enabled", + disabledLabel: "Disabled", + oidcPageElements: { + oidcToggleLabel: "OpenID Connect", + nameLabel: "Name", + clientIdLabel: "Client ID", + clientSecretLabel: "Client secretEncrypted", + encryptedLabel: "Encrypted", + WellKnownUrlLabel: "Well known URL", + // redirectUrlLabel: "Redirect URL", + }, + oidcEnabledToast: "Enabled OpenId SSO", + oidcDisabledToast: "Disabled OpenId SSO", + oidcUpdatedToast: "updated SSO configurations", + testName: "Tooljet OIDC", + testclientId: "24567098-mklj8t20za1smb2if.apps.googleusercontent.com", + testclientSecret: "2345-client-id-.apps.googleusercontent.com", + testWellknownUrl: "google.com", + oidcSSOText: "Sign in with Tooljet OIDC", + + ldapPageElements: { + ldapToggleLabel: "LDAP", + nameLabel: "Name", + hostLabel: "Host name", + portLabel: "Port", + baseDnLabel: "Base DN", + baseDnHelperText: "Location without UID or CN", + sslLabel: "SSL", + }, + ldapSSOText: "Sign in with Tooljet LDAP Auth", + userNameInputLabel: "Username", + samlModalElements: { + toggleLabel: "SAML", + NameLabel: "Name", + metaDataLabel: "Identity provider metadata", + baseDNHelperText: + "Ensure the Identity provider metadata is in XML format. You can download it from your IdP's site", + groupAttributeLabel: "Group attribute", + groupAttributeHelperText: + "Define attribute for user-to-group mapping based on the IdP", + }, +}; +export const eeGroupsText = { + resourceDs: "Datasources", + AddDsButton: "Add", + dsNameHeader: "Datasource name", +}; + +export const instanceSettingsText = { + pageTitle: "Settings", + allUsersTab: "All users", + manageInstanceSettings: "Manage instance settings", + typeColumnHeader: "Type", + workspaceColumnHeader: "Workspaces", + superAdminType: "instance", + viewModalTitle: "Workspaces of The Developer", + archiveAllButton: "Archive All", + archiveState: "Archive", + editModalTitle: "Edit user details", + superAdminToggleLabel: "Super admin", + allowWorkspaceToggleLabel: "Allow personal workspace", + allowWorkspaceHelperText: + "This feature will enable users to create their own workspace", + saveButton: "Save", + untitledWorkspace: "Untitled workspace", +}; diff --git a/cypress-tests/cypress/constants/texts/exportImport.js b/cypress-tests/cypress/constants/texts/exportImport.js index 1a405efa77..398cb2f2c4 100644 --- a/cypress-tests/cypress/constants/texts/exportImport.js +++ b/cypress-tests/cypress/constants/texts/exportImport.js @@ -1,7 +1,7 @@ export const appVersionText = { createNewVersion: "Create new version", createVersion: "Create Version", - versionNameLabel: "Version Name", + versionNameLabel: "Version name", createVersionFromLabel: "Create version from", emptyToastMessage: "Version name should not be empty", createdToastMessage: "Version Created", diff --git a/cypress-tests/cypress/constants/texts/graphQL.js b/cypress-tests/cypress/constants/texts/graphQL.js new file mode 100644 index 0000000000..5d1149efd3 --- /dev/null +++ b/cypress-tests/cypress/constants/texts/graphQL.js @@ -0,0 +1,6 @@ +export const GraphQLText = { + GraphQL: "GraphQL", + cypressGraphQL: "cypress-GraphQL", + urlInputLabel: "URL", + urlInputPlaceholder: "https://api.example.com/v1/graphql", +}; diff --git a/cypress-tests/cypress/constants/texts/harperDb.js b/cypress-tests/cypress/constants/texts/harperDb.js new file mode 100644 index 0000000000..11987296a3 --- /dev/null +++ b/cypress-tests/cypress/constants/texts/harperDb.js @@ -0,0 +1,21 @@ +export const harperDbText = { + harperDb: "HarperDB", + cypressHarperDB: "cypressHarperDB", + hostLabel: "Host", + hostInputPlaceholder: "Enter host", + portLabel: "Port", + portPlaceholder: "Enter port", + userNameLabel: "Username", + passwordlabel: "Password", + userNamePlaceholder: "Enter username", + passwordPlaceholder: "**************", + recordsValue: `[{id: 10, name: 'QA', age: 24}]`, + hashValue: "[10]", + attributesValue: "['name']", + searchAttributeValue: "name", + searchValue: "QA", + condtionValue: `[{'search_attribute': 'name', 'search_type': 'between', 'search_value': [1, 5]}, {'search_attribute': 'name', 'search_type': 'equals', 'search_value': 'QA'}]`, + sqlValue: "SELECT * FROM test_schema.test_table", + schemaValue: "test_schema", + tableValue: "test_table", +}; diff --git a/cypress-tests/cypress/constants/texts/manageGroups.js b/cypress-tests/cypress/constants/texts/manageGroups.js index 0b06097b3e..7d1a556cd0 100644 --- a/cypress-tests/cypress/constants/texts/manageGroups.js +++ b/cypress-tests/cypress/constants/texts/manageGroups.js @@ -1,10 +1,10 @@ export const groupsText = { pageTitle: "User Groups", - createNewGroupButton: "Create new group", + createNewGroupButton: "Add new group", tableHeader: "Name", allUsers: "All users", admin: "Admin", - cardTitle: "Create new group", + cardTitle: "Add new group", cancelButton: "Cancel", createGroupButton: "Create Group", groupNameExistToast: "Group name already exist", @@ -52,7 +52,7 @@ export const groupsText = { editGroupNameButton: "Rename", deleteGroupButton: "Delete group", editPermissionModalTitle: "Edit app permissions", - addPermissionModalTitle: "Add app permissions", + addPermissionModalTitle: "Add apps permissions", appCreateHelperText: 'Create apps in this workspace', appDeleteHelperText: 'Delete any app in this workspace', appEditLabelText: 'Edit', @@ -63,7 +63,7 @@ export const groupsText = { appHideLabel: "Hide from dashboard", appHideLabelPermissionModal: "Hide from dashbaord", groupChipText: 'All apps', - adminAccessHelperText: " Admin has edit access to all apps. These are not editableread documentation to know more !", + adminAccessHelperText: " Admin has all permissions. This is not editableread documentation to know more !", enduserAccessHelperText: " End-user can only have permission to view appsread documentation to know more !", nameTableHeader: 'Name', permissionTableHeader: 'Permission', @@ -78,26 +78,26 @@ export const groupsText = { allAppsLabel: 'All apps', allAppsHelperText: 'This will select all apps in the workspace including any new apps created', customLabel: 'Custom', - customHelperText: 'Select specific applications you want to add to the group', + customHelperText: 'Select specific apps you want to add to the group', updateButtonText: 'Update', addButtonText: 'Add', userRole: 'User role', warningText: "Users must be always be part of one default group. This will define the user count in your plan.", continueButtonText: "Continue", roleUpdateToastMessage: "Role updated successfully", - endUserToBuilderMessage: "Updating the user's details will change their role from end-user to builder. Are you sure you want to continue?", - endUserToAdminMessage: "Updating the user's details will change their role from end-user to admin. Are you sure you want to continue?", - builderToEnduserMessage: "This will also remove the user from any custom groups with builder-like permissions.Are you sure you want to continue?", + endUserToBuilderMessage: "Changing the user role from end-user to builder will grant access the user access to all resources.Are you sure you want to continue?", + endUserToAdminMessage: "Changing the user role from end-user to admin will grant the user access to all resources and settings.Are you sure you want to continue?", + builderToEnduserMessage: "Changing the user role from builder to end-user will revoke their access to edit all resources.Are you sure you want to continue?", builderToAdminMessage: "Changing user role from builder to admin will grant access to all resources and settings.Are you sure you want to continue?", adminToBuilderMessage: "Changing your user default group from admin to builder will revoke your access to settings.Are you sure you want to continue?", - adminToEnduserMessage: "Changing your user group from admin to end-user will revoke your access to settings.Are you sure you want to continue?", + adminToEnduserMessage: "Changing the user role from admin to end-user will revoke their access to edit all resources and settings.Are you sure you want to continue?", modalHeader: "Can not remove last active admin", modalMessage: "Cannot change role of last present admin, please add another admin and change the role", userAddedToast: "Users added to the group", changeUserRoleHeader: " Change in user role", changeUserRoleMessage: "Granting this permission to the user group will result in a role change for the following user(s) from end-users to builders. Are you sure you want to continue?", cantCreatePermissionModalHeader: "Cannot create permissions", - cantCreatePermissionModalMessage: "Cannot assign builder level permission to end users", + cantCreatePermissionModalMessage: "End-users can only be granted permission to view apps. If you wish to add this permission, kindly change the following users role from end-user to builder", deletePermissionToast: "Deleted permission successfully", createPermissionToast: "Permission created successfully!", userEmptyPageTitle: "No users added yet", diff --git a/cypress-tests/cypress/constants/texts/manageSSO.js b/cypress-tests/cypress/constants/texts/manageSSO.js index c1a95e7179..56052be58f 100644 --- a/cypress-tests/cypress/constants/texts/manageSSO.js +++ b/cypress-tests/cypress/constants/texts/manageSSO.js @@ -59,4 +59,9 @@ export const ssoText = { alertText: "Danger zone", disablePasswordHelperText: "Disable password login only if your SSO is configured otherwise you will get locked out", + disablePasswordHelperText: + "Disable password login only if your SSO is configured otherwise you will get locked out", + toggleUpdateToast: (toggle) => { + return `Saved ${toggle} SSO configurations` + } }; diff --git a/cypress-tests/cypress/constants/texts/manageUsers.js b/cypress-tests/cypress/constants/texts/manageUsers.js index 3e463c685a..d4708ea79a 100644 --- a/cypress-tests/cypress/constants/texts/manageUsers.js +++ b/cypress-tests/cypress/constants/texts/manageUsers.js @@ -7,7 +7,7 @@ export const usersText = { usersFilterLabel: "Showing", }, usersPageTitle: "users", - breadcrumbUsersPageTitle: " Users", + breadcrumbUsersPageTitle: "Users", adminUserName: "The Developer", adminUserEmail: "dev@tooljet.io", adminUserState: "Archive", @@ -57,7 +57,7 @@ export const usersText = { buttonUploadCsvFile: "Upload CSV file", helperTextBulkUpload: - "Download the ToolJet template to add user details or format your file in the same as the template. ToolJet won’t be able to recognise files in any other format. ", + "Download the template to add user details or format your file in the same way as the template. Files in any other format may not be recognized. ", helperTextSelectFile: "Select a CSV file to upload", helperTextDropFile: "Or drag and drop it here", }; diff --git a/cypress-tests/cypress/constants/texts/minio.js b/cypress-tests/cypress/constants/texts/minio.js new file mode 100644 index 0000000000..5afd1ed477 --- /dev/null +++ b/cypress-tests/cypress/constants/texts/minio.js @@ -0,0 +1,14 @@ +export const minioText = { + minio: "Minio", + cypressMinio: "cypressMinio", + hostLabel: "Host", + hostInputPlaceholder: "Enter host", + portLabel: "Port", + portPlaceholder: "Enter port", + labelAccesskey: "Access key", + labelSecretKey: "Secret key", + placeholderAccessKey: "Enter access key", + placeholderSecretKey: "**************", + bucketName: `my-second-bucket`, + objectName: `mybucket`, +}; diff --git a/cypress-tests/cypress/constants/texts/onboarding.js b/cypress-tests/cypress/constants/texts/onboarding.js index a41a654525..18efeb9006 100644 --- a/cypress-tests/cypress/constants/texts/onboarding.js +++ b/cypress-tests/cypress/constants/texts/onboarding.js @@ -23,8 +23,8 @@ export const onboardingText = { endUserPriceText: "$10", comparePlansText: "Compare plans", basicPlanText: "Basic Plan", - flexibleText: "Flexible", - businessText: "Business", + flexibleText: "Pro", + businessText: "Team", enterpriseText: "Enterprise", customPricingText: "Custom pricing", noCreditCardText: "No credit card required!", diff --git a/cypress-tests/cypress/constants/texts/postgreSql.js b/cypress-tests/cypress/constants/texts/postgreSql.js index 5fdff49ae4..d5c85c197b 100644 --- a/cypress-tests/cypress/constants/texts/postgreSql.js +++ b/cypress-tests/cypress/constants/texts/postgreSql.js @@ -4,8 +4,8 @@ export const postgreSqlText = { allDataSources: () => { return Cypress.env("marketplace_action") - ? "All data sources (44)" - : "All data sources (42)"; + ? "All data sources (45)" + : "All data sources (43)"; }, commonlyUsed: "Commonly used (5)", allDatabase: () => { @@ -13,17 +13,21 @@ export const postgreSqlText = { ? "Databases (20)" : "Databases (18)"; }, - allApis: "APIs (20)", + allApis: "APIs (21)", allCloudStorage: "Cloud Storages (4)", postgreSQL: "PostgreSQL", + labelConnectionType: "Connection type", + manualConnectionOption: "Manual connection", + connectionStringOption: "Connection string", labelHost: "Host", labelPort: "Port", labelSSL: "SSL", labelDbName: "Database name", labelUserName: "Username", labelPassword: "Password", - label: "Encrypted", + labelEncrypted: "Encrypted", + labelConnectionOptions: "Connection options", sslCertificate: "SSL certificate", whiteListIpText: "Please white-list our IP address if the data source is not publicly accessible", @@ -74,6 +78,8 @@ export const postgreSqlText = { guiOptionBulkUpdate: "Bulk update using primary key", buttonTextTestConnection: "Test connection", + editButtonText: "Edit", + unableAcquireConnectionAlertText: "Unable to acquire a connection", tabAdvanced: "Advanced", labelNoEventhandler: "No event handlers", diff --git a/cypress-tests/cypress/constants/texts/redis.js b/cypress-tests/cypress/constants/texts/redis.js index f249442310..588af00c47 100644 --- a/cypress-tests/cypress/constants/texts/redis.js +++ b/cypress-tests/cypress/constants/texts/redis.js @@ -2,8 +2,7 @@ export const redisText = { redis: "Redis", cypressRedis: "cypress-redis", - errorMaxRetries: - 'Reached the max retries per request limit (which is 1). Refer to "maxRetriesPerRequest" option for details.', - errorPort: "Port should be >= 0 and < 65536. Received type number (108299).", - errorInvalidUserOrPassword: "WRONGPASS invalid username-password pair", + errorMaxRetries: "Connection could not be established", + errorPort: "Connection could not be established", + errorInvalidUserOrPassword: "Connection could not be established", }; diff --git a/cypress-tests/cypress/constants/texts/restAPI.js b/cypress-tests/cypress/constants/texts/restAPI.js new file mode 100644 index 0000000000..dd10f8624b --- /dev/null +++ b/cypress-tests/cypress/constants/texts/restAPI.js @@ -0,0 +1,58 @@ +export const restAPIText = { + restAPI: "REST API", + credentialsText: "CREDENTIALS", + baseUrlLabel: "Base URL", + headersLabel: "Headers", + urlParametesLabel: "URL parameters", + bodyLabel: "Body", + cookiesLabel: "Cookies", + authenticationText: "AUTHENTICATION", + authenticationTypeLabel: "Authentication type", + noneText: "None", + editButtonText: "Edit", + basicAuth: { + basicText: "Basic", + usernameLabel: "Username", + passwordLabel: "Password", + }, + bearerAuth: { + bearerText: "Bearer", + tokenLabel: "Token", + }, + oAuthText: "OAuth 2.0", + grantTypeLabel: "Grant type", + authorizationCode: { + authorizationCodeLabel: "Authorization code", + addAccessTokenLabel: "Add access token to", + headerPrefixLabel: "Header prefix", + requestHeader: "Request header", + accessTokenURLLabel: "Access token URL", + accessTokenURLCustomHeadersLabel: "Access token URL custom headers", + clientIDLabel: "Client ID", + clientSecretLabel: "Client secret", + scopeLabel: "Scope(s)", + customQueryParametersLabel: "Custom query parameters", + authorizationURLLabel: "Authorization URL", + customAuthenticationParametersLabel: "Custom authentication parameters", + clientAuthentication: "Client authentication", + sendBasicAuthheaderOption: "Send as basic auth header", + sendClientCredentialsBodyOption: "Send client credentials in body", + authenticationRequiredUsersToggle: "Authentication required for all users", + }, + + clientCredentials: { + clientCredentialsLabel: "Client credentials", + accessTokenURLLabel: "Access token URL", + accessTokenURLCustomHeadersLabel: "Access token URL custom headers", + clientIDLabel: "Client ID", + clientSecretLabel: "Client secret", + scopeLabel: "Scope(s)", + audiencelabel: "Audience", + }, + authenticationHeader: "Authentication", + secureSocketsLayerText: "SECURE SOCKETS LAYER", + generalSettingsText: "GENERAL SETTINGS", + retryNetworkErrorsToggleLabel: "Retry on network errors", + retryToggleHelperText: + "By default, ToolJet tries to hit API endpoint 3 times before declaring query failed as server did not respond", +}; diff --git a/cypress-tests/cypress/constants/texts/twilio.js b/cypress-tests/cypress/constants/texts/twilio.js new file mode 100644 index 0000000000..535bfb3b8a --- /dev/null +++ b/cypress-tests/cypress/constants/texts/twilio.js @@ -0,0 +1,11 @@ +export const twilioText = { + twilio: "Twilio", + cypresstwilio: "cypress-Twilio", + authTokenLabel: "Auth Token", + authTokenPlaceholder: "**************", + accountSidLabel: "Account SID", + accountSidPlaceholder: "Account SID for Twilio", + messagingSIDLabel: "Messaging Service SID", + messagingSIDPalceholder: "Messaging Service SID for Twilio", + messageText: "Sending test message to check twilio", +}; diff --git a/cypress-tests/cypress/constants/texts/version.js b/cypress-tests/cypress/constants/texts/version.js index e6eb855f49..ed92ea4c29 100644 --- a/cypress-tests/cypress/constants/texts/version.js +++ b/cypress-tests/cypress/constants/texts/version.js @@ -19,9 +19,13 @@ export const deleteVersionText = { export const onlydeleteVersionText = { deleteModalText: (text) => { - return `Are you sure you want to delete this version - ${cyParamName( + return `Deleting a version will permanently remove it from all environments.Are you sure you want to delete this version - ${cyParamName( text )}?`; + + // `Are you sure you want to delete this version - ${cyParamName( + // text + // )}?`; }, deleteToastMessage: (version) => { return `Cannot delete only version of app`; diff --git a/cypress-tests/cypress/constants/texts/workspaceConstants.js b/cypress-tests/cypress/constants/texts/workspaceConstants.js index a8abc75bf9..8073482d93 100644 --- a/cypress-tests/cypress/constants/texts/workspaceConstants.js +++ b/cypress-tests/cypress/constants/texts/workspaceConstants.js @@ -4,7 +4,7 @@ export const workspaceConstantsText = { secretsConstantInfo: "To resolve a secret workspace constant use {{secrets.access_token}}Read documentation", emptyStateHeader: "No Workspace constants yet", emptyStateText: - "Use workspace constants seamlessly in both the app builder and data source connections across ToolJet.", + "Use workspace constants seamlessly within both the app builder and data source connections across the platform.", addNewConstantButton: "+ Create new constant", addConstatntText: "Add new constant in production ", constantCreatedToast: (type) => { return `${type} constant created successfully!` }, diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/codehinter.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/codehinter.skip.js new file mode 100644 index 0000000000..78e5490c06 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/codehinter.skip.js @@ -0,0 +1,29 @@ +import { fake } from "Fixtures/fake"; +import { addAndVerifyOnSingleLine } from "Support/utils/editor/codehinter"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + openEditorSidebar, +} from "Support/utils/commonWidget"; + +describe("Editor- CodeHinter", () => { + let currentVersion = ""; + let newVersion = []; + let versionFrom = ""; + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-inspector-App`); + cy.apiOpenApp(); + }); + + it.only("should verify singleLine: non dynamic values", () => { + cy.dragAndDropWidget("Text", 500, 500); + addAndVerifyOnSingleLine(`{{globals.currentUser.email}} {{globals.currentUser.firstName}} {{globals.currentUser.lastName}} {{globals.currentUser.id}} {{globals.currentUser.avatarId}} {{globals.currentUser.groups[0]}} {{globals.currentUser.groups[1]}} {{globals.currentUser.groups[2]}} {{globals.currentUser.role}} {{globals.currentUser.ssoUserInfo}} {{globals.environment.id}} {{globals.environment.name}} {{globals.mode.value}} {{globals.theme.name}} {{globals.urlparams}} {{page.handle}} {{page.id}} {{page.name}}`) + cy.get('[data-cy=draggable-widget-text1]:eq(0)').invoke('text').then((text => { cy.log(text) })) + }); + it("should verify singleLine: dynamic values", () => { + }); + it("should verify multiLine: non dynamic values", () => { + }); + it("should verify multiLine: dynamic values", () => { + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/codehinterResolver.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/codehinterResolver.skip.js new file mode 100644 index 0000000000..705f592e8f --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/codehinterResolver.skip.js @@ -0,0 +1,235 @@ +import { fake } from "Fixtures/fake"; +import { addAndVerifyOnSingleLine } from "Support/utils/editor/codehinter"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + openEditorSidebar, +} from "Support/utils/commonWidget"; + +describe.only('Tooljet Resolution Cases', () => { + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-inspector-App`); + cy.openApp(); + }); + + it.only('Basic Component and Query Value Access', () => { + + cy.dragAndDropWidget("Text Input", 100, 200); + cy.dragAndDropWidget("Text", 100, 100); + addAndVerifyOnSingleLine('{{components.textinput1.value}}'); + cy.get(commonWidgetSelector.draggableWidget("textinput1")).type("Hello World"); + cy.get(commonWidgetSelector.draggableWidget("text1")).should('have.text', 'Hello World'); + cy.pause() + + // Example 2 + // cy.dragAndDropWidget('Table', 100, 300); + // addAndVerifyOnSingleLine('{{queries.user.data}}', 'data'); + + // // Example 3 + // cy.dragAndDropWidget('Text', 200, 100); + // addAndVerifyOnSingleLine('{{queries.name.data.hello}}'); + + // // Example 4 + // cy.dragAndDropWidget('Text', 300, 100); + // addAndVerifyOnSingleLine('{{queries.products[components.dropdown1.value]?.details.price}}'); + + // Example 5 + cy.dragAndDropWidget('Text', 400, 100); + addAndVerifyOnSingleLine('{{globals.currentUser.email}}'); + cy.get(commonWidgetSelector.draggableWidget("text2")).should('have.text', 'dev@tooljet.io'); + + // Example 6 + cy.dragAndDropWidget('Text', 500, 100); + addAndVerifyOnSingleLine('{{page.variables.pageTitle}}'); + + // Example 7 + cy.dragAndDropWidget('variables.userId'); + addAndVerifyOnSingleLine('{{variables.userId}}'); + + // Example 8 + cy.dragAndDropWidget('queries.localeHelper.data.PRFieldHint'); + addAndVerifyOnSingleLine('{{queries.localeHelper.data.PRFieldHint}}'); + + // Example 9 + cy.dragAndDropWidget('queries.8256e53e-061f-4108-8ab5-d9a0db607e8f.data.countryFieldHint'); + addAndVerifyOnSingleLine('{{queries.8256e53e-061f-4108-8ab5-d9a0db607e8f.data.countryFieldHint}}'); + + // Example 10 + cy.dragAndDropWidget('page.otherProperty.someValue'); + addAndVerifyOnSingleLine('{{page.otherProperty.someValue}}'); + }); + + it('Dynamic Access Using Brackets', () => { + // Example 1 + cy.dragAndDropWidget('components[components.text1.data].value'); + addAndVerifyOnSingleLine('{{components[components.text1.data].value}}'); + + // Example 2 + cy.dragAndDropWidget('queries[ ${components.dropdown1.value}].data.users'); + addAndVerifyOnSingleLine('{{queries[ ${components.dropdown1.value}].data.users}}'); + + // Example 3 + cy.dragAndDropWidget('queries["components.dropdown.value"]?.data'); + addAndVerifyOnSingleLine('{{queries["components.dropdown.value"]?.data}}'); + + // Example 4 + cy.dragAndDropWidget('components[queries.products.data].value'); + addAndVerifyOnSingleLine('{{components[queries.products.data].value}}'); + + // Example 5 + cy.dragAndDropWidget('queries[queries.user.data.fieldKey]?.data'); + addAndVerifyOnSingleLine('{{queries[queries.user.data.fieldKey]?.data}}'); + }); + + it('Logical Operators and Conditionals', () => { + // Example 1 + cy.dragAndDropWidget('components.text1.value || queries.user.data'); + addAndVerifyOnSingleLine('{{components.text1.value || queries.user.data}}'); + + // Example 2 + cy.dragAndDropWidget('queries.currency.data?.currency || globals.defaultCurrency'); + addAndVerifyOnSingleLine('{{queries.currency.data?.currency || globals.defaultCurrency}}'); + + // Example 3 + cy.dragAndDropWidget('components.text1.value ? components.text1.value + " - active" : "Inactive"'); + addAndVerifyOnSingleLine('{{components.text1.value ? components.text1.value + " - active" : "Inactive"}}'); + + // Example 4 + cy.dragAndDropWidget('(components.numberinput1.value || 0).toFixed(2).toString().padStart(8, "0")'); + addAndVerifyOnSingleLine('{{(components.numberinput1.value || 0).toFixed(2).toString().padStart(8, "0")}}'); + }); + + it('String Manipulation', () => { + // Example 1 + cy.dragAndDropWidget('components.text1.value.toUpperCase()'); + addAndVerifyOnSingleLine('{{components.text1.value.toUpperCase()}}'); + + // Example 2 + cy.dragAndDropWidget('variables.userId.substring(0, 5)'); + addAndVerifyOnSingleLine('{{variables.userId.substring(0, 5)}}'); + + // Example 3 + cy.dragAndDropWidget('Hello {{components.text1.value}}'); + addAndVerifyOnSingleLine('Hello {{components.text1.value}}'); + + // Example 4 + cy.dragAndDropWidget('Hello {{components.text1.value + "!"}}'); + addAndVerifyOnSingleLine('Hello {{components.text1.value + "!"}}'); + + // Example 5 + cy.dragAndDropWidget('components.text1.data?.toString() || "default"'); + addAndVerifyOnSingleLine('{{components.text1.data?.toString() || "default"}}'); + + // Example 6 + cy.dragAndDropWidget('components.text1.value + " " + components.text2.value'); + addAndVerifyOnSingleLine('{{components.text1.value + " " + components.text2.value}}'); + + // Example 7 + cy.dragAndDropWidget('"Real time Range 1 : " + components.rangeslider2.value[0] + ""'); + addAndVerifyOnSingleLine('{{"Real time Range 1 : " + components.rangeslider2.value[0] + ""}}'); + }); + + it('Arrays and Iterations', () => { + // Example 1 + cy.dragAndDropWidget('queries.orders.data.map(order => order.id).join(", ")'); + addAndVerifyOnSingleLine('{{queries.orders.data.map(order => order.id).join(", ")}}'); + + // Example 2 + cy.dragAndDropWidget('Array.from({length: queries?.tooljetdbGetProducts?.data?.filter(product => product.id == components.dropdown1.value)[0]?.quantity ?? 0}, (_, i) => i + 1)'); + addAndVerifyOnSingleLine('{{Array.from({length: queries?.tooljetdbGetProducts?.data?.filter(product => product.id == components.dropdown1.value)[0]?.quantity ?? 0}, (_, i) => i + 1)}}'); + + // Example 3 + cy.dragAndDropWidget('queries.user?.data?.filter(item => item.id === components.selected.value)'); + addAndVerifyOnSingleLine('{{queries.user?.data?.filter(item => item.id === components.selected.value)}}'); + + // Example 4 + cy.dragAndDropWidget('queries.products.data.find(p => p.id === variables.productId)?.name || "Unknown"'); + addAndVerifyOnSingleLine('{{queries.products.data.find(p => p.id === variables.productId)?.name || "Unknown"}}'); + + // Example 5 + cy.dragAndDropWidget('queries.data.items.filter(item => item.active).length'); + addAndVerifyOnSingleLine('{{queries.data.items.filter(item => item.active).length}}'); + }); + + it('Numeric Operations', () => { + // Example 1 + cy.dragAndDropWidget('10 * (components.pagination1.currentPageIndex - 1)'); + addAndVerifyOnSingleLine('{{10 * (components.pagination1.currentPageIndex - 1)}}'); + + // Example 2 + cy.dragAndDropWidget('components?.pagination1?.currentPageIndex + 1'); + addAndVerifyOnSingleLine('{{components?.pagination1?.currentPageIndex + 1}}'); + }); + + it('Nested Property Access', () => { + // Example 1 + cy.dragAndDropWidget('queries.user[components.text1.data].extra.field'); + addAndVerifyOnSingleLine('{{queries.user[components.text1.data].extra.field}}'); + + // Example 2 + cy.dragAndDropWidget('queries.user[components.button1.data].extra.field'); + addAndVerifyOnSingleLine('{{queries.user[components.button1.data].extra.field}}'); + + // Example 3 + cy.dragAndDropWidget('queries.user.data[0]'); + addAndVerifyOnSingleLine('{{queries.user.data[0]}}'); + + // Example 4 + cy.dragAndDropWidget('components.text1.data'); + addAndVerifyOnSingleLine('{{components.text1.data}}'); + }); + + it('Complex Template Strings', () => { + // Example 1 + cy.dragAndDropWidget('Hello {{components.text1.value}} {{variables.userId}} {{globals.apiKey}}'); + addAndVerifyOnSingleLine('Hello {{components.text1.value}} {{variables.userId}} {{globals.apiKey}}'); + + // Example 2 + cy.dragAndDropWidget('page.variables.pageTitle'); + addAndVerifyOnSingleLine('{{page.variables.pageTitle}}'); + + // Example 3 + cy.dragAndDropWidget('components.text1.value + variables.userId + globals.apiKey + page.variables.pageTitle'); + addAndVerifyOnSingleLine('{{components.text1.value + variables.userId + globals.apiKey + page.variables.pageTitle}}'); + }); + + it('Nullable/Optional Chaining', () => { + // Example 1 + cy.dragAndDropWidget('components?.text1?.value'); + addAndVerifyOnSingleLine('{{components?.text1?.value}}'); + + // Example 2 + cy.dragAndDropWidget('components?.text1["value"]'); + addAndVerifyOnSingleLine('{{components?.text1["value"]}}'); + + // Example 3 + cy.dragAndDropWidget('queries["user"]?.data'); + addAndVerifyOnSingleLine('{{queries["user"]?.data}}'); + + // Example 4 + cy.dragAndDropWidget('queries?.tooljetdbGetProducts?.data?.filter'); + addAndVerifyOnSingleLine('{{queries?.tooljetdbGetProducts?.data?.filter}}'); + }); + + it('Need to be Verified', () => { + // Example 1 + cy.dragAndDropWidget('components[queries.user.data[0]].value'); + addAndVerifyOnSingleLine('{{components[queries.user.data[0]].value}}'); + + // Example 2 + cy.dragAndDropWidget('queries.products.data.find(p => p.id === variables.productId)?.name || "Unknown"'); + addAndVerifyOnSingleLine('{{queries.products.data.find(p => p.id === variables.productId)?.name || "Unknown"}}'); + + // Example 3 + cy.dragAndDropWidget('queries[ ${components.dropdown1.value}_${components.dropdown2.value}]?.data'); + addAndVerifyOnSingleLine('{{queries[ ${components.dropdown1.value}_${components.dropdown2.value}]?.data}}'); + + // Example 4 + cy.dragAndDropWidget('Array.from({length: queries?.tooljetdbGetProducts?.data?.filter(product => product.id == components.dropdown1.value)[0]?.quantity ?? 0}, (_, i) => i + 1)'); + addAndVerifyOnSingleLine('{{Array.from({length: queries?.tooljetdbGetProducts?.data?.filter(product => product.id == components.dropdown1.value)[0]?.quantity ?? 0}, (_, i) => i + 1)}}'); + + // Example 5 + cy.dragAndDropWidget('queries["components.dropdown.value"]?.data'); + addAndVerifyOnSingleLine('{{queries["components.dropdown.value"]?.data}}'); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/buttonHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/buttonHappyPath.cy.js index 67be49e530..acc65395f7 100644 --- a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/buttonHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/buttonHappyPath.cy.js @@ -32,7 +32,7 @@ import { addSupportCSAData, } from "Support/utils/events"; -describe("Editor- Test Button widget", () => { +describe("Editor- Test Button widget ", () => { beforeEach(() => { cy.apiLogin(); cy.apiCreateApp(`${fake.companyName}-button-App`); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/multiselectHappyPath.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/multiselectHappyPath.skip.js new file mode 100644 index 0000000000..369adec510 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/multiselectHappyPath.skip.js @@ -0,0 +1,429 @@ +import { multiselectSelector } from "Selectors/multiselect"; +import { multiselectText } from "Texts/multiselect"; +import { + commonText, + commonWidgetText, + codeMirrorInputLabel, +} from "Texts/common"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { fake } from "Fixtures/fake"; + +import { + verifyMultiselectHeader, + selectFromMultiSelect, + verifyMultiselectStatus, + verifyMultiselectOptions, +} from "Support/utils/multiselectWidget"; +import { + openAccordion, + verifyAndModifyParameter, + openEditorSidebar, + verifyAndModifyToggleFx, + addDefaultEventHandler, + addAndVerifyTooltip, + editAndVerifyWidgetName, + verifyMultipleComponentValuesFromInspector, + selectColourFromColourPicker, + fillBoxShadowParams, + verifyBoxShadowCss, + verifyAndModifyStylePickerFx, + addTextWidgetToVerifyValue, + verifyTooltip, + verifyWidgetText, +} from "Support/utils/commonWidget"; + +import { + selectCSA, + selectEvent, + addSupportCSAData, +} from "Support/utils/events"; + +describe("Multiselect widget", () => { + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Multiselect-App`); + cy.openApp(); + cy.dragAndDropWidget(multiselectText.multiselect); + }); + afterEach(() => { + cy.apiDeleteApp(); + }); + + it("should verify the properties of the widget", () => { + const data = {}; + data.widgetName = fake.widgetName; + data.label = fake.widgetName; + data.customMessage = fake.randomSentence; + data.alertMessage = fake.randomSentence; + data.randomLabels = multiselectSelector.textArrayOfLength(3); + + openEditorSidebar(multiselectText.defaultWidgetName); + editAndVerifyWidgetName(data.widgetName); + + openAccordion(commonWidgetText.accordionProperties, [ + "Events", + "Properties", + "General", + ]); + verifyAndModifyParameter(commonWidgetText.parameterLabel, data.label); + cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click({ + force: true, + }); + cy.get(multiselectSelector.multiselectLabel(data.widgetName)).should( + "have.text", + data.label + ); + + openEditorSidebar(data.widgetName); + verifyAndModifyParameter( + commonWidgetText.labelDefaultValue, + codeMirrorInputLabel("[1,2,3]") + ); + cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click({ + force: true, + }); + verifyMultiselectHeader( + data.widgetName, + multiselectText.labelAllItemsSelected + ); + verifyMultipleComponentValuesFromInspector(data.widgetName, [1, 2, 3]); + + openEditorSidebar(data.widgetName); + verifyAndModifyParameter( + commonWidgetText.labelDefaultValue, + codeMirrorInputLabel("[1,2]") + ); + verifyMultipleComponentValuesFromInspector(data.widgetName, [1, 2]); + + verifyMultiselectHeader(data.widgetName, "one, two"); + verifyMultiselectStatus(data.widgetName); + verifyMultiselectOptions(data.widgetName); + + selectFromMultiSelect(data.widgetName, ["", "", "true"]); + verifyMultiselectStatus(data.widgetName, ["", "", ""]); + verifyMultiselectHeader( + data.widgetName, + multiselectText.labelAllItemsSelected + ); + + verifyMultipleComponentValuesFromInspector(data.widgetName, [1, 2, 3]); + + openEditorSidebar(data.widgetName); + verifyAndModifyParameter( + commonWidgetText.parameterOptionvalues, + codeMirrorInputLabel(`[${data.randomLabels}]`) + ); + selectFromMultiSelect(data.widgetName, ["true", "true", "true"]); + + verifyMultipleComponentValuesFromInspector( + data.widgetName, + data.randomLabels + ); + + openEditorSidebar(data.widgetName); + data.randomLabels = multiselectSelector.textArrayOfLength(3); + verifyAndModifyParameter( + commonWidgetText.parameterOptionLabels, + codeMirrorInputLabel(`[${data.randomLabels}]`) + ); + verifyMultiselectOptions(data.widgetName, data.randomLabels); + + openEditorSidebar(data.widgetName); + verifyAndModifyToggleFx(multiselectText.enableSelectAllOptions); + + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)) + .find(multiselectSelector.multiselectHeader) + .click(); + cy.get(multiselectSelector.dropdownAllItems) + .first() + .should("have.text", multiselectText.dropdwonOptionSelectAll) + .realClick(); + + verifyMultiselectHeader( + data.widgetName, + multiselectText.labelAllItemsSelected + ); + + openEditorSidebar(data.widgetName); + openAccordion(commonWidgetText.accordionEvents); + cy.get(commonWidgetSelector.noEventHandlerMessage).should( + "have.text", + multiselectText.noEventsMessage + ); + addDefaultEventHandler(data.alertMessage); + cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click({ + force: true, + }); + + selectFromMultiSelect(data.widgetName, ["", "", "true"]); + cy.verifyToastMessage(commonSelectors.toastMessage, data.alertMessage); + + openEditorSidebar(data.widgetName); + openAccordion(commonWidgetText.accordionGenaral); + addAndVerifyTooltip( + commonWidgetSelector.draggableWidget(data.widgetName), + fake.randomSentence + ); + + openEditorSidebar(data.widgetName); + openAccordion(commonWidgetText.accordionLayout); + verifyAndModifyToggleFx( + commonWidgetText.parameterShowOnDesktop, + commonWidgetText.codeMirrorLabelTrue + ); + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).should( + "not.exist" + ); + + // verifyAndModifyToggleFx( + // commonWidgetText.parameterShowOnMobile, + // commonWidgetText.codeMirrorLabelFalse + // ); + // cy.get(commonWidgetSelector.changeLayoutButton).click(); + // cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).should( + // "exist" + // ); + }); + + it("should verify the styles of the widget", () => { + const data = {}; + data.colour = fake.randomRgba; + data.boxShadowParam = fake.boxShadowParam; + + openEditorSidebar(multiselectText.defaultWidgetName); + cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click(); + + verifyAndModifyToggleFx( + commonWidgetText.parameterVisibility, + commonWidgetText.codeMirrorLabelTrue + ); + cy.get( + commonWidgetSelector.draggableWidget(multiselectText.defaultWidgetName) + ).should("not.be.visible"); + cy.get( + commonWidgetSelector.parameterTogglebutton( + commonWidgetText.parameterVisibility + ) + ).click(); + verifyAndModifyToggleFx( + commonWidgetText.parameterDisable, + commonWidgetText.codeMirrorLabelFalse + ); + cy.waitForAutoSave(); + cy.get( + commonWidgetSelector.draggableWidget(multiselectText.defaultWidgetName) + ) + .find(multiselectSelector.dropdownContainer) + .should("have.attr", "aria-disabled", "true"); + + verifyAndModifyParameter( + commonWidgetText.parameterBorderRadius, + commonWidgetText.borderRadiusInput + ); + cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click({ + force: true, + }); + cy.get( + commonWidgetSelector.draggableWidget(multiselectText.defaultWidgetName) + ) + .children(".h-100") + .should("have.css", "border-radius", "20px"); + + openEditorSidebar(multiselectText.defaultWidgetName); + cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click(); + openAccordion(commonWidgetText.accordionGenaral, [], 1); + + verifyAndModifyStylePickerFx( + commonWidgetText.parameterBoxShadow, + commonWidgetText.boxShadowDefaultValue, + commonWidgetText.boxShadowFxValue, + 0, + "0px 0px 0px 0px " + ); + cy.get( + commonWidgetSelector.parameterFxButton( + commonWidgetText.parameterBoxShadow + ) + ).click(); + + cy.get( + commonWidgetSelector.stylePicker(commonWidgetText.parameterBoxShadow) + ).click(); + fillBoxShadowParams( + commonWidgetSelector.boxShadowDefaultParam, + data.boxShadowParam + ); + + selectColourFromColourPicker(commonWidgetText.boxShadowColor, data.colour); + verifyBoxShadowCss( + `${commonWidgetSelector.draggableWidget( + multiselectText.defaultWidgetName + )}>.col`, + data.colour, + data.boxShadowParam, + "child" + ); + }); + + it("should verify widget in preview", () => { + const data = {}; + data.widgetName = fake.widgetName; + data.label = fake.widgetName; + data.customMessage = fake.randomSentence; + data.alertMessage = fake.randomSentence; + data.colour = fake.randomRgba; + data.boxShadowParam = fake.boxShadowParam; + data.randomLabels = multiselectSelector.textArrayOfLength(3); + data.randomValues = multiselectSelector.textArrayOfLength(3); + + openEditorSidebar(multiselectText.defaultWidgetName); + editAndVerifyWidgetName(data.widgetName); + verifyAndModifyParameter(commonWidgetText.parameterLabel, data.label); + verifyAndModifyParameter( + commonWidgetText.labelDefaultValue, + codeMirrorInputLabel(`[${data.randomValues[0]}]`) + ); + + verifyAndModifyToggleFx(multiselectText.enableSelectAllOptions); + verifyAndModifyParameter( + commonWidgetText.parameterOptionvalues, + codeMirrorInputLabel(`[${data.randomValues}]`) + ); + + openAccordion(commonWidgetText.accordionEvents); + addDefaultEventHandler(data.alertMessage); + + openAccordion(commonWidgetText.accordionGenaral); + addAndVerifyTooltip( + commonWidgetSelector.draggableWidget(data.widgetName), + data.customMessage + ); + + openEditorSidebar(data.widgetName); + cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click(); + openAccordion(commonWidgetText.accordionGenaral, [], 1); + + cy.get( + commonWidgetSelector.stylePicker(commonWidgetText.parameterBoxShadow) + ).click(); + + fillBoxShadowParams( + commonWidgetSelector.boxShadowDefaultParam, + data.boxShadowParam + ); + selectColourFromColourPicker(commonWidgetText.boxShadowColor, data.colour); + verifyAndModifyParameter( + commonWidgetText.parameterBorderRadius, + commonWidgetText.borderRadiusInput + ); + + addTextWidgetToVerifyValue(`components.${data.widgetName}.values`); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + + cy.get(multiselectSelector.multiselectLabel(data.widgetName)).should( + "have.text", + `${data.label}` + ); + + verifyMultiselectOptions(data.widgetName, [ + "Select All", + "one", + "two", + "three", + ]); + verifyWidgetText( + commonWidgetText.text1, + `${data.randomValues[0].replaceAll('"', "")}` + ); + + selectFromMultiSelect(data.widgetName, ["", "", "true"]); + cy.verifyToastMessage(commonSelectors.toastMessage, data.alertMessage); + verifyWidgetText( + commonWidgetText.text1, + `${data.randomValues.slice(0, 2)}`.replaceAll('"', "") + ); + + selectFromMultiSelect(data.widgetName, ["true"]); + verifyWidgetText( + commonWidgetText.text1, + `${data.randomValues}`.replaceAll('"', "") + ); + + verifyMultiselectHeader( + data.widgetName, + multiselectText.labelAllItemsSelected + ); + + verifyTooltip( + commonWidgetSelector.draggableWidget(data.widgetName), + data.customMessage + ); + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)) + .children(".h-100") + .should("have.css", "border-radius", "20px"); + + verifyBoxShadowCss( + `${commonWidgetSelector.draggableWidget(data.widgetName)}>.col`, + data.colour, + data.boxShadowParam, + "child" + ); + }); + + it("should verify CSA", () => { + cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); + cy.dragAndDropWidget("Number input", 600, 50); + selectEvent("On change", "Control Component"); + selectCSA("multiselect1", "Select Option", "1000"); + cy.get('[data-cy="action-label"]').click({ force: true }); + cy.wait(2000); + addSupportCSAData("Option", "{{components.numberinput1.value"); + // cy.get('[data-cy="-input-field"]') + // .eq(1) + // .type(`{selectAll}{backspace}1000{enter}`); + + cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); + cy.dragAndDropWidget("Number input", 600, 150); + selectEvent("On change", "Control Component"); + selectCSA("multiselect1", "Deselect Option", "1000"); + cy.wait(2000); + cy.get('[data-cy="action-label"]').click({ force: true }); + cy.wait(2000); + addSupportCSAData("Option", "{{components.numberinput2.value"); + // cy.get('[data-cy="-input-field"]') + // .eq(1) + // .type(`{selectAll}{backspace}1000{enter}`); + + cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); + cy.dragAndDropWidget("Button", 600, 250); + selectEvent("On click", "Control Component"); + selectCSA("Multiselect1", "Clear selections"); + cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); + cy.waitForAutoSave(); + + cy.reload(); + cy.wait(3000); + + verifyMultipleComponentValuesFromInspector("multiselect1", [2, 3]); + cy.get(commonWidgetSelector.draggableWidget("numberinput1")) + .clear() + .type("1"); + cy.forceClickOnCanvas(); + cy.wait(1000); + verifyMultiselectHeader( + "multiselect1", + multiselectText.labelAllItemsSelected + ); + cy.get(commonWidgetSelector.draggableWidget("numberinput2")) + .clear() + .type("3"); + cy.forceClickOnCanvas(); + cy.wait(1000); + verifyMultipleComponentValuesFromInspector("multiselect1", [2, 1]); + + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + + verifyMultiselectHeader("multiselect1", "Select..."); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/textInputHappyPath.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/textInputHappyPath.skip.js new file mode 100644 index 0000000000..16844a6a04 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/components/textInputHappyPath.skip.js @@ -0,0 +1,367 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addDefaultEventHandler, + checkPaddingOfContainer, + closeAccordions, + editAndVerifyWidgetName, + openAccordion, + openEditorSidebar, + randomNumber, + selectColourFromColourPicker, + verifyAndModifyParameter, + verifyBoxShadowCss, + verifyComponentValueFromInspector, + verifyContainerElements, + verifyLayout, + verifyStylesGeneralAccordion, + verifyTooltip, + verifyWidgetColorCss, +} from "Support/utils/commonWidget"; +import { + addAllInputFieldColors, + addAndVerifyAdditionalActions, + addCustomWidthOfLabel, + addValidations, + verifyAlignment, + verifyCustomWidthOfLabel, + verifyInputFieldColors, + verifyLabelStyleElements, +} from "Support/utils/editor/inputFieldUtils"; +import { + addCSA, + randomString, + verifyCSA, +} from "Support/utils/editor/textInput"; +import { commonWidgetText } from "Texts/common"; +import { textInputText } from "Texts/textInput"; + +describe("Text Input", () => { + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Textinput-App`); + cy.openApp(); + cy.dragAndDropWidget("Text Input", 500, 500); + }); + afterEach(() => { + cy.apiDeleteApp(); + }); + + it("should verify the properties of the text input widget", () => { + const data = {}; + data.widgetName = fake.widgetName; + data.tooltipText = fake.randomSentence; + data.minimumLength = randomNumber(1, 4); + data.maximumLength = randomNumber(8, 10); + data.customText = randomString(12); + + openEditorSidebar(textInputText.defaultWidgetName); + closeAccordions([ + "Data", + "Validation", + "Additional Actions", + "Devices", + "Events", + ]); + editAndVerifyWidgetName(data.widgetName, [ + "Data", + "Validation", + "Additional Actions", + "Devices", + "Events", + ]); + openAccordion("Data", [ + "Data", + "Validation", + "Additional Actions", + "Devices", + "Events", + ]); + verifyAndModifyParameter( + commonWidgetText.labelDefaultValue, + data.customText + ); + cy.forceClickOnCanvas(); + cy.get( + commonWidgetSelector.draggableWidget(data.widgetName) + ).verifyVisibleElement("have.value", data.customText); + + verifyComponentValueFromInspector(data.widgetName, data.customText); + cy.forceClickOnCanvas(); + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear(); + + data.customText = fake.randomSentence; + openEditorSidebar(data.widgetName); + openAccordion("Data", [ + "Data", + "Validation", + "Additional Actions", + "Devices", + "Events", + ]); + verifyAndModifyParameter( + commonWidgetText.labelPlaceHolder, + data.customText + ); + cy.forceClickOnCanvas(); + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)) + .invoke("attr", "placeholder") + .should("contain", data.customText); + + openEditorSidebar(data.widgetName); + openAccordion(commonWidgetText.accordionEvents, ["Validation", "Devices"]); + addDefaultEventHandler(data.customText); + cy.wait(1000); + cy.get(commonWidgetSelector.eventSelection).type("On Enter Pressed{Enter}"); + cy.wait("@events"); + + cy.clearAndType( + commonWidgetSelector.draggableWidget(data.widgetName), + `${data.customText}{Enter}` + ); + cy.verifyToastMessage(commonSelectors.toastMessage, data.customText); + cy.forceClickOnCanvas(); + + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear(); + + addValidations(data.widgetName, data); + + cy.clearAndType( + commonWidgetSelector.draggableWidget(data.widgetName), + data.customText + ); + cy.forceClickOnCanvas(); + cy.get( + commonWidgetSelector.validationFeedbackMessage(data.widgetName) + ).verifyVisibleElement("have.text", commonWidgetText.regexValidationError); + + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear(); + cy.get( + commonWidgetSelector.parameterInputField(commonWidgetText.labelRegex) + ).clearCodeMirror(); + + cy.forceClickOnCanvas(); + cy.get( + commonWidgetSelector.validationFeedbackMessage(data.widgetName) + ).verifyVisibleElement( + "have.text", + commonWidgetText.minLengthValidationError(data.minimumLength) + ); + + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear(); + cy.get( + commonWidgetSelector.parameterInputField(commonWidgetText.labelMinLength) + ).clearAndTypeOnCodeMirror("0"); + cy.forceClickOnCanvas(); + cy.clearAndType( + commonWidgetSelector.draggableWidget(data.widgetName), + data.customText.toUpperCase().replaceAll(" ", "").replaceAll(".", "") + ); + cy.get( + commonWidgetSelector.validationFeedbackMessage(data.widgetName) + ).verifyVisibleElement( + "have.text", + commonWidgetText.maxLengthValidationError(data.maximumLength) + ); + cy.forceClickOnCanvas(); + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear(); + cy.get( + commonWidgetSelector.validationFeedbackMessage(data.widgetName) + ).verifyVisibleElement("have.text", data.customText); + cy.forceClickOnCanvas(); + openEditorSidebar(data.widgetName); + cy.get( + commonWidgetSelector.accordion(commonWidgetText.accordionValidation) + ).click(); + addAndVerifyAdditionalActions(data.widgetName, data.tooltipText); + + openEditorSidebar(data.widgetName); + cy.get( + commonWidgetSelector.accordion(commonWidgetText.accordionValidation) + ).click(); + verifyLayout(data.widgetName, "Devices"); + + cy.get(commonWidgetSelector.changeLayoutToDesktopButton).click(); + cy.get( + commonWidgetSelector.parameterTogglebutton( + commonWidgetText.parameterShowOnDesktop + ) + ).click(); + + openEditorSidebar(data.widgetName); + openAccordion("Validation", [ + "Data", + "Validation", + "Additional Actions", + "Devices", + "Events", + ]); + cy.get( + commonWidgetSelector.parameterInputField(commonWidgetText.labelMinLength) + ).clearAndTypeOnCodeMirror("5"); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); + openEditorSidebar(data.widgetName); + + cy.get(commonWidgetSelector.widgetDocumentationLink).should( + "have.text", + textInputText.textInputDocumentationLink + ); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)) + .invoke("attr", "placeholder") + .should("contain", data.customText); + + cy.clearAndType( + commonWidgetSelector.draggableWidget(data.widgetName), + `${data.customText}{Enter}` + ); + cy.verifyToastMessage(commonSelectors.toastMessage, data.customText); + cy.forceClickOnCanvas(); + + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear(); + + cy.clearAndType( + commonWidgetSelector.draggableWidget(data.widgetName), + data.customText + ); + cy.forceClickOnCanvas(); + // cy.get( + // commonWidgetSelector.validationFeedbackMessage(data.widgetName) + // ).verifyVisibleElement("have.text", commonWidgetText.regexValidationError); + + cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear(); + + cy.forceClickOnCanvas(); + cy.get( + commonWidgetSelector.validationFeedbackMessage(data.widgetName) + ).verifyVisibleElement( + "have.text", + commonWidgetText.minLengthValidationError("5") + ); + cy.clearAndType( + commonWidgetSelector.draggableWidget(data.widgetName), + data.customText.toUpperCase().replaceAll(" ", "").replaceAll(".", "") + ); + cy.get( + commonWidgetSelector.validationFeedbackMessage(data.widgetName) + ).verifyVisibleElement( + "have.text", + commonWidgetText.maxLengthValidationError(data.maximumLength) + ); + cy.forceClickOnCanvas(); + verifyTooltip( + commonWidgetSelector.draggableWidget(data.widgetName), + data.tooltipText + ); + }); + + it("should verify the styles of the text input widget", () => { + const data = {}; + data.appName = `${fake.companyName}-App`; + data.colourHex = fake.randomRgbaHex; + data.boxShadowColor = fake.randomRgba; + data.boxShadowParam = fake.boxShadowParam; + data.bgColor = fake.randomRgba; + data.borderColor = fake.randomRgba; + data.textColor = fake.randomRgba; + data.errorTextColor = fake.randomRgba; + data.iconColor = fake.randomRgba; + data.labelColor = fake.randomRgba; + data.widgetName = textInputText.defaultWidgetName; + + openEditorSidebar(textInputText.defaultWidgetName); + cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click(); + addAllInputFieldColors(data); + + cy.clearAndType('[data-cy="border-radius-input"]', "20"); + cy.get('[data-cy="icon-visibility-button"]').click(); + + cy.forceClickOnCanvas(); + cy.get( + commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName) + ).should("have.css", "border-radius", "20px"); + + verifyInputFieldColors("textinput1", data); + + verifyStylesGeneralAccordion( + textInputText.defaultWidgetName, + data.boxShadowParam, + data.colourHex, + data.boxShadowColor, + 4 + ); + + openEditorSidebar(textInputText.defaultWidgetName); + cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click(); + + verifyContainerElements(); + checkPaddingOfContainer(textInputText.defaultWidgetName, 1); + cy.get('[data-cy="togglr-button-none"]').click(); + checkPaddingOfContainer(textInputText.defaultWidgetName, 0); + + verifyLabelStyleElements(); + verifyAlignment(textInputText.defaultWidgetName, "sideLeft"); + cy.get('[data-cy="togglr-button-top"]').click(); + verifyAlignment(textInputText.defaultWidgetName, "topLeft"); + cy.get('[data-cy="togglr-button-right"]').click(); + verifyAlignment(textInputText.defaultWidgetName, "topRight"); + cy.get('[data-cy="togglr-button-side"]').click(); + verifyAlignment(textInputText.defaultWidgetName, "sideRight"); + cy.get('[data-cy="togglr-button-left"]').click(); + verifyAlignment(textInputText.defaultWidgetName, "sideLeft"); + addCustomWidthOfLabel("50"); + verifyCustomWidthOfLabel(textInputText.defaultWidgetName, "35"); + selectColourFromColourPicker( + "Text", + data.labelColor, + 0, + commonWidgetSelector.colourPickerParent, + "0" + ); + verifyWidgetColorCss( + `[data-cy="label-${textInputText.defaultWidgetName}"]>label`, + "color", + data.labelColor, + true + ); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + verifyWidgetColorCss( + `[data-cy="label-${textInputText.defaultWidgetName}"]>label`, + "color", + data.labelColor, + true + ); + + verifyAlignment(textInputText.defaultWidgetName, "sideLeft"); + verifyCustomWidthOfLabel(textInputText.defaultWidgetName, "35"); + verifyInputFieldColors("textinput1", data); + + verifyBoxShadowCss( + textInputText.defaultWidgetName, + data.boxShadowColor, + data.boxShadowParam + ); + + cy.get( + commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName) + ).should("have.css", "border-radius", "20px"); + }); + + it.skip("should verify the app preview", () => { }); + + it("should verify CSA", () => { + const data = {}; + data.customText = randomString(12); + data.widgetName = textInputText.defaultWidgetName; + + addCSA(data); + verifyCSA(data); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + verifyCSA(data); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/appTitle.cy.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/appTitle.cy.js new file mode 100644 index 0000000000..51c3e00ef1 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/appTitle.cy.js @@ -0,0 +1,46 @@ +import { fake } from "Fixtures/fake"; +import { commonWidgetSelector } from "Selectors/common"; + +describe("Editor title", () => { + const data = {}; + beforeEach(() => { + data.appName = fake.companyName; + cy.apiLogin(); + cy.apiCreateApp(data.appName); + cy.visit("/"); + }); + + afterEach(() => { + cy.apiDeleteApp(); + }); + it.skip("should verify titles", () => { + cy.url().should("include", "/tooljets-workspace"); + cy.title().should("eq", "Dashboard | ToolJet"); + // cy.title().should("eq", "ToolJet"); + + cy.log(data.appName); + + cy.openApp(); + cy.url().should("include", Cypress.env("appId")); + cy.title().should("eq", `${data.appName} | ToolJet`); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + + cy.url().should("include", `/applications/${Cypress.env("appId")}`); + // cy.title().should("eq", `${data.appName} | ToolJet`); + cy.title().should("eq", `Preview - ${data.appName} | ToolJet`); + + cy.go("back"); + cy.releaseApp(); + cy.url().then((url) => { + const appId = url.split("/").filter(Boolean).pop(); + cy.log(appId); + cy.visit(`/applications/${appId}`); + }); + + cy.url().should("include", `/applications/${Cypress.env("appId")}`); + cy.title().should("eq", `${data.appName} | ToolJet`); + // cy.title().should("eq", `${data.appName}`); + }); +}); + diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/button.cy.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/button.cy.js new file mode 100644 index 0000000000..56ff165f57 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/button.cy.js @@ -0,0 +1,164 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addCSA, + verifyCSA +} from "Support/utils/editor/textInput"; +import { addMultiEventsWithAlert } from "Support/utils/events"; +import { openAndVerifyNode, openNode, verifyfunctions, verifyNodes, verifyNodeData } from "Support/utils/inspector"; + + +describe('Button Component Tests', () => { + const functions = [ + + { + "key": "setText", + "type": "Function" + }, + { + "key": "click", + "type": "Function" + }, + { + "key": "disable", + "type": "Function" + }, + { + "key": "visibility", + "type": "Function" + }, + { + "key": "setVisibility", + "type": "Function" + }, + { + "key": "setDisable", + "type": "Function" + }, + { + "key": "loading", + "type": "Function" + }, + { + "key": "setLoading", + "type": "Function" + } + ] + const exposedValues = [{ + "key": "buttonText", + "type": "String", + "value": "\"Button\"" + }, + { + "key": "isVisible", + "type": "Boolean", + "value": "true" + }, + { + "key": "isDisabled", + "type": "Boolean", + "value": "false" + }, + { + "key": "isLoading", + "type": "Boolean", + "value": "false" + }, + // { + // "key": "id", + // "type": "String", + // "value": "\"d9f805c-a8d9-4c5a-ad09-badd6c2216ba\"" + // } + ] + + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Button-App`); + cy.openApp(); + cy.dragAndDropWidget("Button", 500, 500); + cy.get('[data-cy="query-manager-toggle-button"]').click(); + }); + + it('should verify all the exposed values on inspector', () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + openNode("components"); + openAndVerifyNode("button1", exposedValues, verifyNodeData); + verifyNodes(functions, verifyNodeData); + //id is pending + + }); + + it('should verify all the events from the button', () => { + const events = [ + { event: "On hover", message: "On hover Event" }, + { event: "On Click", message: "On Click Event" }, + ]; + + addMultiEventsWithAlert(events); + const textInputSelector = '[data-cy="draggable-widget-button1"]'; + + const verifyTextInputEvents = (selector) => { + cy.get(selector).realHover() + cy.verifyToastMessage(commonSelectors.toastMessage, 'On hover Event', false); + + cy.get(selector).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Click Event', false); + }; + + verifyTextInputEvents(textInputSelector); + }); + + it.skip('should verify all the CSA from button', () => { + addMultiEventsWithAlert([ + { event: "On hover", message: "On hover Event" }, + { event: "On Click", message: "On Click Event" }, + ]); + const actions = [ + { event: "On click", action: "Set visibility", valueToggle: "{{false}}" }, //b2 + { event: "On click", action: "Visibility(deprecated)", valueToggle: "{{true}}" },//b3 + { event: "On click", action: "Disable(deprecated)", valueToggle: "{{true}}" },//b4 + { event: "On click", action: "Set disable", valueToggle: "{{false}}" },//b5 + { event: "On click", action: "Set text", value: "New Button Text" },//b6 + { event: "On click", action: "Click" },//b7 + { event: "On click", action: "Set loading", valueToggle: "{{true}}" },//b8 + { event: "On click", action: "Loading(deprecated)", valueToggle: "{{false}}" },//b9 + + ]; + addCSA("button1", actions); + let component = "button1"; + cy.get(commonWidgetSelector.draggableWidget("button2")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("not.be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button3")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button4")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).parent().should("have.attr", "disabled"); + + cy.get(commonWidgetSelector.draggableWidget("button5")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).parent().should("not.have.attr", "disabled"); + + cy.get(commonWidgetSelector.draggableWidget("button6")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.text", "New Button Text"); + + cy.get(commonWidgetSelector.draggableWidget("button7")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Click Event', false); + + cy.get(commonWidgetSelector.draggableWidget("button8")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)) + .parent() + .within(() => { + cy.get(".tj-widget-loader").should("be.visible"); + }); + + cy.get(commonWidgetSelector.draggableWidget("button9")).click(); + cy.notVisible(".tj-widget-loader"); + + }); + + // afterEach(() => { + // cy.apiDeleteApp(); + // }); + +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/checkbox.cy.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/checkbox.cy.js new file mode 100644 index 0000000000..521ac89867 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/checkbox.cy.js @@ -0,0 +1,178 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addCSA, + verifyCSA +} from "Support/utils/editor/textInput"; +import { addMultiEventsWithAlert } from "Support/utils/events"; +import { openAndVerifyNode, openNode, verifyfunctions, verifyNodes, verifyNodeData } from "Support/utils/inspector"; + + +describe('Checkbox Component Tests', () => { + const functions = [ + + { + "key": "setValue", + "type": "Function" + }, + { + "key": "toggle", + "type": "Function" + }, + { + "key": "setChecked", + "type": "Function" + }, + { + "key": "setVisibility", + "type": "Function" + }, + { + "key": "setDisable", + "type": "Function" + }, + { + "key": "setLoading", + "type": "Function" + } + ] + const exposedValues = [{ + "key": "label", + "type": "String", + "value": "\"Label\"" + }, + { + "key": "isVisible", + "type": "Boolean", + "value": "true" + }, + { + "key": "isDisabled", + "type": "Boolean", + "value": "false" + }, + { + "key": "isMandatory", + "type": "Boolean", + "value": "false" + }, + { + "key": "value", + "type": "Boolean", + "value": "false" + }, + { + "key": "isLoading", + "type": "Boolean", + "value": "false" + }, + { + "key": "isValid", + "type": "Boolean", + "value": "true" + }, + + // { + // "key": "id", + // "type": "String", + // "value": "\"d9f805c-a8d9-4c5a-ad09-badd6c2216ba\"" + // } + ] + + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Checkbox-App`); + cy.openApp(); + cy.dragAndDropWidget("Checkbox", 500, 500); + cy.get('[data-cy="query-manager-toggle-button"]').click(); + }); + + it('should verify all the exposed values on inspector', () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + openNode("components"); + openAndVerifyNode("checkbox1", exposedValues, verifyNodeData); + verifyNodes(functions, verifyNodeData); + //id is pending + + }); + + it.skip('should verify all the events from the Checkbox', () => { + const events = [ + { event: "On Change", message: "On Change Event" }, + ]; + + addMultiEventsWithAlert(events, false); + const textInputSelector = '[data-cy="draggable-widget-checkbox1"]'; + + const verifyTextInputEvents = (selector) => { + cy.forceClickOnCanvas(); + cy.get(selector).find('input').click({ force: true }); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Change Event', false); + + // cy.get(selector).click(); + // cy.verifyToastMessage(commonSelectors.toastMessage, 'On Click Event', false); + }; + + verifyTextInputEvents(textInputSelector); + }); + + it.skip('should verify all the CSA from Checkbox', () => { + const events = [ + { event: "On Change", message: "On Change Event" }, + ]; + + addMultiEventsWithAlert(events, false); + const actions = [ + { event: "On click", action: "Set visibility", valueToggle: "{{false}}" }, //b2 + { event: "On click", action: "Set visibility", valueToggle: "{{true}}" },//b3 + { event: "On click", action: "Set disable", valueToggle: "{{true}}" },//b4 + { event: "On click", action: "Set disable", valueToggle: "{{false}}" },//b5 + { event: "On click", action: "Set checked", value: "true" },//b6 + { event: "On click", action: "Toggle" },//b7 + { event: "On click", action: "Set loading", valueToggle: "{{true}}" },//b8 + { event: "On click", action: "Set loading", valueToggle: "{{false}}" },//b9 + + ]; + addCSA("checkbox1", actions); + let component = "checkbox1"; + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("not.be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button2")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button3")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.attr", "data-disabled", 'true'); + + cy.get(commonWidgetSelector.draggableWidget("button4")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.attr", "data-disabled", 'false'); + + // cy.get(commonWidgetSelector.draggableWidget("button5")).click(); + // cy.get(commonWidgetSelector.draggableWidget(component)).should("have.text", "New Button Text"); + + cy.get(commonWidgetSelector.draggableWidget("button5")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).find('input').should('be.checked') + // cy.verifyToastMessage(commonSelectors.toastMessage, 'On Change Event', false); + + cy.get(commonWidgetSelector.draggableWidget("button6")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Change Event', false); + + cy.get(commonWidgetSelector.draggableWidget("button7")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)) + .parent() + .within(() => { + cy.get(".tj-widget-loader").should("be.visible"); + }); + + cy.get(commonWidgetSelector.draggableWidget("button8")).click(); + cy.notVisible(".tj-widget-loader"); + + }); + + // afterEach(() => { + // cy.apiDeleteApp(); + // }); + +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/dropdown.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/dropdown.skip.js new file mode 100644 index 0000000000..d9ee3d8b56 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/dropdown.skip.js @@ -0,0 +1,192 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addCSA, + verifyCSA +} from "Support/utils/editor/textInput"; +import { addMultiEventsWithAlert } from "Support/utils/events"; +import { openAndVerifyNode, openNode, verifyfunctions, verifyNodes, verifyNodeData } from "Support/utils/inspector"; + + +describe('Dropdown Component Tests', () => { + const functions = [ + + { + "key": "clear", + "type": "Function" + }, + { + "key": "selectOption", + "type": "Function" + }, , + { + "key": "setVisibility", + "type": "Function" + }, + { + "key": "setDisable", + "type": "Function" + }, + { + "key": "setLoading", + "type": "Function" + }, + { + "key": "selectedOption", + "type": "Object" + }, + { + "key": "options", + "type": "Array" + }, + ] + const exposedValues = [{ + "key": "searchText", + "type": "String", + "value": "\"\"" + }, + { + "key": "label", + "type": "String", + "value": "\"Select\"" + }, + { + "key": "isVisible", + "type": "Boolean", + "value": "true" + }, + { + "key": "isDisabled", + "type": "Boolean", + "value": "false" + }, + { + "key": "isMandatory", + "type": "Boolean", + "value": "false" + }, + { + "key": "isLoading", + "type": "Boolean", + "value": "false" + }, + { + "key": "isValid", + "type": "Boolean", + "value": "true" + }, + { + "key": "value", + "type": "String", + "value": "2" + } + + // { + // "key": "id", + // "type": "String", + // "value": "\"d9f805c-a8d9-4c5a-ad09-badd6c2216ba\"" + // } + ] + + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Dropdown-App`); + cy.openApp(); + cy.dragAndDropWidget("Dropdown", 50, 50); + cy.get('[data-cy="query-manager-toggle-button"]').click(); + }); + + it('should verify all the exposed values on inspector', () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + openNode("components"); + openAndVerifyNode("dropdown1", exposedValues, verifyNodeData); + verifyNodes(functions, verifyfunctions); + //id is pending + + }); + + it('should verify all the events from the dropdown', () => { + const events = [ + { event: "On Focus", message: "On Focus Event" }, + { event: "On Blur", message: "On Blur Event" }, + { event: "On select", message: "On select Event" }, + { event: "On search text changes", message: "On search Event" } + ]; + + + addMultiEventsWithAlert(events, false); + const textInputSelector = '[data-cy="draggable-widget-dropdown1"]'; + + const verifyTextInputEvents = (selector) => { + cy.get(selector).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Focus Event', false); + + // cy.get(selector).type('r'); + // cy.verifyToastMessage(commonSelectors.toastMessage, 'On Change Event', false); + + // cy.get(selector).type('{enter}'); + // cy.verifyToastMessage(commonSelectors.toastMessage, 'On Enter Event', false); + + cy.forceClickOnCanvas(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Blur Event', false); + }; + + verifyTextInputEvents(textInputSelector); + }); + + it('should verify all the CSA from dropdown', () => { + const events = [ + { event: "On Change", message: "On Change Event" }, + ]; + + addMultiEventsWithAlert(events, false); + const actions = [ + { event: "On click", action: "Set visibility", valueToggle: "{{false}}" }, //b2 + { event: "On click", action: "Set visibility", valueToggle: "{{true}}" },//b3 + { event: "On click", action: "Set disable", valueToggle: "{{true}}" },//b4 + { event: "On click", action: "Set disable", valueToggle: "{{false}}" },//b5 + { event: "On click", action: "Set value", value: "true" },//b6 + { event: "On click", action: "Toggle" },//b7 + { event: "On click", action: "Set loading", valueToggle: "{{true}}" },//b8 + { event: "On click", action: "Set loading", valueToggle: "{{false}}" },//b9 + + ]; + addCSA("dropdown1", actions); + let component = "dropdown1"; + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("not.be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button2")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button3")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.attr", "data-disabled", 'true'); + + cy.get(commonWidgetSelector.draggableWidget("button4")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.attr", "data-disabled", 'false'); + + cy.get(commonWidgetSelector.draggableWidget("button5")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.text", "New Button Text"); + + cy.get(commonWidgetSelector.draggableWidget("button6")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Click Event', false); + + cy.get(commonWidgetSelector.draggableWidget("button7")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)) + .parent() + .within(() => { + cy.get(".tj-widget-loader").should("be.visible"); + }); + + cy.get(commonWidgetSelector.draggableWidget("button9")).click(); + cy.notVisible(".tj-widget-loader"); + + }); + + // afterEach(() => { + // cy.apiDeleteApp(); + // }); + +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/globalActions.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/globalActions.skip.js new file mode 100644 index 0000000000..a892251502 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/globalActions.skip.js @@ -0,0 +1,145 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { multipageSelector } from "Selectors/multipage"; +import { + randomString +} from "Support/utils/editor/textInput"; + +import { + addInputOnQueryField, + query, + selectQueryFromLandingPage +} from "Support/utils/queries"; + +import { deleteDownloadsFolder } from "Support/utils/common"; +import { + resizeQueryPanel +} from "Support/utils/dataSource"; +import { openNode, verifyNodeData } from "Support/utils/inspector"; +import { + addNewPage +} from "Support/utils/multipage"; + +describe("Global Actions", () => { + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-gloabalActions-App`); + cy.openApp(); + cy.viewport(1800, 1800); + cy.dragAndDropWidget("Button"); + resizeQueryPanel("80"); + deleteDownloadsFolder(); + }); + + it("should verify actions", () => { + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runjs", "JavaScript"); + addInputOnQueryField( + "runjs", + `setTimeout(() => { + actions.setVariable('var', 'test'); + actions.setPageVariable('pageVar', 'pageTest'); + }, [0]) ` + ); + query("run"); + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + verifyNodeData("variables", "Object", "1 entry "); + openNode("variables", 0); + + verifyNodeData("var", "String", `"test"`); + + openNode("page"); + openNode("variables", 1); + verifyNodeData("pageVar", "String", `"pageTest"`); + + addInputOnQueryField( + "runjs", + `setTimeout(() => { + actions.unSetVariable('var'); + actions.unsetPageVariable('pageVar'); + }, [0]) ` + ); + query("run"); + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + verifyNodeData("variables", "Object", "0 entry "); + + openNode("page"); + openNode("variables", 1); + verifyNodeData("variables", "Object", "0 entry ", 1); + + addInputOnQueryField( + "runjs", + "actions.showAlert('success', 'alert from runjs');" + ); + query("run"); + + cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs"); + cy.get(multipageSelector.sidebarPageButton).click(); + addNewPage("test_page"); + cy.url().should("contain", "/test-page"); + + addInputOnQueryField("runjs", "actions.switchPage('home');"); + query("run"); + cy.url().should("contain", "/home"); + + cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); + cy.dragAndDropWidget("Modal", 200, 300); + cy.waitForAutoSave(); + addInputOnQueryField("runjs", "actions.showModal('modal1');"); + query("run"); + cy.get('.text-widget-section > div').should("be.visible"); + + addInputOnQueryField("runjs", "actions.closeModal('modal1');"); + query("run"); + cy.wait(200); + cy.notVisible('[data-cy="modal-title"]'); + + addInputOnQueryField( + "runjs", + "actions.copyToClipboard('data from runjs');" + ); + query("run"); + + cy.window().then((win) => { + win.navigator.clipboard.readText().then((text) => { + expect(text).to.eq("data from runjs"); + }); + }); + addInputOnQueryField( + "runjs", + "actions.setLocalStorage('localStorage','data from runjs');" + ); + query("run"); + cy.wait(500) + cy.getAllLocalStorage().then((result) => { + expect(result[Cypress.config().baseUrl].localStorage).to.deep.equal( + "data from runjs" + ); + }); + + addInputOnQueryField( + "runjs", + "actions.generateFile('runjscsv', 'csv', [{ name: 'John', email: 'john@tooljet.com' }])" + ); + query("run"); + + cy.readFile("cypress/downloads/runjscsv.csv", "utf-8") + .should("contain", "name,email") + .and("contain", "John,john@tooljet.com"); + + // addInputOnQueryField( + // "runjs", + // "actions.goToApp('111234')" + // ); + // query("run"); + + addInputOnQueryField("runjs", "actions.logout()"); + query("run"); + cy.get('[data-cy="sign-in-header"]').should("be.visible"); + }); + +}); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/multiselect.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/multiselect.skip.js new file mode 100644 index 0000000000..0097c7d738 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/multiselect.skip.js @@ -0,0 +1,195 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addCSA, + verifyCSA +} from "Support/utils/editor/textInput"; +import { addMultiEventsWithAlert } from "Support/utils/events"; +import { openAndVerifyNode, openNode, verifyfunctions, verifyNodes, verifyNodeData } from "Support/utils/inspector"; + + +describe('Multiselect Component Tests', () => { + const functions = [ + + { + "key": "clear", + "type": "Function" + }, + { + "key": "selectOption", + "type": "Function" + }, { + "key": "deselectOptions", + "type": "Function" + }, + { + "key": "setVisibility", + "type": "Function" + }, + { + "key": "setDisable", + "type": "Function" + }, + { + "key": "setLoading", + "type": "Function" + }, + { + "key": "selectedOption", + "type": "Object" + }, + { + "key": "options", + "type": "Array" + }, + ] + const exposedValues = [{ + "key": "searchText", + "type": "String", + "value": "\"\"" + }, + { + "key": "label", + "type": "String", + "value": "\"Select\"" + }, + { + "key": "isVisible", + "type": "Boolean", + "value": "true" + }, + { + "key": "isDisabled", + "type": "Boolean", + "value": "false" + }, + { + "key": "isMandatory", + "type": "Boolean", + "value": "false" + }, + { + "key": "isLoading", + "type": "Boolean", + "value": "false" + }, + { + "key": "isValid", + "type": "Boolean", + "value": "true" + }, + { + "key": "value", + "type": "String", + "value": "2" + } + + // { + // "key": "id", + // "type": "String", + // "value": "\"d9f805c-a8d9-4c5a-ad09-badd6c2216ba\"" + // } + ] + + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Multiselect-App`); + cy.openApp(); + cy.dragAndDropWidget("Multiselect", 50, 50); + cy.get('[data-cy="query-manager-toggle-button"]').click(); + }); + + it('should verify all the exposed values on inspector', () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + openNode("components"); + openAndVerifyNode("multiselect1", exposedValues, verifyNodeData); + verifyNodes(functions, verifyfunctions); + //id is pending + + }); + + it('should verify all the events from the Multiselect', () => { + const events = [ + { event: "On Focus", message: "On Focus Event" }, + { event: "On Blur", message: "On Blur Event" }, + { event: "On select", message: "On select Event" }, + { event: "On search text changes", message: "On search Event" } + ]; + + + addMultiEventsWithAlert(events, false); + const textInputSelector = '[data-cy="draggable-widget-dropdown1"]'; + + const verifyTextInputEvents = (selector) => { + cy.get(selector).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Focus Event', false); + + // cy.get(selector).type('r'); + // cy.verifyToastMessage(commonSelectors.toastMessage, 'On Change Event', false); + + // cy.get(selector).type('{enter}'); + // cy.verifyToastMessage(commonSelectors.toastMessage, 'On Enter Event', false); + + cy.forceClickOnCanvas(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Blur Event', false); + }; + + verifyTextInputEvents(textInputSelector); + }); + + it.only('should verify all the CSA from multiselect', () => { + const events = [ + { event: "On Change", message: "On Change Event" }, + ]; + + addMultiEventsWithAlert(events, false); + const actions = [ + { event: "On click", action: "Set visibility", valueToggle: "{{false}}" }, //b2 + { event: "On click", action: "Set visibility", valueToggle: "{{true}}" },//b3 + { event: "On click", action: "Set disable", valueToggle: "{{true}}" },//b4 + { event: "On click", action: "Set disable", valueToggle: "{{false}}" },//b5 + { event: "On click", action: "Set value", value: "true" },//b6 + { event: "On click", action: "Toggle" },//b7 + { event: "On click", action: "Set loading", valueToggle: "{{true}}" },//b8 + { event: "On click", action: "Set loading", valueToggle: "{{false}}" },//b9 + + ]; + addCSA("multiselect1", actions); + let component = "multiselect1"; + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("not.be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button2")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button3")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.attr", "data-disabled", 'true'); + + cy.get(commonWidgetSelector.draggableWidget("button4")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.attr", "data-disabled", 'false'); + + cy.get(commonWidgetSelector.draggableWidget("button5")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.text", "New Button Text"); + + cy.get(commonWidgetSelector.draggableWidget("button6")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Click Event', false); + + cy.get(commonWidgetSelector.draggableWidget("button7")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)) + .parent() + .within(() => { + cy.get(".tj-widget-loader").should("be.visible"); + }); + + cy.get(commonWidgetSelector.draggableWidget("button9")).click(); + cy.notVisible(".tj-widget-loader"); + + }); + + // afterEach(() => { + // cy.apiDeleteApp(); + // }); + +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/numberInput.cy.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/numberInput.cy.js new file mode 100644 index 0000000000..93aff8c0cb --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/numberInput.cy.js @@ -0,0 +1,152 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addCSA, + verifyCSA +} from "Support/utils/editor/textInput"; +import { addMultiEventsWithAlert } from "Support/utils/events"; +import { openAndVerifyNode, openNode, verifyfunctions, verifyNodes, verifyNodeData } from "Support/utils/inspector"; + + +describe('Number Input Component Tests', () => { + const functions = [ + + { + "key": "setText", + "type": "Function" + }, + { + "key": "clear", + "type": "Function" + }, + { + "key": "setFocus", + "type": "Function" + }, + { + "key": "setBlur", + "type": "Function" + }, + { + "key": "setVisibility", + "type": "Function" + }, + { + "key": "setDisable", + "type": "Function" + }, + { + "key": "setLoading", + "type": "Function" + } + ] + const exposedValues = [{ + "key": "value", + "type": "Number", + "value": "0" + }, + { + "key": "isMandatory", + "type": "Boolean", + "value": "false" + }, + { + "key": "isVisible", + "type": "Boolean", + "value": "true" + }, + { + "key": "isDisabled", + "type": "Boolean", + "value": "false" + }, + { + "key": "isLoading", + "type": "Boolean", + "value": "false" + }, + { + "key": "label", + "type": "String", + "value": "\"Label\"" + }, + { + "key": "isValid", + "type": "Boolean", + "value": "true" + }, + // { + // "key": "id", + // "type": "String", + // "value": "\"d9f805c-a8d9-4c5a-ad09-badd6c2216ba\"" + // } + ] + + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Numberinput-App`); + cy.openApp(); + cy.dragAndDropWidget("Number Input", 500, 500); + cy.get('[data-cy="query-manager-toggle-button"]').click(); + }); + + it('should verify all the exposed values on inspector', () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + openNode("components"); + openAndVerifyNode("numberinput1", exposedValues, verifyNodeData); + verifyNodes(functions, verifyNodeData); + //id is pending + + }); + + it.skip('should verify all the events from the number input', () => { + const events = [ + { event: "On Focus", message: "On Focus Event" }, + { event: "On Blur", message: "On Blur Event" }, + { event: "On Change", message: "On Change Event" }, + { event: "On Enter", message: "On Enter Event" } + ]; + + addMultiEventsWithAlert(events); + const inputSelector = '[data-cy="draggable-widget-numberinput1"]'; + + const inputEvents = (selector) => { + cy.get(selector).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Focus Event', false); + + cy.get(selector).type('1'); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Change Event', false); + + cy.get(selector).type('{enter}'); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Enter Event', false); + + cy.forceClickOnCanvas(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Blur Event', false); + }; + + inputEvents(inputSelector); + }); + + it.skip('should verify all the CSA from number input', () => { + const actions = [ + { event: "On click", action: "Set visibility", valueToggle: "{{false}}" }, //b1 + { event: "On click", action: "Set visibility", valueToggle: "{{true}}" },//b2 + { event: "On click", action: "Set disable", valueToggle: "{{true}}" },//b3 + { event: "On click", action: "Set disable", valueToggle: "{{false}}" },//b4 + { event: "On click", action: "Set text", value: "1199999" },//b5 + { event: "On click", action: "Clear" },//b6 + { event: "On click", action: "Set focus" },//b7 + { event: "On click", action: "Set blur" },//b8 + { event: "On click", action: "Set loading", valueToggle: "{{true}}" },//b9 + ]; + addCSA("numberinput1", actions); + verifyCSA('numberinput1'); + }); + + // afterEach(() => { + // cy.apiDeleteApp(); + // }); + +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/passwordInput.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/passwordInput.skip.js new file mode 100644 index 0000000000..5d1b377a88 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/passwordInput.skip.js @@ -0,0 +1,152 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addCSA, + verifyCSA +} from "Support/utils/editor/textInput"; +import { addMultiEventsWithAlert } from "Support/utils/events"; +import { openAndVerifyNode, openNode, verifyfunctions, verifyNodes, verifyNodeData } from "Support/utils/inspector"; + + +describe('Password Input Component Tests', () => { + const functions = [ + + { + "key": "setText", + "type": "Function" + }, + { + "key": "clear", + "type": "Function" + }, + { + "key": "setFocus", + "type": "Function" + }, + { + "key": "setBlur", + "type": "Function" + }, + { + "key": "setVisibility", + "type": "Function" + }, + { + "key": "setDisable", + "type": "Function" + }, + { + "key": "setLoading", + "type": "Function" + } + ] + const exposedValues = [{ + "key": "value", + "type": "String", + "value": "\"\"" + }, + { + "key": "isMandatory", + "type": "Boolean", + "value": "false" + }, + { + "key": "isVisible", + "type": "Boolean", + "value": "true" + }, + { + "key": "isDisabled", + "type": "Boolean", + "value": "false" + }, + { + "key": "isLoading", + "type": "Boolean", + "value": "false" + }, + { + "key": "label", + "type": "String", + "value": "\"Label\"" + }, + { + "key": "isValid", + "type": "Boolean", + "value": "true" + }, + // { + // "key": "id", + // "type": "String", + // "value": "\"d9f805c-a8d9-4c5a-ad09-badd6c2216ba\"" + // } + ] + + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Passwordinput-App`); + cy.openApp(); + cy.dragAndDropWidget("Password Input", 50, 50); + cy.get('[data-cy="query-manager-toggle-button"]').click(); + }); + + it('should verify all the exposed values on inspector', () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + openNode("components"); + openAndVerifyNode("passwordinput1", exposedValues, verifyNodeData); + verifyNodes(functions, verifyfunctions); + //id is pending + + }); + + it.skip('should verify all the events from the password input', () => { + const events = [ + { event: "On Focus", message: "On Focus Event" }, + { event: "On Blur", message: "On Blur Event" }, + { event: "On Change", message: "On Change Event" }, + { event: "On Enter", message: "On Enter Event" } + ]; + + addMultiEventsWithAlert(events); + const inputSelector = '[data-cy="draggable-widget-passwordinput1"]'; + + const inputEvents = (selector) => { + cy.get(selector).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Focus Event', false); + + cy.get(selector).type('r'); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Change Event', false); + + cy.get(selector).type('{enter}'); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Enter Event', false); + + cy.forceClickOnCanvas(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Blur Event', false); + }; + + inputEvents(inputSelector); + }); + + it('should verify all the CSA from password input', () => { + const actions = [ + { event: "On click", action: "Set visibility", valueToggle: "{{false}}" }, //b1 + { event: "On click", action: "Set visibility", valueToggle: "{{true}}" },//b2 + { event: "On click", action: "Set disable", valueToggle: "{{true}}" },//b3 + { event: "On click", action: "Set disable", valueToggle: "{{false}}" },//b4 + { event: "On click", action: "Set text", value: "1199999" },//b5 + { event: "On click", action: "Clear" },//b6 + { event: "On click", action: "Set focus" },//b7 + { event: "On click", action: "Set blur" },//b8 + { event: "On click", action: "Set loading", valueToggle: "{{true}}" },//b9 + ]; + addCSA("passwordinput1", actions); + verifyCSA('passwordinput1'); + }); + + // afterEach(() => { + // cy.apiDeleteApp(); + // }); + +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/textInput.cy.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/textInput.cy.js new file mode 100644 index 0000000000..e571cb11ec --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/textInput.cy.js @@ -0,0 +1,160 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addCSA, + verifyCSA +} from "Support/utils/editor/textInput"; +import { addMultiEventsWithAlert } from "Support/utils/events"; +import { openAndVerifyNode, openNode, verifyfunctions, verifyNodes, verifyNodeData } from "Support/utils/inspector"; + + +describe('Text Input Component Tests', () => { + const functions = [ + + { + "key": "setText", + "type": "Function" + }, + { + "key": "clear", + "type": "Function" + }, + { + "key": "setFocus", + "type": "Function" + }, + { + "key": "setBlur", + "type": "Function" + }, + // { + // "key": "disable", + // "type": "Function" + // }, + // { + // "key": "visibility", + // "type": "Function" + // }, + { + "key": "setVisibility", + "type": "Function" + }, + { + "key": "setDisable", + "type": "Function" + }, + { + "key": "setLoading", + "type": "Function" + } + ] + const exposedValues = [{ + "key": "value", + "type": "String", + "value": "\"\"" + }, + { + "key": "isMandatory", + "type": "Boolean", + "value": "false" + }, + { + "key": "isVisible", + "type": "Boolean", + "value": "true" + }, + { + "key": "isDisabled", + "type": "Boolean", + "value": "false" + }, + { + "key": "isLoading", + "type": "Boolean", + "value": "false" + }, + { + "key": "label", + "type": "String", + "value": "\"Label\"" + }, + { + "key": "isValid", + "type": "Boolean", + "value": "true" + }, + // { + // "key": "id", + // "type": "String", + // "value": "\"d9f805c-a8d9-4c5a-ad09-badd6c2216ba\"" + // } + ] + + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Textinput-App`); + cy.openApp(); + cy.dragAndDropWidget("Text Input", 500, 500); + cy.get('[data-cy="query-manager-toggle-button"]').click(); + }); + + it('should verify all the exposed values on inspector', () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + openNode("components"); + openAndVerifyNode("textinput1", exposedValues, verifyNodeData); + verifyNodes(functions, verifyNodeData); + //id is pending + + }); + + it.skip('should verify all the events from the text input', () => { + const events = [ + { event: "On Focus", message: "On Focus Event" }, + { event: "On Blur", message: "On Blur Event" }, + { event: "On Change", message: "On Change Event" }, + { event: "On Enter", message: "On Enter Event" } + ]; + + addMultiEventsWithAlert(events); + const textInputSelector = '[data-cy="draggable-widget-textinput1"]'; + + const verifyTextInputEvents = (selector) => { + cy.get(selector).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Focus Event', false); + + cy.get(selector).type('r'); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Change Event', false); + + cy.get(selector).type('{enter}'); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Enter Event', false); + + cy.forceClickOnCanvas(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Blur Event', false); + }; + + verifyTextInputEvents(textInputSelector); + }); + + it.skip('should verify all the CSA from text input', () => { + const actions = [ + { event: "On click", action: "Set visibility", valueToggle: "{{false}}" }, //b1 + { event: "On click", action: "Visibility", valueToggle: "{{true}}" },//b2 + { event: "On click", action: "Disable", valueToggle: "{{true}}" },//b3 + { event: "On click", action: "Set disable", valueToggle: "{{false}}" },//b4 + { event: "On click", action: "Set text", value: "1199999" },//b5 + { event: "On click", action: "Clear" },//b6 + { event: "On click", action: "Set focus" },//b7 + { event: "On click", action: "Set blur" },//b8 + { event: "On click", action: "Set loading", valueToggle: "{{true}}" },//b9 + ]; + addCSA("textinput1", actions); + verifyCSA('textinput1'); + }); + + // afterEach(() => { + // cy.apiDeleteApp(); + // }); + +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/toggleSwitch.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/toggleSwitch.skip.js new file mode 100644 index 0000000000..9fc577adc1 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/componentsBasics/toggleSwitch.skip.js @@ -0,0 +1,172 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addCSA, + verifyCSA +} from "Support/utils/editor/textInput"; +import { addMultiEventsWithAlert } from "Support/utils/events"; +import { openAndVerifyNode, openNode, verifyfunctions, verifyNodes, verifyNodeData } from "Support/utils/inspector"; + + +describe('ToggleSwitch Component Tests', () => { + const functions = [ + + { + "key": "setValue", + "type": "Function" + }, + { + "key": "toggle", + "type": "Function" + }, , + { + "key": "setVisibility", + "type": "Function" + }, + { + "key": "setDisable", + "type": "Function" + }, + { + "key": "setLoading", + "type": "Function" + } + ] + const exposedValues = [{ + "key": "label", + "type": "String", + "value": "\"Label\"" + }, + { + "key": "isVisible", + "type": "Boolean", + "value": "true" + }, + { + "key": "isDisabled", + "type": "Boolean", + "value": "false" + }, + { + "key": "isMandatory", + "type": "Boolean", + "value": "false" + }, + { + "key": "value", + "type": "Boolean", + "value": "false" + }, + { + "key": "isLoading", + "type": "Boolean", + "value": "false" + }, + { + "key": "isValid", + "type": "Boolean", + "value": "true" + }, + + // { + // "key": "id", + // "type": "String", + // "value": "\"d9f805c-a8d9-4c5a-ad09-badd6c2216ba\"" + // } + ] + + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-Toggle-App`); + cy.openApp(); + cy.dragAndDropWidget("Toggle Switch", 50, 50); + cy.get('[data-cy="query-manager-toggle-button"]').click(); + }); + + it('should verify all the exposed values on inspector', () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + openNode("components"); + openAndVerifyNode("toggleswitch1", exposedValues, verifyNodeData); + verifyNodes(functions, verifyfunctions); + //id is pending + + }); + + it('should verify all the events from the Toggle', () => { + const events = [ + { event: "On Change", message: "On Change Event" }, + ]; + + addMultiEventsWithAlert(events, false); + const textInputSelector = '[data-cy="draggable-widget-toggleswitch1"]'; + + const verifyTextInputEvents = (selector) => { + cy.forceClickOnCanvas(); + cy.get(selector).find('input').click({ force: true }); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Change Event', false); + + // cy.get(selector).click(); + // cy.verifyToastMessage(commonSelectors.toastMessage, 'On Click Event', false); + }; + + verifyTextInputEvents(textInputSelector); + }); + + it.only('should verify all the CSA from toggle', () => { + const events = [ + { event: "On Change", message: "On Change Event" }, + ]; + + addMultiEventsWithAlert(events, false); + const actions = [ + { event: "On click", action: "Set visibility", valueToggle: "{{false}}" }, //b2 + { event: "On click", action: "Set visibility", valueToggle: "{{true}}" },//b3 + { event: "On click", action: "Set disable", valueToggle: "{{true}}" },//b4 + { event: "On click", action: "Set disable", valueToggle: "{{false}}" },//b5 + { event: "On click", action: "Set value", value: "true" },//b6 + { event: "On click", action: "Toggle" },//b7 + { event: "On click", action: "Set loading", valueToggle: "{{true}}" },//b8 + { event: "On click", action: "Set loading", valueToggle: "{{false}}" },//b9 + + ]; + addCSA("toggleswitch1", actions); + let component = "toggleswitch1"; + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("not.be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button2")).click(); + cy.wait(500); + cy.forceClickOnCanvas(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("be.visible"); + + cy.get(commonWidgetSelector.draggableWidget("button3")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.attr", "data-disabled", 'true'); + + cy.get(commonWidgetSelector.draggableWidget("button4")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.attr", "data-disabled", 'false'); + + cy.get(commonWidgetSelector.draggableWidget("button5")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)).should("have.text", "New Button Text"); + + cy.get(commonWidgetSelector.draggableWidget("button6")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, 'On Click Event', false); + + cy.get(commonWidgetSelector.draggableWidget("button7")).click(); + cy.get(commonWidgetSelector.draggableWidget(component)) + .parent() + .within(() => { + cy.get(".tj-widget-loader").should("be.visible"); + }); + + cy.get(commonWidgetSelector.draggableWidget("button9")).click(); + cy.notVisible(".tj-widget-loader"); + + }); + + // afterEach(() => { + // cy.apiDeleteApp(); + // }); + +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/globalSetingsHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/globalSetingsHappyPath.cy.js new file mode 100644 index 0000000000..e86f32c55e --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/globalSetingsHappyPath.cy.js @@ -0,0 +1,111 @@ +import { fake } from "Fixtures/fake"; +import { tableSelector } from "Selectors/table"; + +import { + verifyMultipleComponentValuesFromInspector, + verifyComponentValueFromInspector, + openEditorSidebar, + openAccordion, + verifyBoxShadowCss, + selectColourFromColourPicker, + verifyWidgetColorCss, +} from "Support/utils/commonWidget"; +// import { verifyNodeData, openNode, verifyNodeData } from "Support/utils/inspector"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + commonText, + commonWidgetText, + codeMirrorInputLabel, +} from "Texts/common"; + +describe("Editor- Global Settings", () => { + const data = {}; + beforeEach(() => { + data.appName = `${fake.companyName}-App`; + cy.apiLogin(); + cy.apiCreateApp(data.appName); + cy.openApp(); + }); + + it("should verify global settings", () => { + data.backgroundColor = fake.randomRgba; + cy.get("[data-cy='left-sidebar-settings-button']").click(); + + cy.get('[data-cy="label-global settings"]').verifyVisibleElement( + "have.text", + "Global settings" + ); + // cy.get( + // '[data-cy="label-hide-header-for-launched-apps"]' + // ).verifyVisibleElement("have.text", "Hide header for launched apps"); + // cy.get('[data-cy="label-maintenance-mode"]').verifyVisibleElement( + // "have.text", + // "Maintenance mode" + // ); + cy.hideTooltip(); + cy.get('[data-cy="label-max-canvas-width"]').verifyVisibleElement( + "have.text", + "Max width of canvas" + ); + cy.get('[data-cy="label-bg-canvas"]').verifyVisibleElement( + "have.text", + "Canvas background" + ); + // cy.get('[data-cy="canvas-bg-colour-picker"]').click(); + selectColourFromColourPicker( + "canvas-bg-color", + data.backgroundColor, + 0, + ".canvas-codehinter-container" + ); + + verifyWidgetColorCss( + '[data-cy="real-canvas"]', + "background-color", + data.backgroundColor, + true + ); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + cy.get(".navbar").should("be.visible"); + + cy.go("back"); + cy.wait(5000); + cy.get("[data-cy='left-sidebar-settings-button']").click(); + cy.get('[data-cy="toggle-hide-header-for-launched-apps"]').realClick(); + cy.wait(700); + cy.forceClickOnCanvas(); + cy.wait(1000); + cy.waitForAutoSave(); + cy.wait(1000); + cy.openInCurrentTab(commonWidgetSelector.previewButton); + cy.wait(5000); + cy.notVisible(".navbar"); + cy.go("back"); + cy.wait(5000); + + cy.get("[data-cy='left-sidebar-settings-button']").click(); + cy.get('[data-cy="toggle-maintenance-mode"]').realClick(); + cy.get('[data-cy="modal-confirm-button"]').click(); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // "Application is on maintenance.", + // false + // ); + cy.forceClickOnCanvas(); + cy.wait(500); + cy.waitForAutoSave(); + // Fix this after the release. 2.9.0 + cy.get('[data-cy="button-release"]').click(); + cy.get('[data-cy="yes-button"]').click(); + cy.get('[data-cy="editor-page-logo"]').click(); + cy.get('[data-cy="back-to-app-option"]').click(); + cy.get(`[data-cy="${data.appName.toLowerCase()}-card"]`) + .realHover().within(() => { + cy.get('[data-cy="launch-button"]').should('have.text', 'Maintenance') + .invoke("attr", "class") + .should("contains", "disabled-btn"); + }) + cy.apiDeleteApp(); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/inspectorHappypath.cy.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/inspectorHappypath.cy.js new file mode 100644 index 0000000000..1eec4a4e55 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/inspectorHappypath.cy.js @@ -0,0 +1,164 @@ +import { fake } from "Fixtures/fake"; +import { commonWidgetSelector } from "Selectors/common"; +import { multipageSelector } from "Selectors/multipage"; +import { addSupportCSAData, selectEvent } from "Support/utils/events"; +import { createNewVersion } from "Support/utils/exportImport"; +import { deleteComponentFromInspector, openNode, verifyNodeData, verifyNodes, openAndVerifyNode } from "Support/utils/inspector"; +import { addNewPage } from "Support/utils/multipage"; +import { navigateToCreateNewVersionModal } from "Support/utils/version"; +import testData from "Fixtures/inspectorItems.json"; + +describe("Editor- Inspector", () => { + let currentVersion = ""; + let newVersion = []; + let versionFrom = ""; + + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-inspector-App`); + cy.openApp("?key=value"); + cy.viewport(1800, 1800); + }); + + it.skip("should verify the values of inspector", () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + openAndVerifyNode("globals", testData.globalsNodes, verifyNodeData); + openAndVerifyNode("currentUser", testData.currentUserNodes, verifyNodeData); + openAndVerifyNode("theme", testData.themeNodes, verifyNodeData); + openAndVerifyNode("mode", testData.modeNodes, verifyNodeData); + openAndVerifyNode("urlparams", testData.urlparamsNode, verifyNodeData); + + if (Cypress.env("environment") !== "Community") { + const ssoUserInfoNode = '[data-cy="inspector-node-ssouserinfo"]'; + const inspectorNodeId = '[data-cy="inspector-node-id"]'; + + cy.get(`${ssoUserInfoNode} > .node-key`).should("have.text", "ssoUserInfo"); + cy.get(`${ssoUserInfoNode} > .mx-2`).should("have.text", "undefined"); + + openNode("theme"); + openNode("environment"); + verifyNodeData("name", "String", `"development"`); + cy.get(`${inspectorNodeId} > .node-key`).should("have.text", "id"); + } + + cy.apiDeleteApp(); + }); + + it.skip("should verify dynamic items", () => { + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + cy.get(multipageSelector.sidebarPageButton).click(); + addNewPage("test_page"); + + cy.dragAndDropWidget("Button", 100, 100); + selectEvent("On click", "Switch page"); + cy.get('[data-cy="switch-page-label-and-input"] > .select-search').click().type("home{enter}"); + + cy.get('[data-cy="button-add-query-param"]').click(); + cy.wait(3000); + cy.get("body").then(($body) => { + if ($body.find('[data-cy="event-query-param-key-input-field"]').length == 0) { + cy.get('[data-cy="button-add-query-param"]').click(); + } + }); + + addSupportCSAData("event-query-param-key", "key"); + addSupportCSAData("event-query-param-value", "value"); + cy.get('[data-cy="switch-page-label-and-input"] > .select-search').click().type("home{enter}"); + + cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); + cy.dragAndDropWidget("Button", 500, 300); + selectEvent("On click", "Set variable"); + addSupportCSAData("event-key", "globalVar"); + cy.wait(500) + addSupportCSAData("variable", "globalVar"); + cy.wait(500) + + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); + cy.get(commonWidgetSelector.draggableWidget("button2")).click(); + + cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); + cy.dragAndDropWidget("Button", 500, 400); + selectEvent("On click", "Set page variable"); + addSupportCSAData("key", "pageVar"); + addSupportCSAData("variable", "pageVar"); + + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); + cy.get(commonWidgetSelector.draggableWidget("button3")).click(); + + cy.get(commonWidgetSelector.sidebarinspector).click(); + openAndVerifyNode("variables", testData.variablesNodes, verifyNodeData); + + cy.forceClickOnCanvas() + cy.wait(500) + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + + // openNode("page"); + + openAndVerifyNode("page", testData.testPageNodes, verifyNodeData); + openNode("variables", 1); + verifyNodeData("pageVar", "String", `"pageVar"`); + + openAndVerifyNode("components", testData.componentsNodes, verifyNodeData); + + cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.get(commonWidgetSelector.sidebarinspector).click(); + + openAndVerifyNode("page", testData.pageNodes, verifyNodeData); + openNode("globals"); + openNode("urlparams"); + verifyNodeData("key", "String", `"value"`); + + cy.get(`[data-cy="inspector-node-key"] > .mx-1`) + .realHover() + .parent() + .find('[data-cy="copy-path-to-clipboard"]') + .realClick(); + cy.realPress("Escape"); + + cy.window().then((win) => { + win.navigator.clipboard.readText().then((text) => { + expect(text).to.eq("{{globals.urlparams.key}}"); + }); + }); + + cy.get(`[data-cy="inspector-node-key"] > .mx-1`) + .realHover() + .parent() + .find('[data-cy="copy-value-to-clicpboard"]') + .realClick(); + cy.realPress("Escape"); + cy.window().then((win) => { + win.navigator.clipboard.readText().then((text) => { + expect(text).to.eq(`"value"`); + }); + }); + + cy.dragAndDropWidget("Button", 500, 300); + cy.get(commonWidgetSelector.sidebarinspector).click(); + openNode("components"); + cy.get(`[data-cy="inspector-node-button1"] > .mx-1`).eq(0).realHover(); + cy.get('[style="height: 13px; width: 13px;"] > img').last().click(); + cy.notVisible(commonWidgetSelector.draggableWidget("button1")); + cy.apiDeleteApp(); + }); + + it.skip("should verify deletion of component from inspector", () => { + cy.dragAndDropWidget("button", 500, 500); + cy.get(commonWidgetSelector.sidebarinspector).click(); + deleteComponentFromInspector("button1"); + cy.verifyToastMessage(`[class=go3958317564]`, "Component deleted! (ctrl + Z to undo)"); + + navigateToCreateNewVersionModal((currentVersion = "v1")); + createNewVersion((newVersion = ["v2"]), (versionFrom = "v1")); + cy.notVisible(commonWidgetSelector.draggableWidget("button1")); + cy.apiDeleteApp(); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/queries/chainingOfQueries.cy.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/queries/chainingOfQueries.cy.js new file mode 100644 index 0000000000..3784b6dcec --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/queries/chainingOfQueries.cy.js @@ -0,0 +1,194 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { openEditorSidebar } from "Support/utils/commonWidget"; +import { selectEvent } from "Support/utils/events"; +import { randomString } from "Support/utils/editor/textInput"; +import { buttonText } from "Texts/button"; + +import { addSuccessNotification, chainQuery } from "Support/utils/queries"; + +import { resizeQueryPanel } from "Support/utils/dataSource"; + +describe("Chaining of queries", () => { + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-chaining-App`); + cy.openApp(); + cy.apiFetchDataSourcesId() + cy.viewport(1800, 1800); + cy.dragAndDropWidget("Button"); + resizeQueryPanel("80"); + }); + + it("should verify the chainig of runjs, restapi, runpy, tooljetdb and postgres", () => { + const data = {}; + let dsName = fake.companyName; + data.customText = randomString(12); + cy.apiAddQueryToApp( + "runjs", + { code: "return true", hasParamSupport: true, parameters: [] }, + null, + "runjs" + ); + cy.apiAddQueryToApp( + "runpy", + { code: "True", hasParamSupport: true, parameters: [] }, + null, + "runpy" + ); + cy.apiAddQueryToApp( + "restapi", + { + method: "get", + url: "https://gorest.co.in/public/v2/users", + url_params: [["", ""]], + }, + null, + "restapi" + ); + cy.apiAddQueryToApp( + "tjdb", + { + operation: "", + transformationLanguage: "javascript", + enableTransformation: false, + }, + null, + "tooljetdb" + ); + + cy.apiCreateGDS( + `http://localhost:3000/api/data-sources`, + `cypress-${dsName}-qc-postgresql`, + "postgresql", + [ + { key: "host", value: Cypress.env("pg_host") }, + { key: "port", value: 5432 }, + { key: "database", value: Cypress.env("pg_user") }, + { key: "username", value: Cypress.env("pg_user") }, + { key: "password", value: Cypress.env("pg_password"), encrypted: true }, + { key: "ssl_enabled", value: false, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + { key: "connection_type", value: "manual", encrypted: false } + ] + ); + cy.log("Data source created"); + cy.apiAddQueryToApp( + "psql", + { + mode: "sql", + transformationLanguage: "javascript", + enableTransformation: false, + query: `SELECT * FROM pg_stat_activity;`, + }, + `cypress-${dsName}-qc-postgresql`, + "postgresql" + ); + cy.reload(); + resizeQueryPanel("80"); + chainQuery("psql", "runjs"); + addSuccessNotification("psql"); + chainQuery("runjs", "runpy"); + addSuccessNotification("runjs"); + chainQuery("runpy", "restapi"); + addSuccessNotification("runpy"); + chainQuery("restapi", "tjdb"); + addSuccessNotification("restapi"); + + cy.get(`[data-cy="list-query-tjdb"]`).click(); + cy.get('[data-cy="query-tab-settings"]').click(); + selectEvent("Query Failure", "Show Alert"); + cy.get('[data-cy="debounce-input-field"]') + .click() + .type(`{selectAll}{backspace}2000{enter}`); + cy.wait(1000) + cy.get('[data-cy="query-tab-setup"]').click(); + + cy.wait(1500); + openEditorSidebar(buttonText.defaultWidgetName); + selectEvent("On Click", "Run Query", 0, `[data-cy="add-event-handler"]`, 0); + cy.wait(500); + cy.get('[data-cy="query-selection-field"]') + .click() + .find("input") + .type(`{selectAll}{backspace}psql{enter}`); + cy.forceClickOnCanvas(); + cy.wait(2500) + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "psql"); + cy.verifyToastMessage(commonSelectors.toastMessage, "runjs"); + cy.verifyToastMessage(commonSelectors.toastMessage, "runpy"); + cy.wait(500); + cy.verifyToastMessage(commonSelectors.toastMessage, "restapi"); + // cy.verifyToastMessage(commonSelectors.toastMessage, "Hello World"); + }); + + it("should verify query duplication", () => { + + const data = {}; + let dsName = fake.companyName; + data.customText = randomString(12); + cy.apiAddQueryToApp( + "runjs", + { code: "return true", hasParamSupport: true, parameters: [] }, + null, + "runjs" + ); + cy.apiAddQueryToApp( + "runpy", + { code: "True", hasParamSupport: true, parameters: [] }, + null, + "runpy" + ); + + cy.reload(); + resizeQueryPanel("80"); + addSuccessNotification("runpy"); + chainQuery("runjs", "runpy"); + addSuccessNotification("runjs"); + + cy.wait(1500); + openEditorSidebar(buttonText.defaultWidgetName); + selectEvent("On Click", "Run Query", 0, `[data-cy="add-event-handler"]`, 0); + cy.wait(500); + cy.get('[data-cy="query-selection-field"]') + .click() + .find("input") + .type(`{selectAll}{backspace}runjs{enter}`); + cy.forceClickOnCanvas(); + cy.wait(2500) + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "runjs"); + cy.verifyToastMessage(commonSelectors.toastMessage, "runpy"); + cy.get('[data-cy="list-query-runjs"]') + .trigger("mouseover") + .parent() + .parent() + .find('[data-cy="copy-icon"]') + .eq(0) + .invoke("show") + .click({ force: true }); + cy.get('[data-cy="list-query-runjs_copy"]').verifyVisibleElement( + "have.text", + "runjs_copy " + ); + + cy.get('[data-cy="query-tab-settings"]').click(); + cy.get('[data-cy="notification-on-success-toggle-switch"]').should( + "have.value", + "on" + ); + cy.get('[data-cy="success-message-input-field"]').should( + "contain.text", + "runjs" + ); + cy.get(".query-definition-pane-wrapper").within(() => { + cy.get('[data-cy="event-handler-card"]').eq(0).click(); + cy.wait(500); + }); + cy.get( + `[data-cy="action-selection"] > .select-search > .react-select__control > .react-select__value-container > ` + ).should("have.text", "Run query"); + cy.get('[data-cy="query-selection-field"]').should("have.text", "runpy"); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/queries/runjsHappyPath.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/queries/runjsHappyPath.skip.js new file mode 100644 index 0000000000..1edc529c84 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/queries/runjsHappyPath.skip.js @@ -0,0 +1,190 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + openEditorSidebar +} from "Support/utils/commonWidget"; +import { + randomString +} from "Support/utils/editor/textInput"; +import { + selectEvent +} from "Support/utils/events"; + +import { + addInputOnQueryField, + changeQueryToggles, + query, + renameQueryFromEditor, + selectQueryFromLandingPage +} from "Support/utils/queries"; + +import { deleteDownloadsFolder } from "Support/utils/common"; +import { + resizeQueryPanel, + verifypreview +} from "Support/utils/dataSource"; +import { openNode, verifyNodeData } from "Support/utils/inspector"; + +describe("RunJS", () => { + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-runjs-App`); + cy.openApp(); + cy.viewport(1800, 1800); + cy.dragAndDropWidget("Button"); + resizeQueryPanel("80"); + deleteDownloadsFolder(); + }); + + it("should verify basic runjs", () => { + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runjs", "JavaScript"); + addInputOnQueryField("runjs", "return true"); + query("run"); + verifypreview("raw", "true"); + query("run"); + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + openNode("queries"); + openNode("runjs1"); + verifyNodeData("data", "Boolean", "true"); + verifyNodeData("rawData", "Boolean", "true"); + cy.apiDeleteApp(); + }); + + it.skip("should verify global and page data", () => { + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runjs", "JavaScript"); + addInputOnQueryField("runjs", "return [page.handle,page.name]"); + query("run"); + verifypreview("raw", `["home","Home"]`); + + addInputOnQueryField("runjs", "return globals.theme"); + query("run"); + verifypreview("raw", `{"name":"light"}`); + + // addInputOnQueryField("runjs", "return globals.currentUser"); + // query("preview"); + // verifypreview( + // "raw", + // `{"email":"dev@tooljet.io","firstName":"The","lastName":"Developer","groups":["all_users","admin"]}` + // ); + addInputOnQueryField("runjs", "return globals.currentUser.email"); + query("preview"); + verifypreview("raw", `dev@tooljet.io`); + addInputOnQueryField("runjs", "return globals.currentUser.email"); + query("preview"); + verifypreview("raw", `dev@tooljet.io`); + addInputOnQueryField("runjs", "return globals.currentUser.firstName"); + query("preview"); + verifypreview("raw", `The`); + addInputOnQueryField("runjs", "return globals.currentUser.lastName"); + query("preview"); + verifypreview("raw", `Developer`); + addInputOnQueryField("runjs", "return globals.currentUser.groups"); + query("preview"); + verifypreview("raw", `["all_users","admin"]`); + if (Cypress.env("environment") != "Community") { + addInputOnQueryField("runjs", "return globals.environment.name"); + query("preview"); + verifypreview("raw", `development`); + + addInputOnQueryField( + "runjs", + "return globals.currentUser.ssoUserInfo == undefined" + ); + query("preview"); + verifypreview("raw", `true`); + } + + addInputOnQueryField("runjs", "return globals.mode"); + query("preview"); + verifypreview("raw", `{"value":"edit"}`); + + addInputOnQueryField("runjs", "return constants"); + query("preview"); + verifypreview("raw", `{}`); + cy.apiDeleteApp(); + }); + + it("should verify action by button", () => { + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runjs", "JavaScript"); + addInputOnQueryField( + "runjs", + "actions.showAlert('success', 'alert from runjs');" + ); + query("run"); + openEditorSidebar("button1"); + selectEvent("On Click", "Run query"); + cy.get('[data-cy="query-selection-field"]').type("runjs1{enter}"); + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + + cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs"); + renameQueryFromEditor("newrunjs"); + cy.waitForAutoSave(); + cy.get('[data-cy="event-handler"]').click(); + + cy.get('[data-cy="query-selection-field"]').should("have.text", "newrunjs"); + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs"); + cy.apiDeleteApp(); + }); + + it("should verify runjs toggle options", () => { + cy.intercept("PATCH", "api/data_queries/**").as("editQuery"); + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runjs", "JavaScript"); + addInputOnQueryField( + "runjs", + "actions.showAlert('success', 'alert from runjs');" + ); + cy.get('[data-cy="query-tab-settings"]').click(); + changeQueryToggles("run-on-app-load"); + // cy.wait(`@editQuery`); + cy.waitForAutoSave(); + cy.waitForAutoSave(); + cy.reload(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + "alert from runjs", + false + ); + cy.get('[data-cy="query-tab-settings"]').click(); + changeQueryToggles("confirmation-before-run"); + // cy.wait(`@editQuery`); + cy.waitForAutoSave(); + cy.reload(); + cy.get('[data-cy="modal-message"]').verifyVisibleElement( + "have.text", + "Do you want to run this query - runjs1?" + ); + cy.get('[data-cy="modal-confirm-button"]').realClick(); + cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs"); + + resizeQueryPanel("80"); + cy.get('[data-cy="query-tab-settings"]').click(); + changeQueryToggles("notification-on-success"); + cy.get('[data-cy="success-message-input-field"]').clearAndTypeOnCodeMirror( + "Success alert" + ); + cy.get('[data-cy="query-tab-setup"]').click(); + cy.get('[data-cy="runjs-input-field"]').realClick(); + cy.wait(1000); + cy.waitForAutoSave(); + cy.reload(); + cy.get('[data-cy="modal-confirm-button"]').realClick(); + cy.verifyToastMessage(commonSelectors.toastMessage, "Success alert"); + cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs"); + cy.apiDeleteApp(); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/queries/runpyHappyPath.skip.js b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/queries/runpyHappyPath.skip.js new file mode 100644 index 0000000000..2018367552 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/queries/runpyHappyPath.skip.js @@ -0,0 +1,319 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { multipageSelector } from "Selectors/multipage"; +import { + openEditorSidebar +} from "Support/utils/commonWidget"; +import { + randomString +} from "Support/utils/editor/textInput"; +import { + selectEvent +} from "Support/utils/events"; + +import { + addInputOnQueryField, + changeQueryToggles, + query, + renameQueryFromEditor, + selectQueryFromLandingPage, + waitForQueryAction, +} from "Support/utils/queries"; + +import { + resizeQueryPanel, + verifypreview +} from "Support/utils/dataSource"; +import { openNode, verifyNodeData } from "Support/utils/inspector"; +import { + addNewPage +} from "Support/utils/multipage"; + +describe("runpy", () => { + beforeEach(() => { + cy.apiLogin(); + cy.apiCreateApp(`${fake.companyName}-runpy-App`); + cy.openApp(); + cy.viewport(1800, 1800); + cy.dragAndDropWidget("Button"); + resizeQueryPanel("80"); + cy.intercept("PATCH", "api/data_queries/**").as("editQuery"); + }); + + it("should verify basic runpy", () => { + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runpy", "Python"); + addInputOnQueryField("runpy", "True"); + cy.waitForAutoSave(); + query("preview"); + verifypreview("raw", "true"); + query("run"); + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + openNode("queries"); + openNode("runpy1"); + verifyNodeData("data", "Boolean", "true"); + verifyNodeData("rawData", "Boolean", "true"); + cy.apiDeleteApp(); + }); + + it.skip("should verify actions", () => { + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runpy", "Python"); + addInputOnQueryField( + "runpy", + `actions.setVariable('var', 'test') +actions.setPageVariable('pageVar', 'pageTest')` + ); + cy.waitForAutoSave(); + query("run"); + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + verifyNodeData("variables", "Object", "1 entry "); + openNode("variables", 0); + + verifyNodeData("var", "String", `"test"`); + + openNode("page"); + openNode("variables", 1); + verifyNodeData("pageVar", "String", `"pageTest"`); + + addInputOnQueryField( + "runpy", + `actions.unSetVariable('var') +actions.unsetPageVariable('pageVar')` + ); + query("run"); + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(".tooltip-inner").invoke("hide"); + verifyNodeData("variables", "Object", "0 entry "); + + openNode("page"); + openNode("variables", 1); + verifyNodeData("variables", "Object", "0 entry ", 1); + + addInputOnQueryField( + "runpy", + "actions.showAlert('success', 'alert from runpy')" + ); + query("run"); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + "alert from runpy", + false + ); + cy.get(multipageSelector.sidebarPageButton).click(); + addNewPage("test_page"); + cy.url().should("contain", "/test-page"); + + addInputOnQueryField("runpy", "actions.switchPage('home')"); + query("run"); + cy.url().should("contain", "/home"); + + cy.get('[data-cy="real-canvas"]').click("topRight", { force: true }); + cy.dragAndDropWidget("Modal", 300, 300); + cy.waitForAutoSave(); + addInputOnQueryField("runpy", "actions.showModal('modal1')"); + query("run"); + cy.get('.text-widget-section > div').should("be.visible"); + cy.get('[data-cy="runpy-input-field"]').click({ force: true }); + + addInputOnQueryField("runpy", "actions.closeModal('modal1')"); + // cy.wait(`@editQuery`); + cy.waitForAutoSave(); + query("run"); + waitForQueryAction("run"); + cy.notVisible('[data-cy="modal-title"]'); + + addInputOnQueryField("runpy", "actions.copyToClipboard('data from runpy')"); + // cy.wait(`@editQuery`); + cy.waitForAutoSave(); + query("run"); + waitForQueryAction("run"); + cy.window().then((win) => { + win.navigator.clipboard.readText().then((text) => { + expect(text).to.eq("data from runpy"); + }); + }); + addInputOnQueryField( + "runpy", + "actions.setLocalStorage('localStorage','data from runpy')" + ); + // cy.wait(`@editQuery`); + cy.waitForAutoSave(); + query("run"); + waitForQueryAction("run"); + + cy.getAllLocalStorage().then((result) => { + expect(result[Cypress.config().baseUrl].localStorage).to.deep.equal( + "data from runpy" + ); + }); + + // addInputOnQueryField( //Need fix asap + // "runpy", + // "actions.generateFile('runpycsv', 'csv', [{ 'name': 'John', 'email': 'john@tooljet.com' }])" + // ); + // query("run"); + + // cy.wait(3000); + + // cy.readFile("cypress/downloads/runpycsv.csv", "utf-8") + // .should("contain", "name,email") + // .and("contain", "John,john@tooljet.com"); + + // addInputOnQueryField( + // "runpy", + // "actions.goToApp('111234')" + // ); + // query("run"); + + addInputOnQueryField("runpy", "actions.logout()"); + // cy.wait(`@editQuery`); + cy.wait(200); + cy.waitForAutoSave(); + query("run"); + + cy.get('[data-cy="sign-in-header"]', { timeout: 20000 }).should( + "be.visible" + ); + }); + + it.skip("should verify global and page data", () => { + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runpy", "Python"); + addInputOnQueryField("runpy", "tj_globals.theme"); + cy.waitForAutoSave(); + query("preview"); + verifypreview("raw", `{"name":"light"}`); + + addInputOnQueryField("runpy", "tj_globals.currentUser.email"); + query("preview"); + verifypreview("raw", `dev@tooljet.io`); + addInputOnQueryField("runpy", "tj_globals.currentUser.email"); + query("preview"); + verifypreview("raw", `dev@tooljet.io`); + addInputOnQueryField("runpy", "tj_globals.currentUser.firstName"); + query("preview"); + verifypreview("raw", `The`); + addInputOnQueryField("runpy", "tj_globals.currentUser.lastName"); + query("preview"); + verifypreview("raw", `Developer`); + addInputOnQueryField("runpy", "tj_globals.currentUser.groups"); + query("preview"); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "Query (runpy1) completed." + ); + waitForQueryAction("preview"); + verifypreview("raw", `["all_users","admin"]`); + if (Cypress.env("environment") != "Community") { + addInputOnQueryField("runpy", "tj_globals.environment.name"); + query("preview"); + verifypreview("raw", `development`); + + // addInputOnQueryField( //WIP + // "runpy", + // "(tj_globals.currentUser.ssoUserInfo == undefined)" + // ); + // query("preview"); + // verifypreview("raw", `true`); + } + + addInputOnQueryField("runpy", "tj_globals.mode.value"); + query("preview"); + verifypreview("raw", `edit`); + addInputOnQueryField("runpy", "constants"); + query("preview"); + waitForQueryAction("preview"); + verifypreview("raw", `{}`); + cy.apiDeleteApp(); + }); + + it("should verify action by button", () => { + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runpy", "Python"); + addInputOnQueryField( + "runpy", + "actions.showAlert('success', 'alert from runpy');" + ); + cy.waitForAutoSave(); + query("run"); + + openEditorSidebar("button1"); + selectEvent("On Click", "Run query"); + cy.get('[data-cy="query-selection-field"]').type("runpy1{enter}"); + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runpy"); + renameQueryFromEditor("newrunpy"); + cy.get('[data-cy="event-handler"]').click(); + + cy.get('[data-cy="query-selection-field"]').should("have.text", "newrunpy"); + cy.get(commonWidgetSelector.draggableWidget("button1")).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runpy"); + cy.apiDeleteApp(); + }); + + it("should verify runpy toggle options", () => { + const data = {}; + data.customText = randomString(12); + + selectQueryFromLandingPage("runpy", "Python"); + cy.waitForAutoSave(); + addInputOnQueryField( + "runpy", + "actions.showAlert('success', 'alert from runpy');" + ); + cy.get('[data-cy="query-tab-settings"]').click(); + // cy.wait("@editQuery"); + cy.wait(200); + cy.waitForAutoSave(); + + changeQueryToggles("run-on-app-load"); + // cy.wait("@editQuery"); + cy.waitForAutoSave(); + cy.reload(); + cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runpy"); + + cy.get('[data-cy="query-tab-settings"]').click(); + changeQueryToggles("confirmation-before-run"); + // cy.wait("@editQuery"); + cy.wait(200); + cy.waitForAutoSave(); + cy.reload(); + cy.get('[data-cy="modal-message"]').verifyVisibleElement( + "have.text", + "Do you want to run this query - runpy1?" + ); + cy.get('[data-cy="modal-confirm-button"]').realClick(); + cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runpy"); + + cy.get('[data-cy="query-tab-settings"]').click(); + changeQueryToggles("notification-on-success"); + cy.get('[data-cy="success-message-input-field"]').clearAndTypeOnCodeMirror( + "Success alert" + ); + cy.forceClickOnCanvas(); + // cy.wait("@editQuery"); + cy.wait(200); + cy.waitForAutoSave(); + cy.reload(); + cy.get('[data-cy="modal-confirm-button"]', { timeout: 10000 }).realClick(); + cy.verifyToastMessage(commonSelectors.toastMessage, "Success alert", false); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "alert from runpy", + false + ); + cy.apiDeleteApp(); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/addAllPluginsToApp.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/addAllPluginsToApp.cy.js new file mode 100644 index 0000000000..51b65aeb60 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/addAllPluginsToApp.cy.js @@ -0,0 +1,221 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { commonSelectors } from "Selectors/common"; + +import { selectAndAddDataSource } from "Support/utils/postgreSql"; + +import { closeDSModal } from "Support/utils/dataSource"; + +const data = {}; +data.dsNamefake = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); +data.dsNamefake1 = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); +const cyParamName = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "-"); +data.workspaceName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); +data.workspaceSlug = fake.lastName.toLowerCase().replace(/\s+/g, "-"); + +const dataSources = [ + "BigQuery", + "ClickHouse", + "CosmosDB", + "CouchDB", + "Databricks", + "DynamoDB", + "Elasticsearch", + "Firestore", + "InfluxDB", + "MariaDB", + "MongoDB", + "SQL Server", + "MySQL", + "Oracle DB", + "PostgreSQL", + "Redis", + "RethinkDB", + "SAP HANA", + "Snowflake", + "TypeSense", + "Airtable", + "Amazon SES", + "Appwrite", + "Amazon Athena", + "Baserow", + // "Google Sheets", need to remove + "GraphQL", + // "gRPC", need to remove + "Mailgun", + "n8n", + "Notion", + "OpenAPI", + "REST API", + "SendGrid", + // "Slack", need to remove + "SMTP", + "Stripe", + "Twilio", + "Woocommerce", + //"Zendesk", need to remove + "Azure Blob Storage", + "GCS", + "Minio", + "AWS S3", +]; + +describe("Add all Data sources to app", () => { + beforeEach(() => { + cy.apiLogin(); + }); + + it.skip("Should verify global data source page", () => { + cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); + cy.visit(`${data.workspaceSlug}`); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + }); + + it.skip("Should add all data sources in data source page", () => { + cy.visit(`${data.workspaceSlug}`); + + dataSources.forEach((dsName) => { + cy.get(commonSelectors.globalDataSourceIcon).click(); + selectAndAddDataSource("databases", dsName, dsName); // Using the correct fake name + + // Test connection + // cy.get(postgreSqlSelector.buttonTestConnection).click(); + // cy.get(postgreSqlSelector.textConnectionVerified, { + // timeout: 10000, + // }).should("have.text", postgreSqlText.labelConnectionVerified); + + // // Save data source + // cy.get(postgreSqlSelector.buttonSave).click(); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // `Data Source ${dsName} saved.` + // ); + }); + }); + + it.skip("Should add all data sources in the app", () => { + cy.visit(`${data.workspaceSlug}`); + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsNamefake); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.wrap(dataSources).each((dsName) => { + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type( + `cypress-${cyParamName(dsName)}-${cyParamName(dsName)}` + ); + cy.wait(500); + + cy.contains( + `[id*="react-select-"]`, + `cypress-${cyParamName(dsName)}-${cyParamName(dsName)}` + ) + .should("be.visible") + .click(); + + cy.wait(500); + }); + }); + + it.skip("Should install all makretplace plugins and add them into the app", () => { + cy.visit(`${data.workspaceSlug}`); + const dataSourcesMarketplace = [ + "Plivo", + "GitHub", + "OpenAI", + "AWS Textract", + "HarperDB", + "AWS Redshift", + "PocketBase", + "AWS Lambda", + "Supabase", + "Engagespot", + // "Salesforce", need to remove + "Presto", + "Jira", + // "Sharepoint", need to remove + "Portkey", + "Pinecone", + "Hugging Face", + "Cohere", + "Gemini", + "Mistral", + "Anthropic", + "Qdrant", + "Weaviate DB", + ]; + + cy.get(commonSelectors.globalDataSourceIcon).click(); + + cy.window().then((win) => { + cy.stub(win, "open").callsFake((url) => { + win.location.href = url; + }); + }); + + cy.get('[data-cy="data-source-add-plugin"]').click(); + + cy.get(".marketplace-install").each(($el) => { + cy.wrap($el).click(); + cy.wait(500); + cy.get(commonSelectors.toastMessage).should("include.text", "installed"); + }); + cy.wait(1000); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.get(commonSelectors.pageSectionHeader).should( + "have.text", + "Data sources" + ); + + cy.wrap(dataSourcesMarketplace).each((dsName) => { + cy.get(commonSelectors.globalDataSourceIcon).click(); + selectAndAddDataSource("databases", dsName, dsName); + cy.wait(1000); + }); + + cy.get(commonSelectors.dashboardIcon).should("be.visible").click(); + cy.get(commonSelectors.appCreateButton).should("be.visible").click(); + cy.get(commonSelectors.appNameInput) + .should("be.visible") + .click() + .type(data.dsNamefake1); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.wrap(dataSourcesMarketplace).each((dsName) => { + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type( + `cypress-${cyParamName(dsName)}-${cyParamName(dsName)}` + ); + cy.wait(1000); + + cy.contains( + `[id*="react-select-"]`, + `cypress-${cyParamName(dsName)}-${cyParamName(dsName)}` + ) + .should("be.visible") + .click(); + + cy.wait(1000); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/airTableHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/airTableHappyPath.cy.js new file mode 100644 index 0000000000..9e383b041d --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/airTableHappyPath.cy.js @@ -0,0 +1,349 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector, airTableSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { airtableText } from "Texts/airTable"; +import { commonSelectors } from "Selectors/common"; +import { closeDSModal } from "Support/utils/dataSource"; +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; +data.queryName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); +const airTable_apiKey = Cypress.env("airTable_apikey"); +const airTable_baseId = Cypress.env("airtabelbaseId"); +const airTable_tableName = Cypress.env("airtable_tableName"); +const airTable_recordID = Cypress.env("airtable_recordId"); + +describe("Data source Airtable", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it("Should verify elements on connection AirTable form", () => { + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dsName}-airtable`, + "airtable", + [ + { + key: "personal_access_token", + value: `${Cypress.env("airTable_apikey")}`, + encrypted: true, + }, + ] + ); + cy.reload(); + cy.get( + dataSourceSelector.dataSourceNameButton(`cypress-${data.dsName}-airtable`) + ) + .should("be.visible") + .click(); + cy.get( + dataSourceSelector.labelFieldName(airtableText.ApiKey) + ).verifyVisibleElement("have.text", `${airtableText.ApiKey}*`); + cy.get(postgreSqlSelector.labelEncryptedText).verifyVisibleElement( + "have.text", + postgreSqlText.labelEncrypted + ); + cy.get(dataSourceSelector.button(postgreSqlText.editButtonText)).should( + "be.visible" + ); + cy.get(dataSourceSelector.button(postgreSqlText.editButtonText)).click(); + cy.verifyRequiredFieldValidation(airtableText.ApiKey, "rgb(226, 99, 103)"); + cy.get(dataSourceSelector.textField(airtableText.ApiKey)).should( + "be.visible" + ); + cy.get(postgreSqlSelector.labelIpWhitelist).verifyVisibleElement( + "have.text", + postgreSqlText.whiteListIpText + ); + cy.get(postgreSqlSelector.buttonCopyIp).verifyVisibleElement( + "have.text", + postgreSqlText.textCopy + ); + + cy.get(postgreSqlSelector.linkReadDocumentation).verifyVisibleElement( + "have.text", + postgreSqlText.readDocumentation + ); + cy.get(postgreSqlSelector.buttonTestConnection) + .verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextTestConnection + ) + .click(); + cy.get(postgreSqlSelector.connectionFailedText).verifyVisibleElement( + "have.text", + postgreSqlText.couldNotConnect + ); + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .and("be.disabled"); + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( + "have.text", + airtableText.invalidAccessToken + ); + + cy.apiDeleteGDS(`cypress-${data.dsName}-airtable`); + }); + + it("Should verify the functionality of AirTable connection form.", () => { + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dsName}-airtable`, + "airtable", + [ + { + key: "personal_access_token", + value: "Invalid access token", + encrypted: true, + }, + ] + ); + cy.get( + dataSourceSelector.dataSourceNameButton(`cypress-${data.dsName}-airtable`) + ) + .should("be.visible") + .click(); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(dataSourceSelector.connectionFailedText, { + timeout: 10000, + }).should("have.text", postgreSqlText.couldNotConnect); + + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( + "have.text", + airtableText.invalidAccessToken + ); + cy.reload(); + cy.apiUpdateGDS({ + name: `cypress-${data.dsName}-airtable`, + options: [ + { + key: "personal_access_token", + value: `${Cypress.env("airTable_apikey")}`, + encrypted: true, + }, + ], + }); + cy.get( + dataSourceSelector.dataSourceNameButton(`cypress-${data.dsName}-airtable`) + ) + .should("be.visible") + .click(); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.apiDeleteGDS(`cypress-${data.dsName}-airtable`); + }); + + it("Should able to run the query with valid conection", () => { + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dsName}-airtable`, + "airtable", + [ + { + key: "personal_access_token", + value: `${airTable_apiKey}`, + encrypted: true, + }, + ] + ); + cy.apiCreateApp(`${data.dsName}-airtable-app`); + cy.openApp(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + // Verfiy List Recored operation + + cy.get(airTableSelector.operationSelectDropdown) + .click() + .type("List records{enter}"); + + cy.get(airTableSelector.baseIdInputField).clearAndTypeOnCodeMirror( + airTable_baseId + ); + + cy.get(airTableSelector.tableNameInputField).clearAndTypeOnCodeMirror( + airTable_tableName + ); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // `Query (${data.dsName}) completed.` + // ); + + // Verfiy Retrieve record operation + + cy.get(airTableSelector.operationSelectDropdown) + .click() + .type("Retrieve record{enter}"); + + cy.get(airTableSelector.baseIdInputField).clearAndTypeOnCodeMirror( + airTable_baseId + ); + cy.get(airTableSelector.tableNameInputField).clearAndTypeOnCodeMirror( + airTable_tableName + ); + + cy.get(airTableSelector.recordIdInputField).clearAndTypeOnCodeMirror( + airTable_recordID + ); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // `Query (${data.dsName}) completed.` + // ); + + // Verfiy Create record operation + + cy.get(airTableSelector.operationSelectDropdown) + .click() + .type("Create record{enter}"); + + cy.get(airTableSelector.baseIdInputField).clearAndTypeOnCodeMirror( + airTable_baseId + ); + + cy.get(airTableSelector.tableNameInputField).clearAndTypeOnCodeMirror( + airTable_tableName + ); + + cy.get(airTableSelector.bodyInputField) + .realClick() + .realType('[{"', { force: true, delay: 0 }) + .realType("fields", { force: true, delay: 0 }) + .realType('": {}', { force: true, delay: 0 }); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // `Query (${data.dsName}) completed.` + // ); + + // Verfiy Update record operation + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.queryName); + + cy.get(airTableSelector.operationSelectDropdown) + .click() + .type("Update record{enter}"); + + cy.get(airTableSelector.baseIdInputField).clearAndTypeOnCodeMirror( + airTable_baseId + ); + cy.get(airTableSelector.tableNameInputField).clearAndTypeOnCodeMirror( + airTable_tableName + ); + + cy.get(airTableSelector.recordIdInputField).clearAndTypeOnCodeMirror( + airTable_recordID + ); + + cy.get(airTableSelector.bodyInputField) + .realClick() + .realType("{", { force: true, delay: 0 }) + .realType("{enter}", { force: true, delay: 0 }) + .realType('"Phone Number": "555_98"', { force: true, delay: 0 }); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // `Query (${data.queryName}) completed.` + // ); + + // Verify Delete record operation + + cy.get(airTableSelector.operationSelectDropdown) + .click() + .type("Delete record{enter}"); + + const recordId = Cypress._.uniqueId("recDummy_"); + + cy.request({ + method: "POST", + url: `https://api.airtable.com/v0/${airTable_baseId}/${airTable_tableName}`, + headers: { + Authorization: `Bearer ${Cypress.env("airTable_apikey")}`, + "Content-Type": "application/json", + }, + body: { + records: [ + { + fields: { + "Employee ID": "E005", + "First Name": "test", + "Last Name": "abc", + Email: "doe@example.com", + "Phone Number": "555-12", + }, + }, + ], + }, + }).then((createResponse) => { + const newRecordId = createResponse.body.records[0].id; + + cy.get(airTableSelector.operationSelectDropdown) + .click() + .type("Delete record{enter}"); + + cy.get(airTableSelector.baseIdInputField).clearAndTypeOnCodeMirror( + airTable_baseId + ); + cy.get(airTableSelector.tableNameInputField).clearAndTypeOnCodeMirror( + airTable_tableName + ); + + cy.get(airTableSelector.recordIdInputField).clearAndTypeOnCodeMirror( + newRecordId + ); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // `Query (${data.queryName}) completed.` + // ); + + cy.apiDeleteApp(`${data.dsName}-airtable-app`); + cy.apiDeleteGDS(`cypress-${data.dsName}-airtable`); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonAthenaHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonAthenaHappyPath.cy.skip.js new file mode 100644 index 0000000000..f207f62058 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonAthenaHappyPath.cy.skip.js @@ -0,0 +1,208 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { pluginSelectors } from "Selectors/plugins"; +import { postgreSqlText } from "Texts/postgreSql"; +import { amazonSesText } from "Texts/amazonSes"; +import { amazonAthenaText } from "Texts/amazonAthena"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; + +describe("Data source amazon athena", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it.skip("Should verify elements on amazon athena connection form", () => { + const Accesskey = Cypress.env("amazonathena_accessKey"); + const Secretkey = Cypress.env("amazonathena_secretKey"); + const DbName = Cypress.env("amazonathena_DbName"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + selectAndAddDataSource( + "databases", + amazonAthenaText.AmazonAthena, + data.dsName + ); + + cy.get(pluginSelectors.amazonAthenaDbName).click().type(DbName); + + cy.get(pluginSelectors.amazonsesAccesKey).click().type(" "); + + fillDataSourceTextField( + amazonSesText.labelSecretKey, + amazonAthenaText.placeholderSecretKey, + Secretkey + ); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + cy.get(postgreSqlSelector.buttonTestConnection) + .verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextTestConnection + ) + .click(); + cy.get(postgreSqlSelector.connectionFailedText).verifyVisibleElement( + "have.text", + postgreSqlText.couldNotConnect + ); + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-Amazon-Athena`); + }); + + it.skip("Should verify the functionality of amazon athena connection form.", () => { + const Accesskey = Cypress.env("amazonathena_accessKey"); + const Secretkey = Cypress.env("amazonathena_secretKey"); + const DbName = Cypress.env("amazonathena_DbName"); + selectAndAddDataSource( + "databases", + amazonAthenaText.AmazonAthena, + data.dsName + ); + + cy.get(pluginSelectors.amazonAthenaDbName).click().type(DbName); + + cy.get(pluginSelectors.amazonsesAccesKey).click().type(Accesskey); + + fillDataSourceTextField( + amazonSesText.labelSecretKey, + amazonAthenaText.placeholderSecretKey, + Secretkey + ); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + cy.get(postgreSqlSelector.buttonSave).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-amazon-Athena`); + }); + + it.skip("Should able to run the query with valid conection", () => { + const Accesskey = Cypress.env("amazonathena_accessKey"); + const Secretkey = Cypress.env("amazonathena_secretKey"); + const DbName = Cypress.env("amazonathena_DbName"); + selectAndAddDataSource( + "databases", + amazonAthenaText.AmazonAthena, + data.dsName + ); + + cy.get(pluginSelectors.amazonAthenaDbName).click().type(DbName); + + fillDataSourceTextField( + amazonAthenaText.labelAccesskey, + amazonAthenaText.placeholderEnteraAccessKey, + Cypress.env("amazonathena_accessKey") + ); + fillDataSourceTextField( + amazonAthenaText.labelSecretKey, + amazonAthenaText.placeholderSecretKey, + Cypress.env("amazonathena_secretKey") + ); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.get(postgreSqlSelector.buttonSave).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.get( + `[data-cy="cypress-${data.dsName}-amazon-athena-button"]` + ).verifyVisibleElement("have.text", `cypress-${data.dsName}-amazon-athena`); + cy.wait(1000); + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + cy.get(`[data-cy="list-query-${data.dsName}"]`).should("be.visible"); + cy.get('[data-cy="query-input-field"]').clearAndTypeOnCodeMirror( + "SHOW DATABASES;" + ); + cy.get( + '[data-cy="query-input-field"] >>> .cm-editor >> .cm-content > .cm-line' + ).should("have.text", "SHOW DATABASES;"); + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-amazon-Athena` + ); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonsesHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonsesHappyPath.cy.skip.js new file mode 100644 index 0000000000..9c3864c9fd --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonsesHappyPath.cy.skip.js @@ -0,0 +1,204 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { pluginSelectors } from "Selectors/plugins"; +import { postgreSqlText } from "Texts/postgreSql"; +import { amazonSesText } from "Texts/amazonSes"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; + +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; + +describe("Data source amazon ses", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it.skip("Should verify elements on amazonses connection form", () => { + const Accesskey = Cypress.env("amazonSes_accessKey"); + const Secretkey = Cypress.env("amazonSes_secretKey"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + selectAndAddDataSource("databases", amazonSesText.AmazonSES, data.dsName); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + cy.get(pluginSelectors.amazonsesAccesKey).click().type(Accesskey); + + fillDataSourceTextField( + amazonSesText.labelSecretKey, + "**************", + Secretkey + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-Amazon-ses`); + }); + + it.skip("Should verify the functionality of amazonses connection form.", () => { + selectAndAddDataSource("databases", amazonSesText.AmazonSES, data.dsName); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + fillDataSourceTextField( + amazonSesText.labelAccesskey, + amazonSesText.placeholderAccessKey, + Cypress.env("amazonSes_accessKey") + ); + fillDataSourceTextField( + amazonSesText.labelSecretKey, + amazonSesText.placeholderSecretKey, + Cypress.env("amazonSes_secretKey") + ); + + cy.get(postgreSqlSelector.buttonSave).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.get( + `[data-cy="cypress-${data.dsName}-amazon-ses-button"]` + ).verifyVisibleElement("have.text", `cypress-${data.dsName}-amazon-ses`); + + deleteDatasource(`cypress-${data.dsName}-amazon-ses`); + }); + + it.skip("Should able to run the query with valid conection", () => { + const email = "adish" + "@" + "tooljet.com"; + selectAndAddDataSource("databases", amazonSesText.AmazonSES, data.dsName); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + fillDataSourceTextField( + amazonSesText.labelAccesskey, + amazonSesText.placeholderAccessKey, + Cypress.env("amazonSes_accessKey") + ); + fillDataSourceTextField( + amazonSesText.labelSecretKey, + amazonSesText.placeholderSecretKey, + Cypress.env("amazonSes_secretKey") + ); + + cy.get(postgreSqlSelector.buttonSave).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.get( + `[data-cy="cypress-${data.dsName}-amazon-ses-button"]` + ).verifyVisibleElement("have.text", `cypress-${data.dsName}-amazon-ses`); + cy.wait(1000); + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + cy.get(pluginSelectors.operationDropdown) + .click() + .type("Email service{enter}"); + + cy.wait(500); + + cy.get(pluginSelectors.sendEmailInputField) + .realClick() + .realType('{{["', { force: true, delay: 0 }) + .realType("mekhla@tooljet.com", { force: true, delay: 0 }); + + cy.get(pluginSelectors.ccEmailInputField) + .realClick() + .realType('{{["', { force: true, delay: 0 }) + .realType("mani@tooljet.com", { force: true, delay: 0 }); + + cy.get(pluginSelectors.bccEmailInputField) + .realClick() + .realType('{{["', { force: true, delay: 0 }) + .realType("midhun@tooljet.com", { force: true, delay: 0 }); + + cy.get(pluginSelectors.sendEmailFromInputField) + .realClick() + .realType("adish", { force: true, delay: 0 }) + .realType("@", { force: true, delay: 0 }) + .realType("tooljet.com", { force: true, delay: 0 }); + + cy.get(pluginSelectors.emailSubjetInputField).clearAndTypeOnCodeMirror( + "Testmail for amazon ses" + ); + + cy.get(pluginSelectors.emailbodyInputField).clearAndTypeOnCodeMirror( + "Body text for amazon ses" + ); + + cy.wait(1000); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-amazon-ses` + ); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/appWriteHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/appWriteHappyPath.cy.skip.js new file mode 100644 index 0000000000..27f52fd99d --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/appWriteHappyPath.cy.skip.js @@ -0,0 +1,314 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { appwriteText } from "Texts/appWrite"; +import { appWriteSelectors } from "Selectors/Plugins"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; + +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; + +describe("Data source AppWrite", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it.skip("Should verify elements on appwrite connection form", () => { + const Host = Cypress.env("appwrite_host"); + const ProjectID = Cypress.env("appwrite_projectID"); + const DatabaseID = Cypress.env("appwrite_databaseID"); + const SecretKey = Cypress.env("appwrite_secretkey"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + selectAndAddDataSource("databases", appwriteText.appwrite, data.dsName); + + fillDataSourceTextField( + appwriteText.host, + appwriteText.hostPlaceholder, + Host + ); + + fillDataSourceTextField( + appwriteText.ProjectID, + appwriteText.projectIdPlaceholder, + ProjectID + ); + + fillDataSourceTextField( + appwriteText.DatabaseID, + appwriteText.databaseIdPlaceholder, + DatabaseID + ); + + fillDataSourceTextField( + appwriteText.SecretKey, + appwriteText.SecretKeyPlaceholder, + SecretKey + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-Appwrite`); + }); + + it.skip("Should verify the functionality of appwrite connection form.", () => { + const Host = Cypress.env("appwrite_host"); + const ProjectID = Cypress.env("appwrite_projectID"); + const DatabaseID = Cypress.env("appwrite_databaseID"); + const SecretKey = Cypress.env("appwrite_secretkey"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + selectAndAddDataSource("databases", appwriteText.appwrite, data.dsName); + + fillDataSourceTextField( + appwriteText.host, + appwriteText.hostPlaceholder, + Host + ); + + fillDataSourceTextField( + appwriteText.ProjectID, + appwriteText.projectIdPlaceholder, + ProjectID + ); + + fillDataSourceTextField( + appwriteText.DatabaseID, + appwriteText.databaseIdPlaceholder, + DatabaseID + ); + + fillDataSourceTextField( + appwriteText.SecretKey, + appwriteText.SecretKeyPlaceholder, + SecretKey + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + deleteDatasource(`cypress-${data.dsName}-Appwrite`); + }); + + it.skip("Should be able to run the query with a valid connection", () => { + const Host = Cypress.env("appwrite_host"); + const ProjectID = Cypress.env("appwrite_projectID"); + const DatabaseID = Cypress.env("appwrite_databaseID"); + const SecretKey = Cypress.env("appwrite_secretkey"); + const CollectionID = Cypress.env("appwrite_collectionID"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + selectAndAddDataSource("databases", appwriteText.appwrite, data.dsName); + + fillDataSourceTextField( + appwriteText.host, + appwriteText.hostPlaceholder, + Host + ); + fillDataSourceTextField( + appwriteText.ProjectID, + appwriteText.projectIdPlaceholder, + ProjectID + ); + fillDataSourceTextField( + appwriteText.DatabaseID, + appwriteText.databaseIdPlaceholder, + DatabaseID + ); + fillDataSourceTextField( + appwriteText.SecretKey, + appwriteText.SecretKeyPlaceholder, + SecretKey + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.get( + `[data-cy="cypress-${data.dsName}-appwrite-button"]` + ).verifyVisibleElement("have.text", `cypress-${data.dsName}-appwrite`); + cy.wait(1000); + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + // Create API document for delete operation + + cy.request({ + method: "POST", + url: `https://cloud.appwrite.io/v1/databases/${DatabaseID}/collections/${CollectionID}/documents`, + headers: { + "X-Appwrite-Project": ProjectID, + "X-Appwrite-Key": SecretKey, + "Content-Type": "application/json", + }, + body: { + documentId: "unique()", + data: { + User_name: "test", + User_ID: 30, + }, + permissions: ['read("any")'], + }, + }).then((response) => { + expect(response.status).to.eq(201); + cy.wrap(response.body.$id).as("documentId"); + }); + + // Verify all operations + const operations = [ + "List documents", + "Get document", + "Add Document to Collection", + "Update document", + "Delete document", + ]; + + cy.get("@documentId").then((documentId) => { + operations.forEach((operation) => { + cy.get(".react-select__input") + .eq(1) + .type(`${operation}{enter}`, { force: true }); + + if (operation === "Get document") { + cy.get(appWriteSelectors.collectionId).clearAndTypeOnCodeMirror( + CollectionID + ); + + cy.get(appWriteSelectors.documentId).clearAndTypeOnCodeMirror( + Cypress.env("appwrite_documentID") + ); + } + + if (operation === "Add Document to Collection") { + cy.get(appWriteSelectors.collectionId).clearAndTypeOnCodeMirror( + CollectionID + ); + cy.get(appWriteSelectors.bodyInput).clearAndTypeOnCodeMirror( + '{"User_name": "John Updated", "User_ID": 35}' + ); + } + + if (operation === "Update document") { + cy.get(appWriteSelectors.collectionId).clearAndTypeOnCodeMirror( + CollectionID + ); + + cy.get(appWriteSelectors.documentId).clearAndTypeOnCodeMirror( + Cypress.env("appwrite_documentID") + ); + cy.get(appWriteSelectors.bodyInput).clearAndTypeOnCodeMirror( + '{"User_name": "John Updated", "User_ID": 35}' + ); + } + + if (operation === "List documents") { + cy.get(appWriteSelectors.collectionId).clearAndTypeOnCodeMirror( + CollectionID + ); + } + + if (operation === "Delete document") { + cy.get(appWriteSelectors.collectionId).clearAndTypeOnCodeMirror( + CollectionID + ); + cy.get(appWriteSelectors.documentId).clearAndTypeOnCodeMirror( + documentId + ); + } + cy.get(dataSourceSelector.queryPreviewButton).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + }); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-Appwrite` + ); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsLambdaHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsLambdaHappyPath.cy.skip.js new file mode 100644 index 0000000000..17a3f0426c --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsLambdaHappyPath.cy.skip.js @@ -0,0 +1,188 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { pluginSelectors } from "Selectors/plugins"; +import { postgreSqlText } from "Texts/postgreSql"; +import { awsLambdaText } from "Texts/awsLambda"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; + +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; + +describe("Data source AWS Lambda", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it.skip("Should verify elements on AWS Lambda connection form", () => { + const Accesskey = Cypress.env("awslamda_access"); + const Secretkey = Cypress.env("awslamda_secret"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + cy.installMarketplacePlugin("AWS Lambda"); + + selectAndAddDataSource("databases", awsLambdaText.awsLambda, data.dsName); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + cy.get(pluginSelectors.amazonsesAccesKey).click().type(Accesskey); + + fillDataSourceTextField( + awsLambdaText.labelSecretKey, + "**************", + Secretkey + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-aws-lambda`); + cy.uninstallMarketplacePlugin("AWS Lambda"); + }); + + it.skip("Should verify the functionality of AWS Lambda connection form", () => { + const Accesskey = Cypress.env("awslamda_access"); + const Secretkey = Cypress.env("awslamda_secret"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.installMarketplacePlugin("AWS Lambda"); + + selectAndAddDataSource("databases", awsLambdaText.awsLambda, data.dsName); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + cy.get(pluginSelectors.amazonsesAccesKey).click().type(Accesskey); + + fillDataSourceTextField( + awsLambdaText.labelSecretKey, + "**************", + Secretkey + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-aws-lambda`); + cy.uninstallMarketplacePlugin("AWS Lambda"); + }); + + it.skip("Should able to run the query with valid conection", () => { + const Accesskey = Cypress.env("awslamda_access"); + const Secretkey = Cypress.env("awslamda_secret"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.installMarketplacePlugin("AWS Lambda"); + + selectAndAddDataSource("databases", awsLambdaText.awsLambda, data.dsName); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option") + .contains("US West (N. California)") + .wait(500) + .click(); + + cy.get(pluginSelectors.amazonsesAccesKey).click().type(Accesskey); + + fillDataSourceTextField( + awsLambdaText.labelSecretKey, + "**************", + Secretkey + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + cy.get(pluginSelectors.operationDropdown) + .click() + .type("Invoke Lambda Function{enter}"); + + cy.wait(500); + + cy.get( + '[data-cy="function-name-section"] .cm-content' + ).clearAndTypeOnCodeMirror("testAwslambdaPlugin"); + + cy.wait(500); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-aws-lambda` + ); + cy.uninstallMarketplacePlugin("AWS Lambda"); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsTextractHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsTextractHappyPath.cy.skip.js new file mode 100644 index 0000000000..1a01ecc757 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsTextractHappyPath.cy.skip.js @@ -0,0 +1,225 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { pluginSelectors } from "Selectors/plugins"; +import { awsTextractSelectors } from "Selectors/Plugins"; +import { postgreSqlText } from "Texts/postgreSql"; +import { awsTextractText } from "Texts/awsTextract"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; + +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; + +describe("Data source AWS Textract", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it.skip("Should verify elements on AWS Textract connection form", () => { + const Accesskey = Cypress.env("awstextract_access"); + const Secretkey = Cypress.env("awstextract_secret"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + cy.installMarketplacePlugin("AWS Textract"); + + selectAndAddDataSource( + "databases", + awsTextractText.awsTextract, + data.dsName + ); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + cy.get(pluginSelectors.amazonsesAccesKey).click().type(Accesskey); + + fillDataSourceTextField( + awsTextractText.labelSecretKey, + "**************", + Secretkey + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-aws-textract`); + }); + + it.skip("Should verify functionality of AWS Textract connection form", () => { + const Accesskey = Cypress.env("awstextract_access"); + const Secretkey = Cypress.env("awstextract_secret"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.installMarketplacePlugin("AWS Textract"); + + selectAndAddDataSource( + "databases", + awsTextractText.awsTextract, + data.dsName + ); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option").contains("US West (N. California)").click(); + + cy.get(pluginSelectors.amazonsesAccesKey).click().type(Accesskey); + + fillDataSourceTextField( + awsTextractText.labelSecretKey, + "**************", + Secretkey + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-aws-textract`); + cy.uninstallMarketplacePlugin("AWS Textract"); + }); + + it.skip("Should able to run the query with valid conection", () => { + const Accesskey = Cypress.env("awstextract_access"); + const Secretkey = Cypress.env("awstextract_secret"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.installMarketplacePlugin("AWS Textract"); + + selectAndAddDataSource( + "databases", + awsTextractText.awsTextract, + data.dsName + ); + + cy.get(".react-select__dropdown-indicator").eq(1).click(); + cy.get(".react-select__option") + .contains("US West (N. California)") + .wait(500) + .click(); + + cy.get(pluginSelectors.amazonsesAccesKey).click().type(Accesskey); + + fillDataSourceTextField( + awsTextractText.labelSecretKey, + "**************", + Secretkey + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + // Verifying analyze document operation + cy.get(pluginSelectors.operationDropdown) + .click() + .wait(500) + .type("Analyze Document{enter}"); + + cy.wait(500); + + cy.get(awsTextractSelectors.documentInputField).clearAndTypeOnCodeMirror( + awsTextractText.documentName + ); + + cy.wait(500); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + // Verifying Analyze document stored in AWS S3 operation + + cy.get(pluginSelectors.operationDropdown) + .click() + .wait(500) + .type("Analyze document stored in AWS S3{enter}"); + + cy.wait(500); + + cy.get(awsTextractSelectors.bucketNameInputField).clearAndTypeOnCodeMirror( + awsTextractText.bucketName + ); + + cy.get(awsTextractSelectors.keyNameInputField).clearAndTypeOnCodeMirror( + awsTextractText.keyName + ); + + cy.wait(500); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-aws-textract` + ); + cy.uninstallMarketplacePlugin("AWS Textract"); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/azureBlobStorageHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/azureBlobStorageHappyPath.cy.js index 54aca0322e..0c505cc417 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/azureBlobStorageHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/azureBlobStorageHappyPath.cy.js @@ -17,8 +17,8 @@ data.customText = fake.randomSentence; describe("Data source Azure Blob Storage", () => { beforeEach(() => { - cy.appUILogin(); - cy.intercept("GET", "/api/v2/data_sources"); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/baseRowHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/baseRowHappyPath.cy.skip.js new file mode 100644 index 0000000000..4b03148e9e --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/baseRowHappyPath.cy.skip.js @@ -0,0 +1,217 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { pluginSelectors, baserowSelectors } from "Selectors/plugins"; +import { postgreSqlText } from "Texts/postgreSql"; +import { baseRowText } from "Texts/baseRow"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; + +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; + +describe("Data source baserow", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it.skip("Should verify elements on baserow connection form", () => { + const Apikey = Cypress.env("baserow_apikey"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + selectAndAddDataSource("databases", baseRowText.baserow, data.dsName); + + fillDataSourceTextField( + baseRowText.lableApiToken, + baseRowText.placeholderApiToken, + Apikey + ); + + cy.get(".react-select__control").eq(1).click(); + + cy.get(".react-select__option").contains("Baserow Cloud").click(); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-baserow`); + }); + + it.skip("Should verify the functionality of baserow connection form.", () => { + const Apikey = Cypress.env("baserow_apikey"); + + selectAndAddDataSource("databases", baseRowText.baserow, data.dsName); + + fillDataSourceTextField( + baseRowText.lableApiToken, + baseRowText.placeholderApiToken, + Apikey + ); + + cy.get(".react-select__control").eq(1).click(); + + cy.get(".react-select__option").contains("Baserow Cloud").click(); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + deleteDatasource(`cypress-${data.dsName}-baserow`); + }); + + it.skip("Should be able to run the query with a valid connection", () => { + const baserowTableID = Cypress.env("baserow_tableid"); + const baserowRowID = Cypress.env("baserow_rowid"); + const Apikey = Cypress.env("baserow_apikey"); + + selectAndAddDataSource("databases", baseRowText.baserow, data.dsName); + + fillDataSourceTextField( + baseRowText.lableApiToken, + baseRowText.placeholderApiToken, + Apikey + ); + + cy.get(".react-select__control").eq(1).click(); + cy.get(".react-select__option").contains("Baserow Cloud").click(); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.get( + `[data-cy="cypress-${data.dsName}-baserow-button"]` + ).verifyVisibleElement("have.text", `cypress-${data.dsName}-baserow`); + cy.wait(1000); + + cy.log("Baserow Table ID:", baserowTableID); + cy.log("Row ID:", baserowRowID); + cy.log("API Key:", Apikey); + + if (!baserowTableID || !Apikey) { + throw new Error("Missing required environment variables!"); + } + + cy.request({ + method: "POST", + url: `https://api.baserow.io/api/database/rows/table/${baserowTableID}/`, + headers: { Authorization: `Token ${Apikey}` }, + body: { + field_1: "Sample Data", + field_2: "Another Value", + }, + }).then((response) => { + expect(response.status).to.eq(200); + const rowId = response.body.id; + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + // Verify delete operation (Need to uncomment after bug fixes) + + // cy.get('[data-cy="operation-select-dropdown"]').click(); + // cy.get(".react-select__option").contains("Delete row").click(); + + // cy.get(baserowSelectors.baserowTabelId).clearAndTypeOnCodeMirror(baserowTableID); + // cy.get(baserowSelectors.rowIdinputfield).clearAndTypeOnCodeMirror(rowId.toString()); + + // cy.get(dataSourceSelector.queryPreviewButton).click(); + // cy.verifyToastMessage(commonSelectors.toastMessage, `Query (${data.dsName}) completed.`); + }); + + // Verify other operations + const operations = [ + "List fields", + "List rows", + "Get row", + "Create row", + "Update row", + "Move row", + ]; + + operations.forEach((operation) => { + cy.get(pluginSelectors.operationDropdown).click(); + cy.get(".react-select__option").contains(operation).click(); + + cy.get(baserowSelectors.table).clearAndTypeOnCodeMirror(baserowTableID); + + if (operation === "Get row") { + cy.get(baserowSelectors.rowIdinputfield).clearAndTypeOnCodeMirror( + baserowRowID + ); + } + if (operation === "Move row") { + cy.get('[data-cy="before-id-input-field"]').clearAndTypeOnCodeMirror( + "1" + ); + } + + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + }); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-baserow` + ); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.js index e0e5db1bd4..2bbe0787e7 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.js @@ -4,6 +4,7 @@ import { postgreSqlText } from "Texts/postgreSql"; import { bigqueryText } from "Texts/bigquery"; import { firestoreText } from "Texts/firestore"; import { commonSelectors } from "Selectors/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { fillDataSourceTextField, selectAndAddDataSource, @@ -15,8 +16,8 @@ const data = {}; describe("Data source BigQuery", () => { beforeEach(() => { - cy.appUILogin(); - cy.intercept("GET", "/api/v2/data_sources"); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -50,16 +51,24 @@ describe("Data source BigQuery", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource( - "databases", - bigqueryText.bigQuery, - data.dataSourceName + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-bigquery`, + "bigquery", + [{ key: "private_key", value: "", encrypted: true }] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-bigquery-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-bigquery` ); - cy.get('[data-cy="label-private-key"]').verifyVisibleElement( - "have.text", - firestoreText.labelPrivateKey - ); + cy.get( + dataSourceSelector.labelFieldName(firestoreText.labelPrivateKey) + ).verifyVisibleElement("have.text", "Private key*"); cy.get(".datasource-edit-btn").should("be.visible"); cy.get(postgreSqlSelector.labelIpWhitelist).verifyVisibleElement( "have.text", @@ -88,7 +97,7 @@ describe("Data source BigQuery", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').verifyVisibleElement( + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( "have.text", bigqueryText.errorInvalidEmailId ); @@ -100,38 +109,30 @@ describe("Data source BigQuery", () => { }); it("Should verify the functionality of BigQuery connection form.", () => { - selectAndAddDataSource( - "databases", - bigqueryText.bigQuery, - data.dataSourceName - ); - - fillDataSourceTextField( - firestoreText.privateKey, - bigqueryText.placehlderPrivateKey, - `${JSON.stringify(Cypress.env("bigquery_pvt_key"))}`, - "contain", - { parseSpecialCharSequences: false, delay: 0 } + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-bigquery`, + "bigquery", + [ + { + key: "private_key", + value: `${JSON.stringify(Cypress.env("bigquery_pvt_key"))}`, + encrypted: true, + }, + ] ); + cy.get( + dataSourceSelector.dataSourceNameButton( + `cypress-${data.dataSourceName}-bigquery` + ) + ) + .should("be.visible") + .click(); cy.get(postgreSqlSelector.buttonTestConnection).click(); cy.get(postgreSqlSelector.textConnectionVerified, { timeout: 10000, }).should("have.text", postgreSqlText.labelConnectionVerified); - cy.get(postgreSqlSelector.buttonSave).click(); - - cy.verifyToastMessage( - commonSelectors.toastMessage, - postgreSqlText.toastDSSaved - ); - - cy.get(commonSelectors.globalDataSourceIcon).click(); - cy.get( - `[data-cy="cypress-${data.dataSourceName}-bigquery-button"]` - ).verifyVisibleElement( - "have.text", - `cypress-${data.dataSourceName}-bigquery` - ); - - deleteDatasource(`cypress-${data.dataSourceName}-bigquery`); + cy.apiDeleteGDS(`cypress-${data.dataSourceName}-bigquery`); }); }); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/clickHouseHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/clickHouseHappyPath.cy.js index da601cbe30..6ac8a3c8d1 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/clickHouseHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/clickHouseHappyPath.cy.js @@ -1,9 +1,8 @@ import { fake } from "Fixtures/fake"; import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; -import { commonWidgetText } from "Texts/common"; import { commonSelectors, commonWidgetSelector } from "Selectors/common"; -import { commonText } from "Texts/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { closeDSModal, deleteDatasource } from "Support/utils/dataSource"; import { addQuery, @@ -20,7 +19,8 @@ const data = {}; describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -51,13 +51,20 @@ describe("Data sources", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource("databases", "ClickHouse", data.dataSourceName); - - // cy.get(postgreSqlSelector.dataSourceNameInputField).should( - // //username,password,host,port,protocol,dbname,usepost, trimquery,gzip,debug,raw - // "have.value", - // "ClickHouse" - // ); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-clickhouse`, + "clickhouse", + [] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-clickhouse-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-clickhouse` + ); cy.get(postgreSqlSelector.labelUserName).verifyVisibleElement( "have.text", postgreSqlText.labelUserName @@ -78,7 +85,7 @@ describe("Data sources", () => { cy.get(postgreSqlSelector.labelDbName).verifyVisibleElement( "have.text", - postgreSqlText.labelDbName + "Database Name" ); cy.get('[data-cy="label-protocol"]').verifyVisibleElement( "have.text", @@ -140,11 +147,7 @@ describe("Data sources", () => { Cypress.env("pg_host") ); fillDataSourceTextField(postgreSqlText.labelPort, "8123", "8123"); - fillDataSourceTextField( - postgreSqlText.labelDbName, - "database name", - "{del}" - ); + fillDataSourceTextField("Database Name", "database name", "{del}"); fillDataSourceTextField( postgreSqlText.labelUserName, postgreSqlText.placeholderEnterUserName, diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/cosmosDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/cosmosDbHappyPath.cy.js index 12da4c9684..e450e52fc6 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/cosmosDbHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/cosmosDbHappyPath.cy.js @@ -1,9 +1,8 @@ import { fake } from "Fixtures/fake"; import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; -import { commonWidgetText } from "Texts/common"; import { commonSelectors, commonWidgetSelector } from "Selectors/common"; -import { commonText } from "Texts/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { closeDSModal, deleteDatasource } from "Support/utils/dataSource"; import { addQuery, @@ -20,7 +19,8 @@ const data = {}; describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -50,7 +50,23 @@ describe("Data sources", () => { "have.text", postgreSqlText.allCloudStorage ); - selectAndAddDataSource("databases", "CosmosDB", data.dataSourceName); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-cosmosdb`, + "cosmosdb", + [ + { key: "endpoint", value: "" }, + { key: "key", value: "", encrypted: true }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-cosmosdb-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-cosmosdb` + ); cy.get('[data-cy="label-end-point"]').verifyVisibleElement( "have.text", @@ -85,14 +101,14 @@ describe("Data sources", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').verifyVisibleElement( + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( "have.text", "Invalid URL" ); deleteDatasource(`cypress-${data.dataSourceName}-cosmosdb`); }); - it.only("Should verify the functionality of CosmosDB connection form.", () => { + it("Should verify the functionality of CosmosDB connection form.", () => { selectAndAddDataSource("databases", "CosmosDB", data.dataSourceName); fillDataSourceTextField( diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/couchDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/couchDbHappyPath.cy.js index 240d8de87b..06bfe675fd 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/couchDbHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/couchDbHappyPath.cy.js @@ -3,7 +3,7 @@ import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; import { commonWidgetText } from "Texts/common"; import { commonSelectors, commonWidgetSelector } from "Selectors/common"; -import { commonText } from "Texts/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { closeDSModal, deleteDatasource } from "Support/utils/dataSource"; import { @@ -21,7 +21,8 @@ const data = {}; describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -52,7 +53,27 @@ describe("Data sources", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource("databases", "CouchDB", data.dataSourceName); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-couchdb`, + "couchdb", + [ + { key: "username", value: "", encrypted: false }, + { key: "password", value: "", encrypted: true }, + { key: "database", value: "" }, + { key: "port", value: "5984" }, + { key: "host", value: "" }, + { key: "protocol" }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-couchdb-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-couchdb` + ); cy.get(postgreSqlSelector.labelHost).verifyVisibleElement( "have.text", @@ -72,7 +93,7 @@ describe("Data sources", () => { ); cy.get(postgreSqlSelector.labelDbName).verifyVisibleElement( "have.text", - postgreSqlText.labelDbName + "Database Name" ); cy.get('[data-cy="label-protocol"]').verifyVisibleElement( @@ -106,7 +127,7 @@ describe("Data sources", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').verifyVisibleElement( + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( "have.text", "Invalid URL" ); @@ -122,11 +143,7 @@ describe("Data sources", () => { Cypress.env("couchdb_host") ); fillDataSourceTextField(postgreSqlText.labelPort, "5984 ", "5984"); - fillDataSourceTextField( - postgreSqlText.labelDbName, - "database name", - "{del}" - ); + fillDataSourceTextField("Database Name", "database name", "{del}"); fillDataSourceTextField( postgreSqlText.labelUserName, "username for couchDB", diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/dynamoDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/dynamoDbHappyPath.cy.js index b8e588cf7d..3585fc9c4f 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/dynamoDbHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/dynamoDbHappyPath.cy.js @@ -3,7 +3,7 @@ import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; import { dynamoDbText } from "Texts/dynamodb"; import { commonSelectors } from "Selectors/common"; -import { commonText } from "Texts/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { fillDataSourceTextField, @@ -19,7 +19,8 @@ const data = {}; describe("Data source DynamoDB", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -50,10 +51,28 @@ describe("Data source DynamoDB", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource( - "databases", - dynamoDbText.dynamoDb, - data.dataSourceName + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-dynamodb`, + "dynamodb", + [ + { key: "region", value: "" }, + { key: "access_key", value: "" }, + { key: "secret_key", value: "", encrypted: true }, + { + key: "instance_metadata_credentials", + value: "iam_access_keys", + encrypted: false, + }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-dynamodb-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-dynamodb` ); cy.get('[data-cy="label-region"]').verifyVisibleElement( @@ -96,7 +115,7 @@ describe("Data source DynamoDB", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').verifyVisibleElement( + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( "have.text", dynamoDbText.errorMissingRegion ); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/elasticsearchHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/elasticsearchHappyPath.cy.js index 774416c222..8faca425a0 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/elasticsearchHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/elasticsearchHappyPath.cy.js @@ -3,7 +3,7 @@ import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; import { elasticsearchText } from "Texts/elasticsearch"; import { commonSelectors } from "Selectors/common"; -import { commonText } from "Texts/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { fillDataSourceTextField, selectAndAddDataSource, @@ -17,8 +17,12 @@ import { const data = {}; describe("Data source Elasticsearch", () => { beforeEach(() => { - cy.appUILogin(); - data.lastName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + cy.apiLogin(); + cy.visit("/"); + + data.dataSourceName = fake.lastName + .toLowerCase() + .replaceAll("[^A-Za-z]", ""); }); it("Should verify elements on Elasticsearch connection form", () => { @@ -46,12 +50,27 @@ describe("Data source Elasticsearch", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource( - "databases", - elasticsearchText.elasticSearch, - data.lastName + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-elasticsearch`, + "elasticsearch", + [ + { key: "host", value: "localhost" }, + { key: "port", value: 9200 }, + { key: "username", value: "" }, + { key: "password", value: "", encrypted: true }, + { key: "ssl_enabled", value: true, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-elasticsearch-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-elasticsearch` ); - cy.get(postgreSqlSelector.labelHost).verifyVisibleElement( "have.text", postgreSqlText.labelHost @@ -74,7 +93,7 @@ describe("Data source Elasticsearch", () => { ); cy.get(postgreSqlSelector.labelSSLCertificate).verifyVisibleElement( "have.text", - postgreSqlText.sslCertificate + "SSL Certificate" ); cy.get(postgreSqlSelector.labelIpWhitelist).verifyVisibleElement( "have.text", @@ -103,18 +122,18 @@ describe("Data source Elasticsearch", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').verifyVisibleElement( + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( "have.text", elasticsearchText.errorConnectionRefused ); - deleteDatasource(`cypress-${data.lastName}-elasticsearch`); + deleteDatasource(`cypress-${data.dataSourceName}-elasticsearch`); }); it("Should verify the functionality of Elasticsearch connection form.", () => { selectAndAddDataSource( "databases", elasticsearchText.elasticSearch, - data.lastName + data.dataSourceName ); fillDataSourceTextField( @@ -194,12 +213,12 @@ describe("Data source Elasticsearch", () => { ); cy.get( - `[data-cy="cypress-${data.lastName}-elasticsearch-button"]` + `[data-cy="cypress-${data.dataSourceName}-elasticsearch-button"]` ).verifyVisibleElement( "have.text", - `cypress-${data.lastName}-elasticsearch` + `cypress-${data.dataSourceName}-elasticsearch` ); - deleteDatasource(`cypress-${data.lastName}-elasticsearch`); + deleteDatasource(`cypress-${data.dataSourceName}-elasticsearch`); }); }); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/fireStoreHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/fireStoreHappyPath.cy.js index 8792279352..31b6099d4c 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/fireStoreHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/fireStoreHappyPath.cy.js @@ -3,7 +3,7 @@ import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; import { firestoreText } from "Texts/firestore"; import { commonSelectors } from "Selectors/common"; -import { commonText } from "Texts/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { verifyCouldnotConnectWithAlert, deleteDatasource, @@ -17,7 +17,8 @@ const data = {}; describe("Data source Firestore", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -47,12 +48,20 @@ describe("Data source Firestore", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource( - "databases", - firestoreText.firestore, - data.dataSourceName + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-firestore`, + "firestore", + [{ key: "gcp_key", value: "", encrypted: true }] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-firestore-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-firestore` ); - cy.get('[data-cy="label-private-key"]').verifyVisibleElement( "have.text", firestoreText.labelPrivateKey @@ -85,7 +94,7 @@ describe("Data source Firestore", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').verifyVisibleElement( + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( "have.text", firestoreText.errorGcpKeyCouldNotBeParsed ); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/graphQLHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/graphQLHappyPath.cy.js new file mode 100644 index 0000000000..7ad8e5484b --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/graphQLHappyPath.cy.js @@ -0,0 +1,153 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { GraphQLText } from "Texts/graphQL"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; + +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; + +describe("Data source GraphQL", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it("Should verify elements on GraphQL connection form", () => { + const Url = Cypress.env("GraphQl_Url"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + selectAndAddDataSource("databases", GraphQLText.GraphQL, data.dsName); + + fillDataSourceTextField( + GraphQLText.urlInputLabel, + GraphQLText.urlInputPlaceholder, + Url + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-GraphQL`); + }); + + it("Should verify the functionality of GraphQL connection form", () => { + const Url = Cypress.env("GraphQl_Url"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + selectAndAddDataSource("databases", GraphQLText.GraphQL, data.dsName); + + fillDataSourceTextField( + GraphQLText.urlInputLabel, + GraphQLText.urlInputPlaceholder, + Url + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-GraphQL`); + }); + + it("Should able to run the query with valid conection", () => { + const Url = Cypress.env("GraphQl_Url"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + selectAndAddDataSource("databases", GraphQLText.GraphQL, data.dsName); + + fillDataSourceTextField( + GraphQLText.urlInputLabel, + GraphQLText.urlInputPlaceholder, + Url + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + cy.get('[data-cy="query-input-field"]').clearAndTypeOnCodeMirror( + `{ + allFilms { + films { title director } + } + }` + ); + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-GraphQL` + ); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/harperDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/harperDbHappyPath.cy.js new file mode 100644 index 0000000000..12ad23efd9 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/harperDbHappyPath.cy.js @@ -0,0 +1,345 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { harperDbText } from "Texts/harperDb"; +import { harperDbSelectors } from "Selectors/Plugins"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; + +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; +data.dsName1 = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + +describe("Data source HarperDB", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it("Should verify elements on HarperDB connection form", () => { + const Host = Cypress.env("harperdb_host"); + const Port = Cypress.env("harperdb_port"); + const Username = Cypress.env("harperdb_username"); + const Password = Cypress.env("harperdb_password"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + cy.installMarketplacePlugin("HarperDB"); + + selectAndAddDataSource("databases", harperDbText.harperDb, data.dsName); + + fillDataSourceTextField( + harperDbText.hostLabel, + harperDbText.hostInputPlaceholder, + Host + ); + + fillDataSourceTextField( + harperDbText.portLabel, + harperDbText.portPlaceholder, + Port + ); + + fillDataSourceTextField( + harperDbText.userNameLabel, + harperDbText.userNamePlaceholder, + Username + ); + + fillDataSourceTextField( + harperDbText.passwordlabel, + harperDbText.passwordPlaceholder, + Password + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-HarperDB`); + cy.uninstallMarketplacePlugin("HarperDB"); + }); + + it("Should verify functionality of HarperDB connection form", () => { + const Host = Cypress.env("harperdb_host"); + const Port = Cypress.env("harperdb_port"); + const Username = Cypress.env("harperdb_username"); + const Password = Cypress.env("harperdb_password"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.installMarketplacePlugin("HarperDB"); + + selectAndAddDataSource("databases", harperDbText.harperDb, data.dsName); + + fillDataSourceTextField( + harperDbText.hostLabel, + harperDbText.hostInputPlaceholder, + Host + ); + + fillDataSourceTextField( + harperDbText.portLabel, + harperDbText.portPlaceholder, + Port + ); + + fillDataSourceTextField( + harperDbText.userNameLabel, + harperDbText.userNamePlaceholder, + Username + ); + + fillDataSourceTextField( + harperDbText.passwordlabel, + harperDbText.passwordPlaceholder, + Password + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-HarperDB`); + cy.uninstallMarketplacePlugin("HarperDB"); + }); + + it.skip("Should be able to run the query with a valid connection", () => { + const Host = Cypress.env("harperdb_host"); + const Port = Cypress.env("harperdb_port"); + const Username = Cypress.env("harperdb_username"); + const Password = Cypress.env("harperdb_password"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.installMarketplacePlugin("HarperDB"); + + selectAndAddDataSource("databases", harperDbText.harperDb, data.dsName); + + fillDataSourceTextField( + harperDbText.hostLabel, + harperDbText.hostInputPlaceholder, + Host + ); + + fillDataSourceTextField( + harperDbText.portLabel, + harperDbText.portPlaceholder, + Port + ); + + fillDataSourceTextField( + harperDbText.userNameLabel, + harperDbText.userNamePlaceholder, + Username + ); + + fillDataSourceTextField( + harperDbText.passwordlabel, + harperDbText.passwordPlaceholder, + Password + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + // Verifying NoSQL Operation + + cy.get(".react-select__input") + .eq(1) + .click({ force: true }) + .wait(500) + .type(`NoSQL mode{enter}`, { force: true }); + + const operationsDB = [ + "Insert", + "Update", + "Search By Hash", + "Search By Value", + "Search By Condition", + "Delete", + ]; + + operationsDB.forEach((operation) => { + cy.get(".react-select__input") + .eq(2) + .click({ force: true }) + .wait(500) + .type(`${operation}{enter}`, { force: true }); + + const commonFields = { + "schema-input-field": harperDbText.schemaValue, + "table-input-field": harperDbText.tableValue, + }; + + Object.entries(commonFields).forEach(([field, value]) => { + cy.get(`[data-cy="${field}"]`).clearAndTypeOnCodeMirror(value); + }); + + if (["Insert", "Update"].includes(operation)) { + cy.get(harperDbSelectors.recordsInputField).clearAndTypeOnCodeMirror( + harperDbText.recordsValue + ); + } + + if (operation === "Search By Hash") { + cy.get(harperDbSelectors.hashValueInputField).clearAndTypeOnCodeMirror( + harperDbText.hashValue + ); + cy.get(harperDbSelectors.attributesInputField).clearAndTypeOnCodeMirror( + harperDbText.attributesValue + ); + } + + if (operation === "Search By Value") { + cy.get( + harperDbSelectors.searchAttributeInputField + ).clearAndTypeOnCodeMirror(harperDbText.searchAttributeValue); + + cy.get( + harperDbSelectors.searchValueInputField + ).clearAndTypeOnCodeMirror(harperDbText.searchValue); + cy.get(harperDbSelectors.attributesInputField).clearAndTypeOnCodeMirror( + harperDbText.attributesValue + ); + } + + if (operation === "Search By Condition") { + cy.get(".react-select__input") + .eq(3) + .click({ force: true }) + .wait(500) + .type("Or{enter}", { force: true }); + + cy.get(harperDbSelectors.attributesInputField).clearAndTypeOnCodeMirror( + harperDbText.attributesValue + ); + cy.get(harperDbSelectors.conditionInputField).clearAndTypeOnCodeMirror( + harperDbText.condtionValue + ); + } + + if (operation === "Delete") { + cy.get(harperDbSelectors.hashValueInputField).clearAndTypeOnCodeMirror( + harperDbText.hashValue + ); + } + + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + }); + + // Verifying SQL Operation + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName1); + + cy.get(".react-select__input") + .eq(1) + .should("be.visible") + .click({ force: true }) + .wait(500) + .type(`SQL mode{enter}`, { force: true }); + + cy.wait(1000); + + cy.get(harperDbSelectors.sqlQueryInputField).clearAndTypeOnCodeMirror( + harperDbText.sqlValue + ); + + cy.get(dataSourceSelector.queryPreviewButton).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName1}) completed.` + ); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-HarperDB` + ); + cy.uninstallMarketplacePlugin("HarperDB"); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/influxDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/influxDbHappyPath.cy.js index ee1d515a61..26db831cff 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/influxDbHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/influxDbHappyPath.cy.js @@ -1,8 +1,8 @@ import { fake } from "Fixtures/fake"; import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; -import { commonWidgetText, commonText } from "Texts/common"; import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { addQuery, fillDataSourceTextField, @@ -23,7 +23,8 @@ const data = {}; describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -54,7 +55,25 @@ describe("Data sources", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource("databases", "InfluxDB", data.dataSourceName); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-influxdb`, + "influxdb", + [ + { key: "api_token", value: "", encrypted: true }, + { key: "port", value: "8086", encrypted: false }, + { key: "host", value: "", encrypted: false }, + { key: "protocol", value: "http", encrypted: false }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-influxdb-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-influxdb` + ); cy.get('[data-cy="label-api-token"]').verifyVisibleElement( "have.text", @@ -99,7 +118,7 @@ describe("Data sources", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').verifyVisibleElement( + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( "have.text", "Invalid URL" ); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mariaDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mariaDbHappyPath.cy.js new file mode 100644 index 0000000000..020ca4a40a --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mariaDbHappyPath.cy.js @@ -0,0 +1,192 @@ +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { dataSourceSelector } from "Selectors/dataSource"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { + addQuery, + fillDataSourceTextField, + fillConnectionForm, + selectAndAddDataSource, + openQueryEditor, + selectQueryMode, + addGuiQuery, + addWidgetsToAddUser, +} from "Support/utils/postgreSql"; +import { fake } from "Fixtures/fake"; +import { closeDSModal, deleteDatasource } from "Support/utils/dataSource"; + +const data = {}; + +describe("Data sources", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dataSourceName = fake.lastName + .toLowerCase() + .replaceAll("[^A-Za-z]", ""); + }); + + it("Should verify elements on connection form", () => { + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-mariadb`, + "mariadb", + [{ key: "connectionLimit", value: 5 }] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-mariadb-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-mariadb` + ); + + cy.get(postgreSqlSelector.labelHost).verifyVisibleElement( + "have.text", + postgreSqlText.labelHost + ); + cy.get(postgreSqlSelector.labelUserName).verifyVisibleElement( + "have.text", + postgreSqlText.labelUserName + ); + cy.get(postgreSqlSelector.labelPassword).verifyVisibleElement( + "have.text", + "Password" + ); + cy.get('[data-cy="label-connection-limit"]').verifyVisibleElement( + "have.text", + "Connection Limit" + ); + cy.get(postgreSqlSelector.labelPort).verifyVisibleElement( + "have.text", + postgreSqlText.labelPort + ); + cy.get(postgreSqlSelector.labelSsl).verifyVisibleElement( + "have.text", + postgreSqlText.labelSSL + ); + cy.get('[data-cy="label-database"]').verifyVisibleElement( + "have.text", + "Database" + ); + + cy.get(postgreSqlSelector.labelSSLCertificate).verifyVisibleElement( + "have.text", + "SSL Certificate" + ); + cy.get(postgreSqlSelector.labelIpWhitelist).verifyVisibleElement( + "have.text", + postgreSqlText.whiteListIpText + ); + cy.get(postgreSqlSelector.buttonCopyIp).verifyVisibleElement( + "have.text", + postgreSqlText.textCopy + ); + + cy.get(postgreSqlSelector.linkReadDocumentation).verifyVisibleElement( + "have.text", + postgreSqlText.readDocumentation + ); + cy.get(postgreSqlSelector.buttonTestConnection) + .verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextTestConnection + ) + .click(); + cy.get(postgreSqlSelector.connectionFailedText) + .scrollIntoView() + .verifyVisibleElement("have.text", postgreSqlText.couldNotConnect); + cy.get(postgreSqlSelector.buttonSave).verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextSave + ); + // cy.get(dataSourceSelector.connectionAlertText).should("be.visible") + deleteDatasource(`cypress-${data.dataSourceName}-mariadb`); + }); + + it("Should verify the functionality of MariaDB connection form.", () => { + selectAndAddDataSource("databases", "MariaDB", data.dataSourceName); + + fillDataSourceTextField( + postgreSqlText.labelHost, + postgreSqlText.placeholderEnterHost, + Cypress.env("mariadb_host") + ); + // fillDataSourceTextField( + // postgreSqlText.labelPort, + // postgreSqlText.placeholderEnterPort, + // "5432" + // ); + + cy.get('[data-cy="label-port"]').verifyVisibleElement("have.text", "Port"); + cy.get('[data-cy="port-text-field"]') + .should("be.visible") + .invoke("attr", "placeholder") + .should("contain", "Enter port"); + + fillDataSourceTextField( + postgreSqlText.labelUserName, + postgreSqlText.placeholderEnterUserName, + Cypress.env("mariadb_user") + ); + fillDataSourceTextField( + postgreSqlText.labelPassword, + "**************", + Cypress.env("mariadb_password") + ); + + cy.get('[data-cy="label-database"]').verifyVisibleElement( + "have.text", + "Database" + ); + cy.get('[data-cy="database-text-field"]') + .should("be.visible") + .invoke("attr", "placeholder") + .should("contain", "Enter name of the database"); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + cy.get(postgreSqlSelector.buttonSave).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get( + `[data-cy="cypress-${data.dataSourceName}-mariadb-button"]` + ).verifyVisibleElement( + "have.text", + `cypress-${data.dataSourceName}-mariadb` + ); + + deleteDatasource(`cypress-${data.dataSourceName}-mariadb`); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/minioHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/minioHappyPath.cy.js new file mode 100644 index 0000000000..6572724e3d --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/minioHappyPath.cy.js @@ -0,0 +1,285 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { minioText } from "Texts/minio"; +import { minioSelectors } from "Selectors/Plugins"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; + +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; + +const data = {}; + +describe("Data source minio", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it("Should verify elements on minio connection form", () => { + const Host = Cypress.env("minio_host"); + const Port = Cypress.env("minio_port"); + const AccessKey = Cypress.env("minio_accesskey"); + const SecretKey = Cypress.env("minio_secretkey"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + selectAndAddDataSource("databases", minioText.minio, data.dsName); + + fillDataSourceTextField( + minioText.hostLabel, + minioText.hostInputPlaceholder, + Host + ); + + fillDataSourceTextField( + minioText.portLabel, + minioText.portPlaceholder, + Port + ); + + cy.get(`[${minioSelectors.sslToggle}]`).click(); + + fillDataSourceTextField( + minioText.labelAccesskey, + minioText.placeholderAccessKey, + AccessKey + ); + + fillDataSourceTextField( + minioText.labelSecretKey, + minioText.placeholderSecretKey, + SecretKey + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-minio`); + }); + + it("Should verify functionality of minio connection form", () => { + const Host = Cypress.env("minio_host"); + const Port = Cypress.env("minio_port"); + const AccessKey = Cypress.env("minio_accesskey"); + const SecretKey = Cypress.env("minio_secretkey"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + selectAndAddDataSource("databases", minioText.minio, data.dsName); + + fillDataSourceTextField( + minioText.hostLabel, + minioText.hostInputPlaceholder, + Host + ); + + fillDataSourceTextField( + minioText.portLabel, + minioText.portPlaceholder, + Port + ); + + cy.get(`[${minioSelectors.sslToggle}]`).click(); + + fillDataSourceTextField( + minioText.labelAccesskey, + minioText.placeholderAccessKey, + AccessKey + ); + + fillDataSourceTextField( + minioText.labelSecretKey, + minioText.placeholderSecretKey, + SecretKey + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-minio`); + }); + + it.skip("Should be able to run the query with a valid connection", () => { + const Host = Cypress.env("minio_host"); + const Port = Cypress.env("minio_port"); + const AccessKey = Cypress.env("minio_accesskey"); + const SecretKey = Cypress.env("minio_secretkey"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + selectAndAddDataSource("databases", minioText.minio, data.dsName); + + fillDataSourceTextField( + minioText.hostLabel, + minioText.hostInputPlaceholder, + Host + ); + + fillDataSourceTextField( + minioText.portLabel, + minioText.portPlaceholder, + Port + ); + + cy.get(`[${minioSelectors.sslToggle}]`).click(); + + fillDataSourceTextField( + minioText.labelAccesskey, + minioText.placeholderAccessKey, + AccessKey + ); + + fillDataSourceTextField( + minioText.labelSecretKey, + minioText.placeholderSecretKey, + SecretKey + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + const operationsMinio = [ + "List buckets", + "Put object", + "List objects in a bucket", + "Read object", + "Presigned url for download", + "Presigned url for upload", + "Remove object", + ]; + + operationsMinio.forEach((operation) => { + cy.get(".react-select__input") + .eq(1) + .type(`${operation}{enter}`, { force: true }); + + if (operation === "List objects in a bucket") { + cy.get(minioSelectors.bucketNameInputField).clearAndTypeOnCodeMirror( + minioText.bucketName + ); + } + if (operation === "Read object" || operation === "Remove object") { + cy.get(minioSelectors.bucketNameInputField).clearAndTypeOnCodeMirror( + minioText.bucketName + ); + cy.get(minioSelectors.objectNameInputField).clearAndTypeOnCodeMirror( + minioText.objectName + ); + } + if (operation === "Put object") { + cy.get(minioSelectors.bucketNameInputField).clearAndTypeOnCodeMirror( + minioText.bucketName + ); + cy.get(minioSelectors.objectNameInputField).clearAndTypeOnCodeMirror( + minioText.objectName + ); + cy.get(minioSelectors.contentTypeInputField).clearAndTypeOnCodeMirror( + '"string"' + ); + cy.get(minioSelectors.dataInput).clearAndTypeOnCodeMirror(`test`); + } + if ( + operation === "Presigned url for download" || + operation === "Presigned url for upload" + ) { + cy.get(minioSelectors.bucketNameInputField).clearAndTypeOnCodeMirror( + minioText.bucketName + ); + cy.get(minioSelectors.objectNameInputField).clearAndTypeOnCodeMirror( + minioText.objectName + ); + } + + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + }); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-minio` + ); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mongoDbHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mongoDbHappyPath.cy.skip.js new file mode 100644 index 0000000000..1da8b5c6d7 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mongoDbHappyPath.cy.skip.js @@ -0,0 +1,405 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { mongoDbText } from "Texts/mongoDb"; +import { commonSelectors } from "Selectors/common"; +import { dataSourceSelector } from "Selectors/dataSource"; +import { closeDSModal, deleteDatasource } from "Support/utils/dataSource"; +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; +import { + connectMongo, + openMongoQueryEditor, + selectQueryType, +} from "Support/utils/mongoDB"; + +import { + verifyCouldnotConnectWithAlert, + resizeQueryPanel, + query, + verifypreview, + addInput, +} from "Support/utils/dataSource"; + +const data = {}; + +describe("Data source MongoDB", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dataSourceName = fake.lastName + .toLowerCase() + .replaceAll("[^A-Za-z]", ""); + }); + + it("Should verify elements on MongoDB connection form", () => { + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-mongodb`, + "mongodb", + [ + { key: "database", value: "", encrypted: false }, + { key: "host", value: "localhost" }, + { key: "port", value: 27017 }, + { key: "username", value: "" }, + { key: "password", value: "", encrypted: true }, + { key: "connection_type", value: "manual" }, + { key: "connection_string", value: "", encrypted: true }, + { key: "tls_certificate", value: "none", encrypted: false }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-mongodb-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-mongodb` + ); + + cy.get(postgreSqlSelector.labelHost).verifyVisibleElement( + "have.text", + postgreSqlText.labelHost + ); + cy.get(postgreSqlSelector.labelPort).verifyVisibleElement( + "have.text", + postgreSqlText.labelPort + ); + cy.get(postgreSqlSelector.labelDbName).verifyVisibleElement( + "have.text", + "Database Name" + ); + cy.get(postgreSqlSelector.labelUserName).verifyVisibleElement( + "have.text", + postgreSqlText.labelUserName + ); + cy.get(postgreSqlSelector.labelPassword).verifyVisibleElement( + "have.text", + postgreSqlText.labelPassword + ); + cy.get(postgreSqlSelector.labelIpWhitelist).verifyVisibleElement( + "have.text", + postgreSqlText.whiteListIpText + ); + cy.get(postgreSqlSelector.buttonCopyIp).verifyVisibleElement( + "have.text", + postgreSqlText.textCopy + ); + + cy.get(postgreSqlSelector.linkReadDocumentation).verifyVisibleElement( + "have.text", + postgreSqlText.readDocumentation + ); + cy.get(postgreSqlSelector.buttonTestConnection) + .verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextTestConnection + ) + .click(); + cy.get(postgreSqlSelector.connectionFailedText, { + timeout: 70000, + }).verifyVisibleElement("have.text", postgreSqlText.couldNotConnect, { + timeout: 65000, + }); + cy.get(postgreSqlSelector.buttonSave).verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextSave + ); + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( + "have.text", + mongoDbText.errorConnectionRefused + ); + cy.get('[data-cy="connection-type-select-dropdown"]').type( + mongoDbText.optionConnectUsingConnectionString + ); + cy.get('[data-cy="label-connection-string"]').verifyVisibleElement( + "have.text", + mongoDbText.labelConnectionString + ); + cy.get(postgreSqlSelector.labelIpWhitelist).verifyVisibleElement( + "have.text", + postgreSqlText.whiteListIpText + ); + cy.get(postgreSqlSelector.buttonCopyIp).verifyVisibleElement( + "have.text", + postgreSqlText.textCopy + ); + + cy.get(postgreSqlSelector.linkReadDocumentation).verifyVisibleElement( + "have.text", + postgreSqlText.readDocumentation + ); + cy.get(postgreSqlSelector.buttonTestConnection) + .verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextTestConnection + ) + .click(); + cy.get(postgreSqlSelector.connectionFailedText, { + timeout: 70000, + }).verifyVisibleElement("have.text", postgreSqlText.couldNotConnect, { + timeout: 95000, + }); + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( + "have.text", + "Cannot read properties of null (reading '2')" + ); + verifyCouldnotConnectWithAlert(mongoDbText.errorInvalisScheme); + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + cy.wait(1000); + deleteDatasource(`cypress-${data.dataSourceName}-mongodb`); + }); + + it("Should verify the functionality of MongoDB connection form.", () => { + selectAndAddDataSource( + "databases", + mongoDbText.mongoDb, + data.dataSourceName + ); + + cy.get('[data-cy="connection-type-select-dropdown"]').type( + mongoDbText.optionConnectUsingConnectionString + ); + + fillDataSourceTextField( + mongoDbText.labelConnectionString, + "**************", + Cypress.env("mongodb_connString"), + "contain", + { parseSpecialCharSequences: false, delay: 0 } + ); + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + cy.get(postgreSqlSelector.buttonSave).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.get( + `[data-cy="cypress-${data.dataSourceName}-mongodb-button"]` + ).verifyVisibleElement( + "have.text", + `cypress-${data.dataSourceName}-mongodb` + ); + + deleteDatasource(`cypress-${data.dataSourceName}-mongodb`); + }); + + it.skip("Should verify the queries of MongoDB.", () => { + connectMongo(); + openMongoQueryEditor(); + resizeQueryPanel(); + + selectQueryType("Delete Many"); + addInput("collection", "test"); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + + selectQueryType("List Collections"); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview("raw", '[{"name":"test"'); //'root:[] 0 items' + + selectQueryType("Insert One"); + addInput("collection", "test"); + addInput("document", '{name:"mike"}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview("raw", '{"acknowledged":true,"insertedId"'); + + selectQueryType("Find One"); + addInput("collection", "test"); + addInput("filter", '{name:"mike"}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview("raw", '"name":"mike"}'); + + selectQueryType("Find many"); + addInput("collection", "test"); + addInput("filter", '{name:"mike"}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview("raw", '"name":"mike"}'); + + selectQueryType("Total Count"); + addInput("collection", "test"); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview("raw", '{"count":'); + + selectQueryType("Count"); + addInput("collection", "test"); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview("raw", '{"count":'); + + selectQueryType("Distinct"); + addInput("collection", "test"); + addInput("field", "name"); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview("raw", '["mike"]'); + + selectQueryType("Insert Many"); + addInput("collection", "test"); + addInput( + "documents", + '[{_id:331, name:"Nina"},{_id:441, name:"mina"}, {_id:4441, name:"Steph"}, {_id:41, name:"Mark"},{_id:3131, name:"Lina"}]' + ); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + addInput("documents", '[{_id:3113, name:"Nina"},{_id:414, name:"mina"}]'); + query("preview"); + verifypreview( + "raw", + '{"acknowledged":true,"insertedCount":2,"insertedIds":{"0":3113,"1":414}}' + ); + + selectQueryType("Update One"); + addInput("collection", "test"); + addInput("filter", '{name:"mina"}'); + addInput("update", '{$set:{name: "mike2023"}}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview( + "raw", + '{"acknowledged":true,"modifiedCount":1,"upsertedId":null,"upsertedCount":0' + ); + + selectQueryType("Update Many"); + addInput("collection", "test"); + addInput("filter", '{name:"Nina"}'); + addInput("update", '{$set:{name: "mike22222"}}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + addInput("filter", '{name:"mike22222"}'); + addInput("update", '{$set:{name: "Mark"}}'); + query("preview"); + verifypreview( + "raw", + '{"acknowledged":true,"modifiedCount":2,"upsertedId":null,"upsertedCount":0' + ); + + selectQueryType("Replace One"); + addInput("collection", "test"); + addInput("filter", '{name:"mike"}'); + addInput("replacement", '{name: "mike2023"}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + addInput("filter", '{name:"mike"}'); + addInput("replacement", '{name: "Nina"}'); + query("preview"); + verifypreview( + "raw", + '{"acknowledged":true,"modifiedCount":1,"upsertedId":null,"upsertedCount":0' + ); + + selectQueryType("Find One and Update"); + addInput("collection", "test"); + addInput("filter", '{name:"mike"}'); + addInput("update", '{$set:{name: "mike2023"}}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + addInput("filter", '{name:"Mark"}'); + addInput("update", '{$set:{name: "Nina"}}'); + query("preview"); + verifypreview( + "raw", + '{"lastErrorObject":{"n":1,"updatedExisting":true},"value":{"_id":' + ); + + selectQueryType("Find One and Replace"); + addInput("collection", "test"); + addInput("filter", '{name:"mike"}'); + addInput("replacement", '{name: "mike2023"}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + addInput("filter", '{name:"mike2023"}'); + addInput("replacement", '{name: "Nina"}'); + query("preview"); + verifypreview( + "raw", + '{"lastErrorObject":{"n":1,"updatedExisting":true},"value":{"_id":' + ); + + selectQueryType("Find One and Delete"); + addInput("collection", "test"); + addInput("filter", '{name:"Nina"}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + addInput("filter", '{name:"mike2023"}'); + query("preview"); + verifypreview("raw", '{"lastErrorObject":{"n":1},"value":{"_id":'); + + selectQueryType("Delete One"); + addInput("collection", "test"); + addInput("filter", '{name:"mike"}'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + addInput("filter", '{name:"Lina"}'); + query("preview"); + verifypreview("raw", '{"acknowledged":true,"deletedCount":1}'); + + selectQueryType("Aggregate"); + addInput("collection", "test"); + addInput("pipeline", '[{$match:{name:"mike2023"}}, {$match:{_id:414}}]'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview("raw", '[{"_id":414,"name":"mike2023"}]'); + + selectQueryType("Operations"); + addInput("collection", "test"); + addInput("operations", '[{insertOne:{name:"midhun"}}]'); + query("run"); + cy.verifyToastMessage(".go2072408551", "Query (mongodb1) completed."); + query("preview"); + verifypreview( + "raw", + '{"ok":1,"writeErrors":[],"writeConcernErrors":[],"insertedIds":[{"index":' + ); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mysqlHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mysqlHappyPath.cy.skip.js new file mode 100644 index 0000000000..d54e15c933 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mysqlHappyPath.cy.skip.js @@ -0,0 +1,506 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { mySqlText } from "Texts/mysql"; +import { commonSelectors } from "Selectors/common"; +import { commonWidgetText, commonText } from "Texts/common"; +import { + fillDataSourceTextField, + selectAndAddDataSource, + addQuery, + fillConnectionForm, + openQueryEditor, + selectQueryMode, + addGuiQuery, + addWidgetsToAddUser, +} from "Support/utils/postgreSql"; +import { + closeDSModal, + deleteDatasource, + verifyCouldnotConnectWithAlert, +} from "Support/utils/dataSource"; +import { dataSourceSelector } from "Selectors/dataSource"; +import { realHover } from "cypress-real-events/commands/realHover"; + +const data = {}; + +describe("Data sources MySql", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dataSourceName = fake.lastName + .toLowerCase() + .replaceAll("[^A-Za-z]", ""); + }); + + it("Should verify elements on MySQL connection form", () => { + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-mysql`, + "mysql", + [ + { key: "connection_type", value: "hostname" }, + { key: "host", value: "localhost" }, + { key: "port", value: 3306 }, + { key: "database", value: "" }, + { key: "socket", value: "", encrypted: false }, + { key: "username", value: "" }, + { key: "password", value: "", encrypted: true }, + { key: "ssl_enabled", value: false, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-mysql-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-mysql` + ); + + cy.get(postgreSqlSelector.labelHost).verifyVisibleElement( + "have.text", + postgreSqlText.labelHost + ); + cy.get(postgreSqlSelector.labelPort).verifyVisibleElement( + "have.text", + postgreSqlText.labelPort + ); + cy.get(postgreSqlSelector.labelSsl).verifyVisibleElement( + "have.text", + postgreSqlText.labelSSL + ); + cy.get(postgreSqlSelector.labelDbName).verifyVisibleElement( + "have.text", + postgreSqlText.labelDbName + ); + cy.get(postgreSqlSelector.labelUserName).verifyVisibleElement( + "have.text", + postgreSqlText.labelUserName + ); + cy.get(postgreSqlSelector.labelPassword).verifyVisibleElement( + "have.text", + postgreSqlText.labelPassword + ); + + cy.get(postgreSqlSelector.labelIpWhitelist).verifyVisibleElement( + "have.text", + postgreSqlText.whiteListIpText + ); + cy.get(postgreSqlSelector.buttonCopyIp).verifyVisibleElement( + "have.text", + postgreSqlText.textCopy + ); + + cy.get(postgreSqlSelector.linkReadDocumentation).verifyVisibleElement( + "have.text", + postgreSqlText.readDocumentation + ); + cy.get(postgreSqlSelector.buttonTestConnection) + .verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextTestConnection + ) + .click(); + cy.get(postgreSqlSelector.buttonSave).verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextSave + ); + verifyCouldnotConnectWithAlert(mySqlText.errorConnectionRefused); + deleteDatasource(`cypress-${data.dataSourceName}-mysql`); + }); + + it("Should verify the functionality of MySQL connection form.", () => { + selectAndAddDataSource("databases", "MySQL", data.dataSourceName); + + fillDataSourceTextField( + postgreSqlText.labelHost, + postgreSqlText.placeholderEnterHost, + Cypress.env("mysql_host") + ); + fillDataSourceTextField( + postgreSqlText.labelPort, + postgreSqlText.placeholderEnterPort, + "3318" + ); + fillDataSourceTextField( + postgreSqlText.labelDbName, + postgreSqlText.placeholderNameOfDB, + "unknowndb" + ); + fillDataSourceTextField( + postgreSqlText.labelUserName, + postgreSqlText.placeholderEnterUserName, + Cypress.env("mysql_user") + ); + fillDataSourceTextField( + postgreSqlText.labelPassword, + "**************", + Cypress.env("mysql_password") + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.wait(500); + verifyCouldnotConnectWithAlert(""); + fillDataSourceTextField( + postgreSqlText.labelDbName, + postgreSqlText.placeholderNameOfDB, + "test_db" + ); + fillDataSourceTextField( + postgreSqlText.labelUserName, + postgreSqlText.placeholderEnterUserName, + "admin1" + ); + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.wait(500); + verifyCouldnotConnectWithAlert( + "ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client" + ); + + fillDataSourceTextField( + postgreSqlText.labelUserName, + postgreSqlText.placeholderEnterUserName, + Cypress.env("mysql_user") + ); + cy.get(postgreSqlSelector.passwordTextField).type("testpassword"); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.wait(500); + verifyCouldnotConnectWithAlert( + "ER_ACCESS_DENIED_ERROR: Access denied for user 'root'@'103.171.99.42' (using password: YES)" + ); + cy.get('[data-cy="ssl-enabled-toggle-input"]').then(($el) => { + if ($el.is(":checked")) { + cy.get('[data-cy="ssl-enabled-toggle-input"]').uncheck(); + } + }); + + fillDataSourceTextField( + postgreSqlText.labelPassword, + "**************", + Cypress.env("mysql_password") + ); + + // cy.get(postgreSqlSelector.passwordTextField).should("be.visible"); + // cy.get(".datasource-edit-btn").should("be.visible").click(); + // cy.get(postgreSqlSelector.passwordTextField).type( + // `{selectAll}{backspace}${Cypress.env("mysql_password")}`, + // { log: false } + // ); + cy.get(postgreSqlSelector.buttonTestConnection).click(); + + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + cy.get(postgreSqlSelector.buttonSave).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.get( + `[data-cy="cypress-${data.dataSourceName}-mysql-button"]` + ).verifyVisibleElement("have.text", `cypress-${data.dataSourceName}-mysql`); + + deleteDatasource(`cypress-${data.dataSourceName}-mysql`); + }); + + it.skip("Should verify elements of the Query section.", () => { + cy.viewport(1200, 1300); + selectAndAddDataSource("databases", "MySQL", data.dataSourceName); + fillConnectionForm({ + Host: Cypress.env("mysql_host"), + Port: Cypress.env("mysql_port"), + "Database name": "test_db", + Username: Cypress.env("mysql_user"), + Password: Cypress.env("mysql_password"), + }); + + cy.get('[class="query-pane"]').invoke("css", "height", "calc(85%)"); + openQueryEditor("MySQL"); + // cy.get('[class="query-pane"]').invoke("css", "height", "calc(95%)"); + + // cy.get(postgreSqlSelector.addQueriesCard) + // .verifyVisibleElement("contain", mySqlText.cypressMySql) + // .click(); + + // cy.get(postgreSqlSelector.queryTabGeneral).verifyVisibleElement( + // "contain", + // postgreSqlText.tabGeneral + // ); + // cy.get(postgreSqlSelector.queryLabelInputField).verifyVisibleElement( + // "have.value", + // postgreSqlText.firstQueryName + // ); + // cy.get(postgreSqlSelector.queryPreviewButton).verifyVisibleElement( + // "have.text", + // postgreSqlText.buttonLabelPreview + // ); + // cy.get(postgreSqlSelector.queryCreateAndRunButton).verifyVisibleElement( + // "have.text", + // postgreSqlText.buttonLabelCreateAndRun + // ); + + // cy.get(postgreSqlSelector.queryCreateDropdown).should("be.visible").click(); + // cy.get(postgreSqlSelector.queryCreateAndRunOption).verifyVisibleElement( + // "have.text", + // postgreSqlText.buttonLabelCreateAndRun + // ); + // cy.get(postgreSqlSelector.queryCreateOption) + // .verifyVisibleElement("have.text", postgreSqlText.buttonLabelCreate) + // .click(); + + // cy.get(postgreSqlSelector.queryCreateAndRunButton).verifyVisibleElement( + // "have.text", + // postgreSqlText.buttonLabelCreate + // ); + + cy.get('[class="query-pane"]').invoke("css", "height", "calc(85%)"); + + cy.get(`${postgreSqlSelector.querySelectDropdown}:eq(0)`) + .scrollIntoView() + .should("be.visible") + .click(); + cy.contains("[id*=react-select-]", postgreSqlText.queryModeSql).should( + "have.text", + postgreSqlText.queryModeSql + ); + cy.contains("[id*=react-select-]", postgreSqlText.queryModeGui).should( + "have.text", + postgreSqlText.queryModeGui + ); + + cy.get(postgreSqlSelector.queryCreateAndRunButton) + .should("be.visible") + .click(); + // cy.get('[data-cy="list-query-mysql1"]').should("be.visible").click(); + + cy.get(postgreSqlSelector.labelTransformation) + .scrollIntoView() + .verifyVisibleElement("have.text", postgreSqlText.headerTransformations); + cy.wait(200); + cy.get(postgreSqlSelector.toggleTransformation).parent().click(); + cy.get(postgreSqlSelector.inputFieldTransformation).should("be.visible"); + cy.get(postgreSqlSelector.toggleTransformation).parent().click(); + + cy.get(postgreSqlSelector.headerQueryPreview).verifyVisibleElement( + "have.text", + postgreSqlText.buttonLabelPreview + ); + cy.get(postgreSqlSelector.previewTabJson).verifyVisibleElement( + "have.text", + postgreSqlText.json + ); + cy.get(postgreSqlSelector.previewTabRaw).verifyVisibleElement( + "have.text", + postgreSqlText.raw + ); + + selectQueryMode(postgreSqlText.queryModeGui, "4"); + cy.get(postgreSqlSelector.operationsDropDownLabel).verifyVisibleElement( + "have.text", + postgreSqlText.labelOperation + ); + cy.get(`${postgreSqlSelector.querySelectDropdown}:eq(1)`).click(); + cy.contains('[id*="react-select-10"]', postgreSqlText.guiOptionBulkUpdate) + .should("have.text", postgreSqlText.guiOptionBulkUpdate) + .click(); + + cy.get(postgreSqlSelector.labelTableNameInputField).verifyVisibleElement( + "have.text", + postgreSqlText.labelTable + ); + cy.get(postgreSqlSelector.labelPrimaryKeyColoumn).verifyVisibleElement( + "have.text", + postgreSqlText.labelPrimaryKeyColumn + ); + cy.get('[data-cy="label-records"]').verifyVisibleElement( + "have.text", + "Records" + ); + + // cy.get(postgreSqlSelector.queryTabAdvanced) + // .verifyVisibleElement("contain", postgreSqlText.tabAdvanced) + // .click(); + + cy.get(postgreSqlSelector.labelRunQueryOnPageLoad).verifyVisibleElement( + "have.text", + postgreSqlText.toggleLabelRunOnPageLoad + ); + cy.get( + postgreSqlSelector.labelRequestConfirmationOnRun + ).verifyVisibleElement("have.text", postgreSqlText.toggleLabelconfirmation); + cy.get(postgreSqlSelector.labelShowNotification).verifyVisibleElement( + "have.text", + postgreSqlText.toggleLabelShowNotification + ); + + cy.get(postgreSqlSelector.toggleNotification).parent().click(); + cy.get(postgreSqlSelector.labelSuccessMessageInput).verifyVisibleElement( + "have.text", + postgreSqlText.labelSuccessMessage + ); + cy.get(postgreSqlSelector.notificationDurationInput).verifyVisibleElement( + "have.text", + postgreSqlText.labelNotificatioDuration + ); + cy.get(postgreSqlSelector.addEventHandler).verifyVisibleElement( + "have.text", + commonWidgetText.addEventHandlerLink + ); + cy.get(postgreSqlSelector.noEventHandlerMessage).verifyVisibleElement( + "have.text", + postgreSqlText.labelNoEventhandler + ); + + cy.get('[data-cy="list-query-mysql1"]').verifyVisibleElement( + "have.text", + "mysql1" + ); + cy.get('[class="row query-row query-row-selected"]') + .realHover() + .then(() => { + cy.get('[data-cy="delete-query-mysql1"]').click(); + }); + cy.get(postgreSqlSelector.deleteModalMessage).verifyVisibleElement( + "have.text", + postgreSqlText.dialogueTextDelete + ); + cy.get(postgreSqlSelector.deleteModalCancelButton).verifyVisibleElement( + "have.text", + postgreSqlText.cancel + ); + cy.get(postgreSqlSelector.deleteModalConfirmButton) + .verifyVisibleElement("have.text", postgreSqlText.yes) + .click(); + }); + + it.skip("Should verify CRUD operations on SQL Query.", () => { + let dbName = "7mmplik"; + selectAndAddDataSource("databases", "MySQL", data.dataSourceName); + + cy.clearAndType( + postgreSqlSelector.dataSourceNameInputField, + mySqlText.cypressMySql + ); + cy.get('[class="query-pane"]').invoke("css", "height", "calc(85%)"); + + cy.intercept("GET", "api/data_sources?**").as("datasource"); + fillConnectionForm({ + Host: Cypress.env("mysql_host"), + Port: Cypress.env("mysql_port"), + "Database name": "test_db", + Username: Cypress.env("mysql_user"), + Password: Cypress.env("mysql_password"), + }); + cy.wait("@datasource"); + + addQuery( + "table_creation", + `CREATE TABLE ${dbName} (id MEDIUMINT NOT NULL AUTO_INCREMENT, name CHAR(30) NOT NULL,email VARCHAR(255),PRIMARY KEY (id));`, + mySqlText.cypressMySql + ); + + addQuery( + "table_preview", + `SELECT * FROM ${dbName}`, + mySqlText.cypressMySql + ); + + addQuery( + "existance_of_table", + `SHOW TABLES LIKE '${dbName}';`, + mySqlText.cypressMySql + ); + + cy.get(postgreSqlSelector.queryPreviewButton, { timeout: 3000 }).click(); + cy.get('[class="tab-pane active"]', { timeout: 3000 }).should("be.visible"); + cy.get(postgreSqlSelector.previewTabRaw, { timeout: 3000 }) + .scrollIntoView() + .should("be.visible", { timeout: 3000 }) + .click(); + + cy.get(".p-3").should( + "have.text", + `[{"Tables_in_test_db (${dbName})":"${dbName}"}]` + ); + + // addQuery( + // "add_data_using_widgets", + // `INSERT INTO "public"."cypress_test_users"("name", "email") VALUES('{{components.textinput1.value{rightArrow}{rightArrow}', '{{}{{}components.textinput2.value{rightArrow}{rightArrow}') RETURNING "id", "name", "email";`, + // mySqlText.cypressMySql + // ); + + addQuery( + "truncate_table", + `TRUNCATE TABLE ${dbName}`, + mySqlText.cypressMySql + ); + + cy.get(postgreSqlSelector.queryPreviewButton).click(); + cy.get('[class="tab-pane active"]', { timeout: 3000 }).should("be.visible"); + cy.get(postgreSqlSelector.previewTabRaw).click(); + cy.get('[class="tab-pane active"]').should( + "have.text", + `{"fieldCount":0,"affectedRows":0,"insertId":0,"serverStatus":2,"warningCount":0,"message":"","protocol41":true,"changedRows":0}` + ); + + addQuery("drop_table", `DROP TABLE ${dbName}`, mySqlText.cypressMySql); + cy.get('[data-cy="list-query-existance_of_table"]').click(); + cy.get(postgreSqlSelector.queryPreviewButton).click(); + cy.get('[class="tab-pane active"]', { timeout: 3000 }).should("be.visible"); + cy.get(postgreSqlSelector.previewTabRaw).click(); + cy.get('[class="tab-pane active"]').should("have.text", "[]"); + + // addWidgetsToAddUser(); + }); + + it.skip("Should verify bulk update", () => { + selectAndAddDataSource("databases", "MySQL", data.dataSourceName); + cy.clearAndType( + postgreSqlSelector.dataSourceNameInputField, + mySqlText.cypressMySql + ); + fillConnectionForm({ + Host: Cypress.env("mysql_host"), + Port: "3318", + "Database name": "test_db", + Username: Cypress.env("mysql_user"), + Password: Cypress.env("mysql_password"), + }); + + openQueryEditor(mySqlText.cypressMySql); + cy.get('[class="query-pane"]').invoke("css", "height", "calc(85%)"); + selectQueryMode(postgreSqlText.queryModeGui); + addGuiQuery("name", "email"); + cy.get(postgreSqlSelector.queryCreateAndRunButton).click(); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/oracleDbHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/oracleDbHappyPath.cy.skip.js index c42bda600b..0cd7ea4223 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/oracleDbHappyPath.cy.skip.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/oracleDbHappyPath.cy.skip.js @@ -15,7 +15,7 @@ import { describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); // cy.createApp(); }); @@ -123,7 +123,7 @@ describe("Data sources", () => { ); cy.clearAndType( - '[data-cy="data-source-name-input-filed"]', + '[data-cy="data-source-name-input-field"]', postgreSqlText.psqlName ); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/postgresHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/postgresHappyPath.cy.js index 26ca6a8f21..962baeb991 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/postgresHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/postgresHappyPath.cy.js @@ -3,6 +3,7 @@ import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; import { commonWidgetText, commonText } from "Texts/common"; import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { addQuery, fillDataSourceTextField, @@ -19,13 +20,14 @@ const data = {}; describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); }); - it("Should verify elements on connection form", () => { + it("Should verify elements on connection form with validation", () => { cy.log(process.env.NODE_ENV); cy.log(postgreSqlText.allDatabase()); cy.get(commonSelectors.globalDataSourceIcon).click(); @@ -52,36 +54,174 @@ describe("Data sources", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource( - "databases", - postgreSqlText.postgreSQL, - data.dataSourceName + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-postgresql`, + "postgresql", + [ + { key: "connection_type", value: "manual", encrypted: false }, + { key: "host", value: "localhost", encrypted: false }, + { key: "port", value: 5432, encrypted: false }, + { key: "ssl_enabled", value: true, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + { key: "password", value: null, encrypted: true }, + { key: "ca_cert", value: null, encrypted: true }, + { key: "client_key", value: null, encrypted: true }, + { key: "client_cert", value: null, encrypted: true }, + { key: "root_cert", value: null, encrypted: true }, + { key: "connection_string", value: null, encrypted: true }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-postgresql-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-postgresql` ); - cy.get(postgreSqlSelector.labelHost).verifyVisibleElement( + cy.get( + dataSourceSelector.dropdownLabel(postgreSqlText.labelConnectionType) + ).verifyVisibleElement("have.text", postgreSqlText.labelConnectionType); + cy.get(dataSourceSelector.dropdownField(postgreSqlText.labelConnectionType)) + .should("be.visible") + .click(); + cy.contains( + `[id*="react-select-"]`, + postgreSqlText.connectionStringOption + ).click(); + + cy.get( + dataSourceSelector.dropdownField(postgreSqlText.labelConnectionType) + ).should("be.visible"); + cy.get( + dataSourceSelector.labelFieldName(postgreSqlText.connectionStringOption) + ).verifyVisibleElement( "have.text", - postgreSqlText.labelHost + `${postgreSqlText.connectionStringOption}*` ); - cy.get(postgreSqlSelector.labelPort).verifyVisibleElement( + cy.get(postgreSqlSelector.labelEncryptedText).verifyVisibleElement( "have.text", - postgreSqlText.labelPort + postgreSqlText.labelEncrypted ); + cy.get(dataSourceSelector.button(postgreSqlText.editButtonText)).should( + "be.visible" + ); + cy.get(dataSourceSelector.button(postgreSqlText.editButtonText)).click(); + cy.verifyRequiredFieldValidation( + postgreSqlText.connectionStringOption, + "rgb(226, 99, 103)" + ); + cy.get( + dataSourceSelector.textField(postgreSqlText.connectionStringOption) + ).should("be.visible"); + cy.get(postgreSqlSelector.labelIpWhitelist).verifyVisibleElement( + "have.text", + postgreSqlText.whiteListIpText + ); + cy.get(postgreSqlSelector.buttonCopyIp).verifyVisibleElement( + "have.text", + postgreSqlText.textCopy + ); + + cy.get(postgreSqlSelector.linkReadDocumentation).verifyVisibleElement( + "have.text", + postgreSqlText.readDocumentation + ); + cy.get(postgreSqlSelector.buttonTestConnection) + .verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextTestConnection + ) + .click(); + cy.get(postgreSqlSelector.connectionFailedText).verifyVisibleElement( + "have.text", + postgreSqlText.couldNotConnect + ); + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .and("be.disabled"); + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( + "have.text", + postgreSqlText.unableAcquireConnectionAlertText + ); + + cy.get(dataSourceSelector.dropdownField(postgreSqlText.labelConnectionType)) + .should("be.visible") + .click(); + cy.contains( + `[id*="react-select-"]`, + postgreSqlText.manualConnectionOption + ).click(); + + cy.get( + dataSourceSelector.dropdownField(postgreSqlText.labelConnectionType) + ).should("be.visible"); + + const requiredFields = [ + postgreSqlText.labelHost, + postgreSqlText.labelPort, + postgreSqlText.labelUserName, + postgreSqlText.labelPassword, + ]; + const sections = [ + postgreSqlText.labelHost, + postgreSqlText.labelPort, + postgreSqlText.labelDbName, + postgreSqlText.labelUserName, + postgreSqlText.labelPassword, + postgreSqlText.labelConnectionOptions, + ]; + sections.forEach((section) => { + if (section === postgreSqlText.labelConnectionOptions) { + cy.get(dataSourceSelector.keyInputField(section, 0)).should( + "be.visible" + ); + cy.get(dataSourceSelector.valueInputField(section, 0)).should( + "be.visible" + ); + cy.get(dataSourceSelector.deleteButton(section, 0)).should( + "be.visible" + ); + cy.get(dataSourceSelector.addMoreButton(section)).should("be.visible"); + } else if (requiredFields.includes(section)) { + cy.get(dataSourceSelector.labelFieldName(section)).verifyVisibleElement( + "have.text", + `${section}*` + ); + cy.get(dataSourceSelector.textField(section)).should("be.visible"); + if (section === postgreSqlText.labelPassword) { + cy.get( + dataSourceSelector.button(postgreSqlText.editButtonText) + ).click(); + cy.verifyRequiredFieldValidation(section, "rgb(215, 45, 57)"); + } else { + cy.get(dataSourceSelector.textField(section)).click(); + cy.get(commonSelectors.textField(section)).should( + "have.css", + "border-color", + "rgba(0, 0, 0, 0)" + ); + cy.get(dataSourceSelector.textField(section)) + .type("123") + .clear() + .blur(); + cy.verifyRequiredFieldValidation(section, "rgb(215, 45, 57)"); + } + } else { + cy.get(dataSourceSelector.labelFieldName(section)).verifyVisibleElement( + "have.text", + section + ); + cy.get(dataSourceSelector.textField(section)).should("be.visible"); + } + }); cy.get(postgreSqlSelector.labelSsl).verifyVisibleElement( "have.text", postgreSqlText.labelSSL ); - cy.get(postgreSqlSelector.labelDbName).verifyVisibleElement( - "have.text", - postgreSqlText.labelDbName - ); - cy.get(postgreSqlSelector.labelUserName).verifyVisibleElement( - "have.text", - postgreSqlText.labelUserName - ); - cy.get(postgreSqlSelector.labelPassword).verifyVisibleElement( - "have.text", - postgreSqlText.labelPassword - ); + cy.get(postgreSqlSelector.sslToggleInput).should("be.visible"); cy.get(postgreSqlSelector.labelSSLCertificate).verifyVisibleElement( "have.text", postgreSqlText.sslCertificate @@ -109,72 +249,85 @@ describe("Data sources", () => { "have.text", postgreSqlText.couldNotConnect ); - cy.get(postgreSqlSelector.buttonSave).verifyVisibleElement( + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .and("be.disabled"); + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( "have.text", - postgreSqlText.buttonTextSave + postgreSqlText.serverNotSuppotSsl ); - cy.get('[data-cy="connection-alert-text"]').should("be.visible"); - deleteDatasource(`cypress-${data.dataSourceName}-postgresql`); + + cy.apiDeleteGDS(`cypress-${data.dataSourceName}-postgresql`); }); it("Should verify the functionality of PostgreSQL connection form.", () => { - selectAndAddDataSource( - "databases", - postgreSqlText.postgreSQL, - data.dataSourceName + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-manual-pgsql`, + "postgresql", + [ + { key: "connection_type", value: "manual", encrypted: false }, + { key: "host", value: `${Cypress.env("pg_host")}`, encrypted: false }, + { key: "port", value: 5432, encrypted: false }, + { key: "ssl_enabled", value: false, encrypted: false }, + { key: "database", value: "postgres", encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + { + key: "username", + value: `${Cypress.env("pg_user")}`, + encrypted: false, + }, + { + key: "password", + value: `${Cypress.env("pg_password")}`, + encrypted: true, + }, + { key: "ca_cert", value: null, encrypted: true }, + { key: "client_key", value: null, encrypted: true }, + { key: "client_cert", value: null, encrypted: true }, + { key: "root_cert", value: null, encrypted: true }, + { key: "connection_string", value: null, encrypted: true }, + ] ); - - fillDataSourceTextField( - postgreSqlText.labelHost, - postgreSqlText.placeholderEnterHost, - Cypress.env("pg_host") - ); - fillDataSourceTextField( - postgreSqlText.labelPort, - postgreSqlText.placeholderEnterPort, - "5432" - ); - cy.get('[data-cy="-toggle-input"]').then(($el) => { - if ($el.is(":checked")) { - cy.get('[data-cy="-toggle-input"]').uncheck(); - } - }); - fillDataSourceTextField( - postgreSqlText.labelDbName, - postgreSqlText.placeholderNameOfDB, - "postgres" - ); - fillDataSourceTextField( - postgreSqlText.labelUserName, - postgreSqlText.placeholderEnterUserName, - "postgres" - ); - fillDataSourceTextField( - postgreSqlText.labelPassword, - "**************", - Cypress.env("pg_password") - ); - + cy.get( + dataSourceSelector.dataSourceNameButton( + `cypress-${data.dataSourceName}-manual-pgsql` + ) + ) + .should("be.visible") + .click(); cy.get(postgreSqlSelector.buttonTestConnection).click(); cy.get(postgreSqlSelector.textConnectionVerified, { timeout: 10000, }).should("have.text", postgreSqlText.labelConnectionVerified); - cy.get(postgreSqlSelector.buttonSave).click(); - - cy.verifyToastMessage( - commonSelectors.toastMessage, - postgreSqlText.toastDSSaved + cy.apiDeleteGDS(`cypress-${data.dataSourceName}-manual-pgsql`); + cy.reload(); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-string-pgsql`, + "postgresql", + [ + { key: "connection_type", value: "string", encrypted: false }, + { + key: "connection_string", + value: `${Cypress.env("pg_string")}`, + encrypted: true, + }, + ] ); - - cy.get(commonSelectors.globalDataSourceIcon).click(); cy.get( - `[data-cy="cypress-${data.dataSourceName}-postgresql-button"]` - ).verifyVisibleElement( - "have.text", - `cypress-${data.dataSourceName}-postgresql` - ); - - deleteDatasource(`cypress-${data.dataSourceName}-postgresql`); + dataSourceSelector.dataSourceNameButton( + `cypress-${data.dataSourceName}-string-pgsql` + ) + ) + .should("be.visible") + .click(); + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + cy.apiDeleteGDS(`cypress-${data.dataSourceName}-string-pgsql`); }); it.skip("Should verify elements of the Query section.", () => { diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/redisHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/redisHappyPath.cy.js index 6f4ff3baaa..7e4ed90b8d 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/redisHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/redisHappyPath.cy.js @@ -3,7 +3,7 @@ import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; import { redisText } from "Texts/redis"; import { commonSelectors } from "Selectors/common"; -import { commonText } from "Texts/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { fillDataSourceTextField, @@ -11,26 +11,22 @@ import { } from "Support/utils/postgreSql"; import { - verifyCouldnotConnectWithAlert, deleteDatasource, closeDSModal, - addQuery, addDsAndAddQuery, + deleteAppandDatasourceAfterExecution, } from "Support/utils/dataSource"; -import { openQueryEditor } from "Support/utils/dataSource"; -import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; - const data = {}; data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); describe("Data source Redis", () => { beforeEach(() => { - cy.appUILogin(); - cy.intercept("POST", "/api/data_queries").as("createQuery"); + cy.apiLogin(); + cy.visit("/"); }); - it("Should verify elements on connection Redison form", () => { + it("Should verify elements on connection Redis form", () => { cy.get(commonSelectors.globalDataSourceIcon).click(); closeDSModal(); @@ -100,7 +96,7 @@ describe("Data source Redis", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", redisText.errorMaxRetries ); @@ -114,7 +110,7 @@ describe("Data source Redis", () => { deleteDatasource(`cypress-${data.dsName}-redis`); }); - it("Should verify the functionality of Redis connection form.", () => { + it("Should verify the functionality of Redis connection form", () => { selectAndAddDataSource("databases", redisText.redis, data.dsName); fillDataSourceTextField( @@ -141,9 +137,9 @@ describe("Data source Redis", () => { ); cy.get(postgreSqlSelector.buttonTestConnection).click(); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", - "WRONGPASS invalid username-password pair or user is disabled." + redisText.errorInvalidUserOrPassword ); fillDataSourceTextField( postgreSqlText.labelHost, @@ -156,7 +152,7 @@ describe("Data source Redis", () => { "108299" ); cy.get(postgreSqlSelector.buttonTestConnection).click(); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", redisText.errorPort ); @@ -174,9 +170,9 @@ describe("Data source Redis", () => { ); cy.get(postgreSqlSelector.buttonTestConnection).click(); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", - "WRONGPASS invalid username-password pair or user is disabled." + redisText.errorInvalidUserOrPassword ); fillDataSourceTextField( @@ -191,9 +187,9 @@ describe("Data source Redis", () => { "redis" ); cy.get(postgreSqlSelector.buttonTestConnection).click(); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", - "WRONGPASS invalid username-password pair or user is disabled." + redisText.errorInvalidUserOrPassword ); fillDataSourceTextField( @@ -219,7 +215,7 @@ describe("Data source Redis", () => { deleteDatasource(`cypress-${data.dsName}-redis`); }); - it("Should able to run the query with valid conection", () => { + it.skip("Should able to run the query with valid conection", () => { selectAndAddDataSource("databases", redisText.redis, data.dsName); fillDataSourceTextField( @@ -256,5 +252,9 @@ describe("Data source Redis", () => { cy.skipWalkthrough(); addDsAndAddQuery("redis", `TIME`, `cypress-${data.dsName}-redis`); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-redis` + ); }); }); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/restAPIHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/restAPIHappyPath.cy.js new file mode 100644 index 0000000000..ab3d3993a3 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/restAPIHappyPath.cy.js @@ -0,0 +1,792 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors } from "Selectors/common"; +import { closeDSModal, deleteDatasource } from "Support/utils/dataSource"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { restAPISelector } from "Selectors/restAPI"; +import { restAPIText } from "Texts/restAPI"; +import { createAndRunRestAPIQuery } from "Support/utils/restAPI"; + +const data = {}; +const authenticationDropdownSelector = + ".dynamic-form-element > .css-nwhe5y-container > .react-select__control"; +const grantTypeDropdown = + ":nth-child(1) > :nth-child(2) > .react-select__control"; +const addAccessTokenDropdown = + ":nth-child(9) > .css-nwhe5y-container > .react-select__control"; +const clientAuthenticationDropdown = + ":nth-child(14) > .css-nwhe5y-container > .react-select__control"; + +describe("Data source Rest API", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dataSourceName = fake.lastName + .toLowerCase() + .replaceAll("[^A-Za-z]", ""); + }); + it("Should verify elements on Rest API connection form", () => { + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-restapi`, + "restapi", + [ + { key: "url", value: "" }, + { key: "auth_type", value: "none" }, + { key: "grant_type", value: "authorization_code" }, + { key: "add_token_to", value: "header" }, + { key: "header_prefix", value: "Bearer " }, + { key: "access_token_url", value: "" }, + { key: "client_id", value: "" }, + { key: "client_secret", value: "", encrypted: true }, + { key: "audience", value: "" }, + { key: "scopes", value: "read, write" }, + { key: "username", value: "", encrypted: false }, + { key: "password", value: "", encrypted: true }, + { key: "bearer_token", value: "", encrypted: true }, + { key: "auth_url", value: "" }, + { key: "client_auth", value: "header" }, + { key: "headers", value: [["", ""]] }, + { key: "custom_query_params", value: [["", ""]], encrypted: false }, + { key: "custom_auth_params", value: [["", ""]] }, + { + key: "access_token_custom_headers", + value: [["", ""]], + encrypted: false, + }, + { key: "multiple_auth_enabled", value: false, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + { key: "retry_network_errors", value: true, encrypted: false }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-restapi-button"]`) + .should("be.visible") + .click(); + cy.get(restAPISelector.inputField("data-source-name")).should( + "have.value", + `cypress-${data.dataSourceName}-restapi` + ); + cy.get(restAPISelector.subHeaderLabel(restAPIText.credentialsText)).should( + "have.text", + restAPIText.credentialsText + ); + const sections = [ + restAPIText.baseUrlLabel, + restAPIText.headersLabel, + restAPIText.urlParametesLabel, + restAPIText.bodyLabel, + restAPIText.cookiesLabel, + ]; + + sections.forEach((section) => { + cy.get(restAPISelector.subHeaderLabel(section)).should( + "have.text", + section + ); + + if (section !== restAPIText.baseUrlLabel) { + cy.get(restAPISelector.keyInputField(section, 0)).should("be.visible"); + cy.get(restAPISelector.valueInputField(section, 0)).should( + "be.visible" + ); + cy.get(restAPISelector.deleteButton(section, 0)).should("be.visible"); + cy.get(restAPISelector.addMoreButton(section)).should("be.visible"); + } else { + cy.get('[data-cy="base-url-text-field"]').should("be.visible"); + } + }); + + cy.get( + restAPISelector.subHeaderLabel(restAPIText.authenticationText) + ).should("have.text", restAPIText.authenticationText); + cy.get( + restAPISelector.subHeaderLabel(restAPIText.authenticationTypeLabel) + ).should("have.text", restAPIText.authenticationTypeLabel); + + cy.get(authenticationDropdownSelector).click(); + cy.contains( + `[id*="react-select-"]`, + restAPIText.basicAuth.basicText + ).click(); + cy.get(authenticationDropdownSelector).should( + "have.text", + restAPIText.basicAuth.basicText + ); + + cy.get( + restAPISelector.subHeaderLabel(restAPIText.basicAuth.usernameLabel) + ).should("have.text", restAPIText.basicAuth.usernameLabel); + cy.get( + restAPISelector.inputField(restAPIText.basicAuth.usernameLabel) + ).should("be.visible"); + cy.get( + restAPISelector.subHeaderLabel(restAPIText.basicAuth.passwordLabel) + ).should("have.text", restAPIText.basicAuth.passwordLabel); + cy.get(restAPISelector.button(restAPIText.editButtonText)).should( + "be.visible" + ); + cy.get( + restAPISelector.inputField(restAPIText.basicAuth.passwordLabel) + ).should("be.visible"); + + cy.get(authenticationDropdownSelector).click(); + cy.contains(`[id*="react-select-"]`, restAPIText.bearerAuth.bearerText) + .should("be.visible") + .click(); + cy.get(authenticationDropdownSelector).should( + "have.text", + restAPIText.bearerAuth.bearerText + ); + cy.get( + restAPISelector.subHeaderLabel(restAPIText.bearerAuth.tokenLabel) + ).should("have.text", restAPIText.bearerAuth.tokenLabel); + cy.get( + restAPISelector.inputField(restAPIText.bearerAuth.tokenLabel) + ).should("be.visible"); + + cy.get(authenticationDropdownSelector).click(); + cy.contains(`[id*="react-select-"]`, restAPIText.oAuthText).click(); + cy.get(authenticationDropdownSelector).should( + "have.text", + restAPIText.oAuthText + ); + cy.get(restAPISelector.subHeaderLabel(restAPIText.grantTypeLabel)).should( + "have.text", + restAPIText.grantTypeLabel + ); + cy.get(grantTypeDropdown).click(); + cy.contains( + `[id*="react-select-"]`, + restAPIText.authorizationCode.authorizationCodeLabel + ) + .should("be.visible") + .click(); + cy.get(grantTypeDropdown).should( + "contain", + restAPIText.authorizationCode.authorizationCodeLabel + ); + cy.get( + restAPISelector.inputField( + restAPIText.authorizationCode.headerPrefixLabel + ) + ).should(($input) => { + expect($input.val().trim()).to.equal(restAPIText.bearerAuth.bearerText); + }); + cy.get( + restAPISelector.inputField( + restAPIText.authorizationCode.accessTokenURLLabel + ) + ) + .invoke("attr", "placeholder") + .should("eq", "https://api.example.com/oauth/token"); + cy.get( + restAPISelector.inputField(restAPIText.authorizationCode.clientIDLabel) + ).should("be.visible"); + cy.get(restAPISelector.button(restAPIText.editButtonText)).should( + "be.visible" + ); + cy.get( + restAPISelector.inputField( + restAPIText.authorizationCode.clientSecretLabel + ) + ).should("be.visible"); + Object.entries(restAPIText.authorizationCode).forEach(([key, value]) => { + if ( + key !== "authorizationCodeLabel" && + key !== "requestHeader" && + key !== "sendBasicAuthheaderOption" && + key !== "sendClientCredentialsBodyOption" + ) { + cy.get( + restAPISelector.subHeaderLabel(restAPIText.authorizationCode[key]) + ).should("have.text", value); + } + }); + cy.get(addAccessTokenDropdown) + .should("be.visible") + .and("contain", restAPIText.authorizationCode.requestHeader); + cy.get( + restAPISelector.inputField(restAPIText.authorizationCode.scopeLabel) + ).should("be.visible"); + + const authorizationCodeSections = [ + restAPIText.authorizationCode.accessTokenURLCustomHeadersLabel, + restAPIText.authorizationCode.customQueryParametersLabel, + restAPIText.authorizationCode.customAuthenticationParametersLabel, + ]; + authorizationCodeSections.forEach((authorizationCodeSections) => { + cy.get( + restAPISelector.subHeaderLabel(authorizationCodeSections) + ).verifyVisibleElement("have.text", authorizationCodeSections); + if (authorizationCodeSections !== restAPIText.baseUrlLabel) { + cy.get( + restAPISelector.keyInputField(authorizationCodeSections, 0) + ).should("be.visible"); + cy.get( + restAPISelector.valueInputField(authorizationCodeSections, 0) + ).should("be.visible"); + cy.get( + restAPISelector.deleteButton(authorizationCodeSections, 0) + ).should("be.visible"); + cy.get(restAPISelector.addMoreButton(authorizationCodeSections)).should( + "be.visible" + ); + } else { + cy.get('[data-cy="base-url-text-field"]').should("be.visible"); + } + }); + cy.get(clientAuthenticationDropdown).click(); + cy.contains( + `[id*="react-select-"]`, + restAPIText.authorizationCode.sendClientCredentialsBodyOption + ).should("be.visible"); + cy.contains( + `[id*="react-select-"]`, + restAPIText.authorizationCode.sendBasicAuthheaderOption + ) + .should("be.visible") + .click(); + cy.get(clientAuthenticationDropdown).should( + "have.text", + restAPIText.authorizationCode.sendBasicAuthheaderOption + ); + cy.get( + restAPISelector.subHeaderLabel( + restAPIText.authorizationCode.authenticationRequiredUsersToggle + ) + ).verifyVisibleElement( + "have.text", + restAPIText.authorizationCode.authenticationRequiredUsersToggle + ); + cy.get(restAPISelector.authenticationAllUsersToggleSwitch).should( + "be.visible" + ); + cy.get(grantTypeDropdown).click(); + cy.contains( + `[id*="react-select-"]`, + restAPIText.clientCredentials.clientCredentialsLabel + ) + .should("be.visible") + .click(); + cy.get(grantTypeDropdown).should( + "contain", + restAPIText.clientCredentials.clientCredentialsLabel + ); + Object.entries(restAPIText.clientCredentials).forEach(([key, value]) => { + if (key !== "clientCredentialsLabel") { + cy.get( + restAPISelector.subHeaderLabel(restAPIText.clientCredentials[key]) + ).should("have.text", value); + } + }); + + cy.get( + restAPISelector.subHeaderLabel(restAPIText.secureSocketsLayerText) + ).should("have.text", restAPIText.secureSocketsLayerText); + cy.get(restAPISelector.dropdownLabel("SSL Certificate")).should( + "have.text", + "SSL Certificate" + ); + cy.get( + restAPISelector.subHeaderLabel(restAPIText.generalSettingsText) + ).should("have.text", restAPIText.generalSettingsText); + cy.get(restAPISelector.retryNetworkToggleSwitch).should("be.visible"); + cy.get(restAPISelector.retryNetworkToggleText).should( + "have.text", + restAPIText.retryNetworkErrorsToggleLabel + ); + cy.get(restAPISelector.retryNetworkToggleSubtext).should( + "have.text", + restAPIText.retryToggleHelperText + ); + cy.get(restAPISelector.readDocumentationLinkText).should( + "have.text", + postgreSqlText.readDocumentation + ); + cy.contains("Save").click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "Data Source Saved"); + cy.apiDeleteGDS(`cypress-${data.dataSourceName}-restapi`); + }); + it("Should verify connection response for all methods", () => { + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-restapi`, + "restapi", + [ + { key: "url", value: "https://jsonplaceholder.typicode.com" }, + { key: "auth_type", value: "none" }, + { key: "grant_type", value: "authorization_code" }, + { key: "add_token_to", value: "header" }, + { key: "header_prefix", value: "Bearer " }, + { key: "access_token_url", value: "" }, + { key: "client_id", value: "" }, + { key: "client_secret", value: "", encrypted: true }, + { key: "audience", value: "" }, + { key: "scopes", value: "read, write" }, + { key: "username", value: "", encrypted: false }, + { key: "password", value: "", encrypted: true }, + { key: "bearer_token", value: "", encrypted: true }, + { key: "auth_url", value: "" }, + { key: "client_auth", value: "header" }, + { key: "headers", value: [["", ""]] }, + { key: "custom_query_params", value: [["", ""]], encrypted: false }, + { key: "custom_auth_params", value: [["", ""]] }, + { + key: "access_token_custom_headers", + value: [["", ""]], + encrypted: false, + }, + { key: "multiple_auth_enabled", value: false, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + { key: "retry_network_errors", value: true, encrypted: false }, + ] + ); + cy.reload(); + + cy.apiCreateApp(`${fake.companyName}-restAPI-CURD-App`); + cy.openApp(); + createAndRunRestAPIQuery({ + queryName: "get_all_users", + dsName: `cypress-${data.dataSourceName}-restapi`, + method: "GET", + urlSuffix: "/users", + run: true, + expectedResponseShape: { + "0.id": true, + "0.name": true, + "0.email": true, + }, + }); + createAndRunRestAPIQuery({ + queryName: "post_restapi", + dsName: `cypress-${data.dataSourceName}-restapi`, + method: "POST", + headersList: [["Content-Type", "application/json"]], + rawBody: `{ + "name": "Test User", + "username": "testuser", + "email": "test@example.com", + "address": { + "street": "123 Test St", + "city": "Testville", + "zipcode": "12345" + } + }`, + urlSuffix: "/users", + run: true, + expectedResponseShape: { + id: true, + name: "Test User", + email: "test@example.com", + }, + }); + const id1 = 1; + + createAndRunRestAPIQuery({ + queryName: "put_restapi_id", + dsName: `cypress-${data.dataSourceName}-restapi`, + method: "PUT", + headersList: [["Content-Type", "application/json"]], + rawBody: `{ + "id": 1, + "name": "Fully Updated User", + "username": "updateduser", + "email": "updated@example.com", + "address": { + "street": "456 Updated St", + "city": "Updatedville", + "zipcode": "54321" + } + }`, + urlSuffix: `/users/${id1}`, + run: true, + expectedResponseShape: { + id: id1, + name: "Fully Updated User", + email: "updated@example.com", + }, + }); + createAndRunRestAPIQuery({ + queryName: "patch_restapi_id", + dsName: `cypress-${data.dataSourceName}-restapi`, + method: "PATCH", + headersList: [["Content-Type", "application/json"]], + rawBody: `{ + "email": "partially.updated@example.com" + }`, + urlSuffix: `/users/${id1}`, + run: true, + expectedResponseShape: { + id: id1, + email: "partially.updated@example.com", + }, + }); + createAndRunRestAPIQuery({ + queryName: "get_restapi_id", + dsName: `cypress-${data.dataSourceName}-restapi`, + method: "GET", + urlSuffix: `/users/${id1}`, + run: true, + expectedResponseShape: { + id: id1, + email: "Sincere@april.biz", + }, + }); + createAndRunRestAPIQuery({ + queryName: "delete_restapi_id", + dsName: `cypress-${data.dataSourceName}-restapi`, + method: "DELETE", + urlSuffix: `/users/${id1}`, + run: true, + expectedResponseShape: {}, + }); + cy.apiDeleteApp(`${fake.companyName}-restAPI-CURD-App`); + cy.apiDeleteGDS(`cypress-${data.dataSourceName}-restapi`); + }); + it("Should verify response for basic authentication type connection", () => { + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-restapi`, + "restapi", + [ + { key: "url", value: "https://httpbin.org" }, + { key: "auth_type", value: "basic" }, + { key: "grant_type", value: "authorization_code" }, + { key: "add_token_to", value: "header" }, + { key: "header_prefix", value: "Bearer " }, + { key: "access_token_url", value: "" }, + { key: "client_id", value: "" }, + { + key: "client_secret", + encrypted: true, + credential_id: "b044a293-82b4-4381-84fd-d173c86a6a0c", + }, + { key: "audience", value: "" }, + { key: "scopes", value: "read, write" }, + { key: "username", value: "user", encrypted: false }, + { key: "password", value: "pass", encrypted: true }, + { + key: "bearer_token", + encrypted: true, + credential_id: "21caf3cb-dbde-43c7-9f42-77feffb63062", + }, + { key: "auth_url", value: "" }, + { key: "client_auth", value: "header" }, + { key: "headers", value: [["", ""]] }, + { key: "custom_query_params", value: [["", ""]], encrypted: false }, + { key: "custom_auth_params", value: [["", ""]] }, + { + key: "access_token_custom_headers", + value: [["", ""]], + encrypted: false, + }, + { key: "multiple_auth_enabled", value: false, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + { key: "retry_network_errors", value: true, encrypted: false }, + { key: "url_parameters", value: [["", ""]], encrypted: false }, + { key: "tokenData", encrypted: false }, + ] + ); + cy.reload(); + cy.intercept("GET", "/api/library_apps").as("appLibrary"); + cy.apiCreateApp(`${fake.companyName}-restAPI-Basic-App`); + createAndRunRestAPIQuery({ + queryName: "get_basic_auth_valid", + dsName: `cypress-${data.dataSourceName}-restapi`, + method: "GET", + urlSuffix: "/basic-auth/user/pass", + expectedResponseShape: { authenticated: true, user: "user" }, + }); + createAndRunRestAPIQuery({ + queryName: "get_basic_auth_invalid", + dsName: `cypress-${data.dataSourceName}-restapi`, + method: "GET", + urlSuffix: "/basic-auth/invaliduser/invalidpass", + }); + cy.apiDeleteApp(`${fake.companyName}-restAPI-Basic-App`); + cy.apiDeleteGDS(`cypress-${data.dataSourceName}-restapi`); + }); + it("Should verify response for bearer authentication type connection", () => { + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-restapi`, + "restapi", + [ + { key: "url", value: "https://httpbin.org" }, + { key: "auth_type", value: "bearer" }, + { key: "grant_type", value: "authorization_code" }, + { key: "add_token_to", value: "header" }, + { key: "header_prefix", value: "Bearer " }, + { key: "access_token_url", value: "" }, + { key: "client_id", value: "" }, + { + key: "client_secret", + encrypted: true, + credential_id: "b044a293-82b4-4381-84fd-d173c86a6a0c", + }, + { key: "audience", value: "" }, + { key: "scopes", value: "read, write" }, + { key: "username", value: "", encrypted: false }, + { key: "password", value: "", encrypted: true }, + { + key: "bearer_token", + value: "my-token-123", + encrypted: true, + }, + { key: "auth_url", value: "" }, + { key: "client_auth", value: "header" }, + { key: "headers", value: [["", ""]] }, + { key: "custom_query_params", value: [["", ""]], encrypted: false }, + { key: "custom_auth_params", value: [["", ""]] }, + { + key: "access_token_custom_headers", + value: [["", ""]], + encrypted: false, + }, + { key: "multiple_auth_enabled", value: false, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + { key: "retry_network_errors", value: true, encrypted: false }, + { key: "url_parameters", value: [["", ""]], encrypted: false }, + { key: "tokenData", encrypted: false }, + ] + ); + cy.reload(); + cy.intercept("GET", "/api/library_apps").as("appLibrary"); + cy.apiCreateApp(`${fake.companyName}-restAPI-Bearer-App`); + cy.openApp(); + createAndRunRestAPIQuery({ + queryName: "get_bearer_auth_valid", + dsName: `cypress-${data.dataSourceName}-restapi`, + method: "GET", + urlSuffix: "/bearer", + expectedResponseShape: { authenticated: true, token: "my-token-123" }, + }); + cy.apiDeleteApp(`${fake.companyName}-restAPI-Bearer-App`); + cy.intercept("GET", "api/data_sources?**").as("datasource"); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-restapi-invalid`, + "restapi", + [ + { key: "url", value: "https://httpbin.org" }, + { key: "auth_type", value: "bearer" }, + { key: "grant_type", value: "authorization_code" }, + { key: "add_token_to", value: "header" }, + { key: "header_prefix", value: "Bearer " }, + { key: "access_token_url", value: "" }, + { key: "client_id", value: "" }, + { + key: "client_secret", + encrypted: true, + credential_id: "b044a293-82b4-4381-84fd-d173c86a6a0c", + }, + { key: "audience", value: "" }, + { key: "scopes", value: "read, write" }, + { key: "username", value: "", encrypted: false }, + { key: "password", value: "", encrypted: true }, + { + key: "bearer_token", + value: "", + encrypted: true, + }, + { key: "auth_url", value: "" }, + { key: "client_auth", value: "header" }, + { key: "headers", value: [["", ""]] }, + { key: "custom_query_params", value: [["", ""]], encrypted: false }, + { key: "custom_auth_params", value: [["", ""]] }, + { + key: "access_token_custom_headers", + value: [["", ""]], + encrypted: false, + }, + { key: "multiple_auth_enabled", value: false, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + { key: "retry_network_errors", value: true, encrypted: false }, + { key: "url_parameters", value: [["", ""]], encrypted: false }, + { key: "tokenData", encrypted: false }, + ] + ); + cy.apiCreateApp(`${fake.companyName}-restAPI-Bearer-invalid`); + cy.openApp(); + createAndRunRestAPIQuery({ + queryName: "get_bearer_auth_invalid", + dsName: `cypress-${data.dataSourceName}-restapi-invalid`, + method: "GET", + urlSuffix: "/bearer", + }); + cy.apiDeleteApp(`${fake.companyName}-restAPI-Bearer-invalid`); + cy.apiDeleteGDS(`cypress-${data.dataSourceName}-restapi`); + }); + it.skip("Should verify response for authentication code grant type connection", () => { + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-restapi`, + "restapi", + [ + { + key: "url", + value: "https://dev-6lj2hoxdz5fg3m57.uk.auth0.com/api/v2/users", + }, + { key: "auth_type", value: "oauth2" }, + { key: "grant_type", value: "client_credentials" }, + { key: "add_token_to", value: "header" }, + { key: "header_prefix", value: "Bearer " }, + { + key: "access_token_url", + value: "https://dev-6lj2hoxdz5fg3m57.uk.auth0.com/oauth/token", + }, + { key: "client_id", value: "JBDuuLU9vaSTP6Do7zYSkw0GvVgWhfyZ" }, + { + key: "client_secret", + encrypted: true, + credential_id: "a6d26607-4d09-42a2-8bc0-e5c185c7c2f7", + }, + { + key: "audience", + value: "https://dev-6lj2hoxdz5fg3m57.uk.auth0.com/api/v2/", + }, + { key: "scopes", value: "" }, + { key: "username", value: "", encrypted: false }, + { + key: "password", + encrypted: true, + credential_id: "4502a906-b512-447a-a128-39f67e9778d2", + }, + { + key: "bearer_token", + encrypted: true, + credential_id: "c94262c7-d2c5-4d7f-96f8-657689f2b1f0", + }, + { key: "auth_url", value: "" }, + { key: "client_auth", value: "header" }, + { key: "headers", value: [["", ""]] }, + { key: "custom_query_params", value: [["", ""]], encrypted: false }, + { key: "custom_auth_params", value: [["", ""]] }, + { + key: "access_token_custom_headers", + value: [["", ""]], + encrypted: false, + }, + { key: "multiple_auth_enabled", value: false, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + { key: "retry_network_errors", value: true, encrypted: false }, + ] + ); + cy.reload(); + cy.intercept("GET", "/api/library_apps").as("appLibrary"); + cy.apiCreateApp(`${fake.companyName}-client-Grant-RestAPI`); + }); + it("Should verify response for content-type", () => { + cy.apiCreateApp(`${fake.companyName}-restAPI-Content-App`); + createAndRunRestAPIQuery({ + queryName: "post_json", + dsName: "restapidefault", + method: "POST", + url: "https://jsonplaceholder.typicode.com/posts", + headersList: [["Content-Type", "application/json"]], + rawBody: '{"title": "foo","body": "bar","userId": 1}', + run: true, + urlSuffix: "", + expectedResponseShape: { id: true, title: "foo", body: "bar", userId: 1 }, + }); + createAndRunRestAPIQuery({ + queryName: "post_raw_text", + dsName: "restapidefault", + method: "POST", + url: "https://httpbin.org/post", + headersList: [["Content-Type", "text/plain"]], + rawBody: "This is plain text content", + jsonBody: null, + run: true, + expectedResponseShape: { data: "This is plain text content" }, + }); + createAndRunRestAPIQuery({ + queryName: "post_form_urlencoded", + dsName: "restapidefault", + method: "POST", + url: "https://httpbin.org/post", + headersList: [["Content-Type", "application/x-www-form-urlencoded"]], + bodyList: [ + ["name", "Jane"], + ["age", "30"], + ], + expectedResponseShape: { + "form.name": "Jane", + "form.age": "30", + }, + }); + createAndRunRestAPIQuery({ + queryName: "post_xml_soap", + dsName: "restapidefault", + method: "POST", + url: "http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?WSDL", + headersList: [["Content-Type", "text/xml; charset=utf-8"]], + rawBody: ` + + + + + + `, + jsonBody: null, + bodyList: [], + cookiesList: [["session", "abc123"]], + paramsList: [["lang", "en"]], + run: true, + shouldSucceed: true, + expectedResponseShape: {}, + }); + // createAndRunRestAPIQuery({ + // queryName: "post_text_csv", + // dsName: "restapidefault", + // method: "POST", + // url: `https://tejasvi.free.beeceptor.com/csv-upload`, + // headersList: [["Content-Type", "text/csv"]], + // rawBody: + // "id,name,email\n1,Alice,alice@example.com\n2,Bob,bob@example.com", + // expectedResponseShape: { + // data: '{\n "status": "ok",\n "message": "File uploaded successfully",\n "body": id,name,email\n1,Alice,alice@example.com\n2,Bob,bob@example.com\n}', + // }, + // }); + // const filename = "tooljet.png"; + + // createAndRunRestAPIQuery({ + // queryName: "upload_image", + // dsName: "restapidefault", + // method: "POST", + // url: `https://tejasvi.free.beeceptor.commultipart-upload`, + // headersList: [["Content-Type", "multipart/form-data"]], + // bodyList: [ + // ["Image_File", "fixture:Image/tooljet.png"], + // ["filename", filename], + // ], + // expectedResponseShape: { + // filename: filename, + // }, + // }); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/rethinkDbHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/rethinkDbHappyPath.cy.skip.js index 8f04fa4572..abdde2ba00 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/rethinkDbHappyPath.cy.skip.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/rethinkDbHappyPath.cy.skip.js @@ -3,6 +3,7 @@ import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; import { commonWidgetText, commonText } from "Texts/common"; import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { addQuery, fillDataSourceTextField, @@ -18,7 +19,8 @@ const data = {}; describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -49,7 +51,26 @@ describe("Data sources", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource("databases", "RethinkDB", data.dataSourceName); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-rethinkdb`, + "rethinkdb", + [ + { key: "port", value: "28015", encrypted: false }, + { key: "host", value: "", encrypted: false }, + { key: "database", value: "", encrypted: false }, + { key: "username", value: "", encrypted: false }, + { key: "password", value: "", encrypted: true }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-rethinkdb-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-rethinkdb` + ); cy.get('[data-cy="label-database"]').verifyVisibleElement( "have.text", diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/s3HappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/s3HappyPath.cy.js index 3e8a243c53..79f2534f9e 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/s3HappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/s3HappyPath.cy.js @@ -4,7 +4,7 @@ import { s3Selector } from "Selectors/awss3"; import { postgreSqlText } from "Texts/postgreSql"; import { s3Text } from "Texts/awss3"; import { commonSelectors } from "Selectors/common"; -import { commonText } from "Texts/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { fillDataSourceTextField, selectAndAddDataSource, @@ -19,7 +19,8 @@ const data = {}; describe("Data sources AWS S3", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -50,7 +51,31 @@ describe("Data sources AWS S3", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource("cloudstorage", s3Text.awsS3, data.dataSourceName); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-aws-s3`, + "s3", + [ + { key: "access_key", value: "" }, + { key: "secret_key", value: "", encrypted: true }, + { key: "region", value: "" }, + { key: "endpoint", value: "" }, + { key: "endpoint_enabled", value: false, encrypted: false }, + { + key: "instance_metadata_credentials", + value: "iam_access_keys", + encrypted: false, + }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-aws-s3-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-aws-s3` + ); cy.get(s3Selector.accessKeyLabel).verifyVisibleElement( "have.text", s3Text.accessKey @@ -95,7 +120,7 @@ describe("Data sources AWS S3", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", s3Text.alertRegionIsMissing ); @@ -119,7 +144,7 @@ describe("Data sources AWS S3", () => { ); cy.get(postgreSqlSelector.buttonTestConnection).click(); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", s3Text.alertRegionIsMissing ); @@ -145,7 +170,7 @@ describe("Data sources AWS S3", () => { .click(); cy.get(postgreSqlSelector.buttonTestConnection).click(); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", s3Text.alertInvalidUrl ); @@ -163,7 +188,7 @@ describe("Data sources AWS S3", () => { ); cy.get(postgreSqlSelector.buttonTestConnection).click(); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", s3Text.accessKeyError ); @@ -182,7 +207,7 @@ describe("Data sources AWS S3", () => { cy.get(postgreSqlSelector.buttonTestConnection).click(); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", s3Text.sinatureError ); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sapHanaHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sapHanaHappyPath.cy.skip.js index 813b2663c9..25ec2e4a9c 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sapHanaHappyPath.cy.skip.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sapHanaHappyPath.cy.skip.js @@ -15,7 +15,7 @@ import { describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); // cy.createApp(); }); @@ -108,7 +108,7 @@ describe("Data sources", () => { selectAndAddDataSource(postgreSqlText.postgreSQL); cy.clearAndType( - '[data-cy="data-source-name-input-filed"]', + '[data-cy="data-source-name-input-field"]', postgreSqlText.psqlName ); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/smtpHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/smtpHappyPath.cy.js index 90594f8122..15d716ae8e 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/smtpHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/smtpHappyPath.cy.js @@ -2,7 +2,7 @@ import { fake } from "Fixtures/fake"; import { postgreSqlSelector } from "Selectors/postgreSql"; import { postgreSqlText } from "Texts/postgreSql"; import { commonSelectors } from "Selectors/common"; -import { commonText } from "Texts/common"; +import { dataSourceSelector } from "Selectors/dataSource"; import { fillDataSourceTextField, selectAndAddDataSource, @@ -13,7 +13,8 @@ const data = {}; describe("Data source SMTP", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -43,7 +44,25 @@ describe("Data source SMTP", () => { postgreSqlText.allCloudStorage ); - selectAndAddDataSource("apis", "SMTP", data.dataSourceName); + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-smtp`, + "smtp", + [ + { key: "host", value: "localhost", encrypted: false }, + { key: "port", value: 465, encrypted: false }, + { key: "user", value: "", encrypted: false }, + { key: "password", value: "", encrypted: true }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-smtp-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-smtp` + ); cy.get(postgreSqlSelector.labelHost).verifyVisibleElement( "have.text", @@ -86,7 +105,7 @@ describe("Data source SMTP", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", "Invalid credentials" ); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/snowflakeHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/snowflakeHappyPath.cy.js index 22f2afd08e..e8419657bd 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/snowflakeHappyPath.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/snowflakeHappyPath.cy.js @@ -4,6 +4,7 @@ import { postgreSqlText } from "Texts/postgreSql"; import { commonWidgetText, commonText } from "Texts/common"; import { commonSelectors, commonWidgetSelector } from "Selectors/common"; import { closeDSModal, deleteDatasource } from "Support/utils/dataSource"; +import { dataSourceSelector } from "Selectors/dataSource"; import { addQuery, @@ -19,7 +20,8 @@ import { const data = {}; describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); + cy.visit("/"); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -49,8 +51,28 @@ describe("Data sources", () => { "have.text", postgreSqlText.allCloudStorage ); - selectAndAddDataSource("databases", "Snowflake", data.dataSourceName); - + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-snowflake`, + "snowflake", + [ + { key: "username", value: "" }, + { key: "account", value: "" }, + { key: "password", value: "", encrypted: true }, + { key: "database", value: "" }, + { key: "schema", value: "" }, + { key: "warehouse", value: "" }, + { key: "role", value: "" }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-snowflake-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-snowflake` + ); cy.get(postgreSqlSelector.labelUserName).verifyVisibleElement( "have.text", postgreSqlText.labelUserName @@ -106,14 +128,14 @@ describe("Data sources", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", "Invalid account. The specified value must be a valid subdomain string." ); deleteDatasource(`cypress-${data.dataSourceName}-snowflake`); }); - it.skip("Should verify the functionality of PostgreSQL connection form.", () => { + it.skip("Should verify the functionality of snowflake connection form.", () => { selectAndAddDataSource("databases", "Snowflake", data.dataSourceName); fillDataSourceTextField( diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sqlServerHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sqlServerHappyPath.cy.skip.js new file mode 100644 index 0000000000..1ac953a42b --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sqlServerHappyPath.cy.skip.js @@ -0,0 +1,197 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { commonWidgetText, commonText } from "Texts/common"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { deleteDatasource, closeDSModal } from "Support/utils/dataSource"; +import { dataSourceSelector } from "Selectors/dataSource"; + +import { + addQuery, + fillDataSourceTextField, + fillConnectionForm, + selectAndAddDataSource, + openQueryEditor, + selectQueryMode, + addGuiQuery, + addWidgetsToAddUser, +} from "Support/utils/postgreSql"; + +const data = {}; + +describe("Data sources", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dataSourceName = fake.lastName + .toLowerCase() + .replaceAll("[^A-Za-z]", ""); + }); + + it("Should verify elements on connection form", () => { + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + `cypress-${data.dataSourceName}-sql-server`, + "mssql", + [ + { key: "host", value: "localhost" }, + { key: "instanceName", value: "" }, + { key: "port", value: 1433 }, + { key: "database", value: "" }, + { key: "username", value: "" }, + { key: "password", value: "", encrypted: true }, + { key: "azure", value: false, encrypted: false }, + ] + ); + cy.reload(); + cy.get(`[data-cy="cypress-${data.dataSourceName}-sql-server-button"]`) + .should("be.visible") + .click(); + cy.get(dataSourceSelector.dsNameInputField).should( + "have.value", + `cypress-${data.dataSourceName}-sql-server` + ); + + cy.get(postgreSqlSelector.labelHost).verifyVisibleElement( + "have.text", + postgreSqlText.labelHost + ); + cy.get('[data-cy="label-instance"]').verifyVisibleElement( + "have.text", + "Instance" + ); + cy.get(postgreSqlSelector.labelPort).verifyVisibleElement( + "have.text", + postgreSqlText.labelPort + ); + cy.get(postgreSqlSelector.labelDbName).verifyVisibleElement( + "have.text", + "Database Name" + ); + cy.get(postgreSqlSelector.labelUserName).verifyVisibleElement( + "have.text", + postgreSqlText.labelUserName + ); + cy.get(postgreSqlSelector.labelPassword).verifyVisibleElement( + "have.text", + "Password" + ); + + cy.get('[data-cy^="label-azure-"]').verifyVisibleElement( + "contain", + "Azure" + ); + cy.get(postgreSqlSelector.labelIpWhitelist).verifyVisibleElement( + "have.text", + postgreSqlText.whiteListIpText + ); + cy.get(postgreSqlSelector.buttonCopyIp).verifyVisibleElement( + "have.text", + postgreSqlText.textCopy + ); + + cy.get(postgreSqlSelector.linkReadDocumentation).verifyVisibleElement( + "have.text", + postgreSqlText.readDocumentation + ); + cy.get(postgreSqlSelector.buttonTestConnection) + .verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextTestConnection + ) + .click(); + cy.get(postgreSqlSelector.connectionFailedText).verifyVisibleElement( + "have.text", + postgreSqlText.couldNotConnect + ); + cy.get(postgreSqlSelector.buttonSave).verifyVisibleElement( + "have.text", + postgreSqlText.buttonTextSave + ); + cy.get(dataSourceSelector.connectionAlertText).verifyVisibleElement( + "have.text", + "Failed to connect to localhost:1433 - Could not connect (sequence)" + ); + deleteDatasource(`cypress-${data.dataSourceName}-sql-server`); + }); + + it("Should verify the functionality of SQL Server connection form.", () => { + selectAndAddDataSource("databases", "SQL Server", data.dataSourceName); + + fillDataSourceTextField( + postgreSqlText.labelHost, + postgreSqlText.placeholderEnterHost, + Cypress.env("sqlserver_host") + ); + // fillDataSourceTextField( + // "Instance", + // "Enter the name of the database instance", + // Cypress.env("sqlserver_instance") + // ); + fillDataSourceTextField( + postgreSqlText.labelPort, + postgreSqlText.placeholderEnterPort, + "1433" + ); + fillDataSourceTextField( + "Database Name", + postgreSqlText.placeholderNameOfDB, + Cypress.env("sqlserver_db") + ); + fillDataSourceTextField( + postgreSqlText.labelUserName, + postgreSqlText.placeholderEnterUserName, + Cypress.env("sqlserver_user") + ); + + fillDataSourceTextField( + postgreSqlText.labelPassword, + "**************", + Cypress.env("sqlserver_password") + ); + + cy.get(postgreSqlSelector.buttonTestConnection).click(); + cy.get(postgreSqlSelector.textConnectionVerified, { + timeout: 10000, + }).should("have.text", postgreSqlText.labelConnectionVerified); + cy.get(postgreSqlSelector.buttonSave).click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + cy.get( + `[data-cy="cypress-${data.dataSourceName}-sql-server-button"]` + ).verifyVisibleElement( + "have.text", + `cypress-${data.dataSourceName}-sql-server` + ); + deleteDatasource(`cypress-${data.dataSourceName}-sql-server`); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/twilioHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/twilioHappyPath.cy.skip.js new file mode 100644 index 0000000000..4ac2575266 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/twilioHappyPath.cy.skip.js @@ -0,0 +1,197 @@ +import { fake } from "Fixtures/fake"; +import { postgreSqlSelector } from "Selectors/postgreSql"; +import { postgreSqlText } from "Texts/postgreSql"; +import { twilioText } from "Texts/twilio"; +import { twilioSelectors } from "Selectors/Plugins"; +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; + +import { + fillDataSourceTextField, + selectAndAddDataSource, +} from "Support/utils/postgreSql"; + +import { + deleteDatasource, + closeDSModal, + deleteAppandDatasourceAfterExecution, +} from "Support/utils/dataSource"; + +import { dataSourceSelector } from "../../../../../constants/selectors/dataSource"; +import { pluginSelectors } from "Selectors/plugins"; + +const data = {}; + +describe("Data source Twilio", () => { + beforeEach(() => { + cy.apiLogin(); + cy.visit("/"); + data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + }); + + it.skip("Should verify elements on Twilio connection form", () => { + const AuthToken = Cypress.env("twilio_auth_token"); + const AccountSID = Cypress.env("twilio_account_SID"); + const MessageSID = Cypress.env("twilio_messaging_service_SID"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + cy.get(postgreSqlSelector.allDatasourceLabelAndCount).should( + "have.text", + postgreSqlText.allDataSources() + ); + cy.get(postgreSqlSelector.commonlyUsedLabelAndCount).should( + "have.text", + postgreSqlText.commonlyUsed + ); + cy.get(postgreSqlSelector.databaseLabelAndCount).should( + "have.text", + postgreSqlText.allDatabase() + ); + cy.get(postgreSqlSelector.apiLabelAndCount).should( + "have.text", + postgreSqlText.allApis + ); + cy.get(postgreSqlSelector.cloudStorageLabelAndCount).should( + "have.text", + postgreSqlText.allCloudStorage + ); + + selectAndAddDataSource("databases", twilioText.twilio, data.dsName); + + fillDataSourceTextField( + twilioText.authTokenLabel, + twilioText.authTokenPlaceholder, + AuthToken + ); + + fillDataSourceTextField( + twilioText.accountSidLabel, + twilioText.accountSidPlaceholder, + AccountSID + ); + + fillDataSourceTextField( + twilioText.messagingSIDLabel, + twilioText.messagingSIDPalceholder, + MessageSID + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-twilio`); + }); + + it.skip("Should verify functionality of Twilio connection form", () => { + const AuthToken = Cypress.env("twilio_auth_token"); + const AccountSID = Cypress.env("twilio_account_SID"); + const MessageSID = Cypress.env("twilio_messaging_service_SID"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + selectAndAddDataSource("databases", twilioText.twilio, data.dsName); + + fillDataSourceTextField( + twilioText.authTokenLabel, + twilioText.authTokenPlaceholder, + AuthToken + ); + + fillDataSourceTextField( + twilioText.accountSidLabel, + twilioText.accountSidPlaceholder, + AccountSID + ); + + fillDataSourceTextField( + twilioText.messagingSIDLabel, + twilioText.messagingSIDPalceholder, + MessageSID + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + deleteDatasource(`cypress-${data.dsName}-twilio`); + }); + + it.skip("Should be able to run the query with a valid connection", () => { + const AuthToken = Cypress.env("twilio_auth_token"); + const AccountSID = Cypress.env("twilio_account_SID"); + const MessageSID = Cypress.env("twilio_messaging_service_SID"); + const MessageNumber = Cypress.env("twilio_message_number"); + + cy.get(commonSelectors.globalDataSourceIcon).click(); + closeDSModal(); + + selectAndAddDataSource("databases", twilioText.twilio, data.dsName); + + fillDataSourceTextField( + twilioText.authTokenLabel, + twilioText.authTokenPlaceholder, + AuthToken + ); + + fillDataSourceTextField( + twilioText.accountSidLabel, + twilioText.accountSidPlaceholder, + AccountSID + ); + + fillDataSourceTextField( + twilioText.messagingSIDLabel, + twilioText.messagingSIDPalceholder, + MessageSID + ); + + cy.get(postgreSqlSelector.buttonSave) + .verifyVisibleElement("have.text", postgreSqlText.buttonTextSave) + .click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + postgreSqlText.toastDSSaved + ); + + cy.get(commonSelectors.dashboardIcon).click(); + cy.get(commonSelectors.appCreateButton).click(); + cy.get(commonSelectors.appNameInput).click().type(data.dsName); + cy.get(commonSelectors.createAppButton).click(); + cy.skipWalkthrough(); + + cy.get('[data-cy="show-ds-popover-button"]').click(); + cy.get(".css-4e90k9").type(`${data.dsName}`); + cy.contains(`[id*="react-select-"]`, data.dsName).click(); + cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName); + + cy.get(pluginSelectors.operationDropdown).click().type("Send SMS{enter}"); + + cy.get(twilioSelectors.toNumberInputField).clearAndTypeOnCodeMirror( + MessageNumber + ); + cy.get(twilioSelectors.bodyInput).clearAndTypeOnCodeMirror( + twilioText.messageText + ); + cy.get(dataSourceSelector.queryPreviewButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + `Query (${data.dsName}) completed.` + ); + deleteAppandDatasourceAfterExecution( + data.dsName, + `cypress-${data.dsName}-twilio` + ); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/typeSenseHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/typeSenseHappyPath.cy.skip.js index ff15053f09..9e480da66c 100644 --- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/typeSenseHappyPath.cy.skip.js +++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/typeSenseHappyPath.cy.skip.js @@ -20,7 +20,7 @@ const data = {}; describe("Data sources", () => { beforeEach(() => { - cy.appUILogin(); + cy.apiLogin(); data.dataSourceName = fake.lastName .toLowerCase() .replaceAll("[^A-Za-z]", ""); @@ -96,7 +96,7 @@ describe("Data sources", () => { "have.text", postgreSqlText.buttonTextSave ); - cy.get('[data-cy="connection-alert-text"]').should( + cy.get(dataSourceSelector.connectionAlertText).should( "have.text", "Ensure that apiKey is set" ); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appExport.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appExport.cy.js new file mode 100644 index 0000000000..26a80bfb52 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appExport.cy.js @@ -0,0 +1,218 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors } from "Selectors/common"; +import { importSelectors } from "Selectors/exportImport"; +import { commonText } from "Texts/common"; + +import { exportAppModalText } from "Texts/exportImport"; +import { + clickOnExportButtonAndVerify, + exportAllVersionsAndVerify, + verifyElementsOfExportModal, +} from "Support/utils/exportImport"; +import { selectAppCardOption, closeModal } from "Support/utils/common"; + +describe("App Export", () => { + const TEST_DATA = { + appFiles: { + multiVersion: "cypress/fixtures/templates/three-versions.json", + singleVersion: "cypress/fixtures/templates/one_version.json", + }, + }; + + let data; + + data = { + workspaceName: fake.firstName, + workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"), + appName: `${fake.companyName}-IE-App`, + appReName: `${fake.companyName}-${fake.companyName}-IE-App`, + dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""), + }; + + beforeEach(() => { + data = { + workspaceName: fake.firstName, + workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"), + appName: `${fake.companyName}-IE-App`, + appReName: `${fake.companyName}-${fake.companyName}-IE-App`, + dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""), + }; + cy.exec("mkdir -p ./cypress/downloads/"); + cy.exec("cd ./cypress/downloads/ && rm -rf *"); + cy.exec("mkdir -p ./cypress/downloads/"); + cy.wait(3000); + + cy.apiLogin(); + cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); + cy.apiLogout(); + }); + + it("Verify the elements of export dialog box", () => { + cy.skipWalkthrough(); + + cy.apiLogin(); + cy.visit(`${data.workspaceSlug}`); + cy.get(importSelectors.importOptionInput) + .eq(0) + .selectFile(TEST_DATA.appFiles.multiVersion, { force: true }); + cy.wait(2000); + cy.clearAndType(commonSelectors.appNameInput, data.appName); + cy.get(importSelectors.importAppButton).click(); + cy.wait(3000); + cy.backToApps(); + + // Select the app card option to export the app + selectAppCardOption( + data.appName, + commonSelectors.appCardOptions(commonText.exportAppOption) + ); + + // Verify the elements of the export modal + verifyElementsOfExportModal("v3", ["v2", "v1"], [true, false, false]); + + // Close the modal + closeModal(exportAppModalText.modalCloseButton); + + // Ensure the modal title is no longer visible + cy.get( + commonSelectors.modalTitle(exportAppModalText.selectVersionTitle) + ).should("not.exist"); + + // Re-open the export modal and click the export button + cy.wait(2000); + selectAppCardOption( + data.appName, + commonSelectors.appCardOptions(commonText.exportAppOption) + ); + clickOnExportButtonAndVerify(exportAppModalText.exportAll, data.appName); + + cy.exec("ls ./cypress/downloads/").then((result) => { + const downloadedAppExportFileName = result.stdout.split("\n")[0]; + const filePath = `./cypress/downloads/${downloadedAppExportFileName}`; + + // Ensure the file name contains the expected app export name + expect(downloadedAppExportFileName).to.contain( + data.appName.toLowerCase() + ); + + // Read and validate the exported JSON file + cy.readFile(filePath).then((appData) => { + // Validate the app name + const appNameFromFile = appData.app[0].definition.appV2.name; + expect(appNameFromFile).to.equal(data.appName); + + // Validate the schema for the student table in tooljetdb + const tooljetDatabase = appData.tooljet_database.find( + (db) => db.table_name === "student" + ); + expect(tooljetDatabase).to.exist; + expect(tooljetDatabase.schema).to.exist; + + // Validate components and queries + const components = appData.app[0].definition.appV2.components; + + const text2Component = components.find( + (component) => component.name === "text2" + ); + expect(text2Component).to.exist; + expect(text2Component.properties.text.value).to.equal( + "{{constants.pageHeader}}" + ); + + const textinput1 = components.find( + (component) => component.name === "textinput1" + ); + expect(textinput1).to.exist; + expect(textinput1.properties.value.value).to.include("queries"); + + const textinput2 = components.find( + (component) => component.name === "textinput2" + ); + expect(textinput2).to.exist; + expect(textinput2.properties.value.value).to.include("queries"); + + const textinput3 = components.find( + (component) => component.name === "textinput3" + ); + expect(textinput3).to.exist; + expect(textinput3.properties.value.value).to.include("queries"); + + // Validate the data queries + const dataQueries = appData.app[0].definition.appV2.dataQueries; + + const postgresqlQuery = dataQueries.find( + (query) => query.name === "postgresql1" + ); + expect(postgresqlQuery).to.exist; + expect(postgresqlQuery.options.query).to.include( + "Select * from {{secrets.db_name}}" + ); + + const restapiQuery = dataQueries.find( + (query) => query.name === "restapi1" + ); + expect(restapiQuery).to.exist; + expect(restapiQuery.options.url).to.equal( + "https://jsonplaceholder.typicode.com/users/1" + ); + + const tooljetdbQuery = dataQueries.find( + (query) => query.name === "tooljetdb1" + ); + expect(tooljetdbQuery).to.exist; + expect(tooljetdbQuery.options.operation).to.equal("list_rows"); + + // Ensure appVersions exists + const appVersions = appData.app[0].definition.appV2.appVersions; + expect(appVersions).to.exist; + + // Map and verify app version names + const versionNames = appVersions.map((version) => version.name); + expect(versionNames).to.include.members(["v1", "v2", "v3"]); + }); + }); + + cy.exec("cd ./cypress/downloads/ && rm -rf *"); + + selectAppCardOption( + data.appName, + commonSelectors.appCardOptions(commonText.exportAppOption) + ); + cy.get(`[data-cy="v1-radio-button"]`).check(); + cy.get( + commonSelectors.buttonSelector(exportAppModalText.exportSelectedVersion) + ).click(); + + cy.exec("ls ./cypress/downloads/").then((result) => { + const downloadedAppExportFileName = result.stdout.split("\n")[0]; + const filePath = `./cypress/downloads/${downloadedAppExportFileName}`; + + // Ensure the file name contains the expected app export name + expect(downloadedAppExportFileName).to.contain( + data.appName.toLowerCase() + ); + + // Read and validate the exported JSON file + cy.readFile(filePath).then((appData) => { + // Validate the app name + const appNameFromFile = appData.app[0].definition.appV2.name; + expect(appNameFromFile).to.equal(data.appName); + }); + }); + }); + + it.skip("Verify 'Export app' functionality of an application inside app editor", () => { + data.appName2 = `${fake.companyName}-App`; + cy.apiCreateApp(data.appName2); + cy.openApp(data.appName2); + + cy.dragAndDropWidget("Text Input", 50, 50); + + cy.get('[data-cy="left-sidebar-settings-button"]').click(); + cy.get('[data-cy="button-user-status-change"]').click(); + + verifyElementsOfExportModal("v1"); + + exportAllVersionsAndVerify(data.appName1, "v1"); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appImport.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appImport.cy.js new file mode 100644 index 0000000000..f75edd64ce --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appImport.cy.js @@ -0,0 +1,258 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { appVersionSelectors, importSelectors } from "Selectors/exportImport"; +import { dashboardSelector } from "Selectors/dashboard"; +import { buttonText } from "Texts/button"; + +import { importText } from "Texts/exportImport"; +import { importAndVerifyApp } from "Support/utils/exportImport"; +import { switchVersionAndVerify } from "Support/utils/version"; + +describe("App Import Functionality", () => { + const TEST_DATA = { + toolJetImage: "cypress/fixtures/Image/tooljet.png", + invalidApp: "cypress/fixtures/templates/invalid_app.json", + invalidFile: "cypress/fixtures/templates/invalid_file.json", + appFiles: { + multiVersion: "cypress/fixtures/templates/three-versions.json", + singleVersion: "cypress/fixtures/templates/one_version.json", + }, + }; + + let data; + + beforeEach(() => { + cy.viewport(1400, 1400); + data = { + workspaceName: fake.firstName, + workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"), + appName: `${fake.companyName}-IE-App`, + appReName: `${fake.companyName}-${fake.companyName}-IE-App`, + dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""), + }; + + cy.apiLogin(); + cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); + cy.apiLogout(); + cy.skipWalkthrough(); + }); + + it("should verify app import functionality", () => { + cy.apiLogin(); + cy.visit(`${data.workspaceSlug}`); + + // Test invalid file import + cy.get(dashboardSelector.importAppButton).click(); + importAndVerifyApp( + TEST_DATA.toolJetImage, + importText.couldNotImportAppToastMessage + ); + + cy.wait(500); + cy.get(dashboardSelector.importAppButton).click(); + importAndVerifyApp( + TEST_DATA.invalidApp, + "Could not import: SyntaxError: Expected ',' or '}' after property value in JSON at position 246 (line 11 column 13)" + ); + + cy.wait(500); + + // Test valid app import + cy.get(importSelectors.dropDownMenu).should("be.visible").click(); + cy.get(importSelectors.importOptionLabel).verifyVisibleElement( + "have.text", + importText.importOption + ); + + cy.intercept("POST", "/api/v2/resources/import").as("importApp"); + cy.get(importSelectors.importOptionInput) + .eq(0) + .selectFile(TEST_DATA.appFiles.multiVersion, { + force: true, + }); + cy.wait(1500); + + cy.get(importSelectors.importAppTitle).verifyVisibleElement( + "have.text", + "Import app" + ); + cy.get(commonSelectors.appNameLabel).verifyVisibleElement( + "have.text", + "App name" + ); + cy.get(commonSelectors.appNameInput) + .should("be.visible") + .and("have.value", "three-versions"); + cy.get(commonSelectors.appNameInfoLabel).verifyVisibleElement( + "have.text", + "App name must be unique and max 50 characters" + ); + cy.get(commonSelectors.cancelButton) + .should("be.visible") + .and("have.text", "Cancel"); + cy.get(commonSelectors.importAppButton).verifyVisibleElement( + "have.text", + "Import app" + ); + + cy.get(importSelectors.importAppButton).click(); + cy.get(".go3958317564") + .should("be.visible") + .and("have.text", importText.appImportedToastMessage); + + // Verify imported app + cy.get(commonSelectors.toastCloseButton).click(); + cy.wait(500); + cy.get(commonSelectors.appNameInput).verifyVisibleElement( + "contain.value", + "three-versions" + ); + cy.get(appVersionSelectors.currentVersionField("v3")).should("be.visible"); + + // Configure app + cy.skipEditorPopover(); + cy.dragAndDropWidget(buttonText.defaultWidgetText); + cy.get(appVersionSelectors.appVersionLabel).should("be.visible"); + cy.get(commonWidgetSelector.draggableWidget("button1")).should( + "be.visible" + ); + + cy.renameApp(data.appName); + cy.get(commonSelectors.appNameInput).verifyVisibleElement( + "contain.value", + data.appName + ); + cy.waitForAutoSave(); + + // Verify initial widget states + + verifyCommonData({ + text2: "", + textInput1: "", + textInput2: "Leanne Graham", + }); + + // cy.get( + // commonWidgetSelector.draggableWidget("textInput3") + // ).verifyVisibleElement("have.value", ""); + + // Setup database and data sources + cy.visit(`${data.workspaceSlug}/database`); + cy.get('[data-cy="student-table"]').verifyVisibleElement( + "have.text", + "student" + ); + + // cy.apiAddDataToTable("student", { + // name: "Paramu", + // country: "India", + // state: "Kerala", + // }); + + cy.visit(`${data.workspaceSlug}/data-sources`); + cy.get('[data-cy="postgresql-button"]').should("be.visible"); + + cy.ifEnv("Community", () => { + cy.apiUpdateDataSource("postgresql", "production", { + options: [ + { + key: "password", + value: `${Cypress.env("pg_password")}`, + encrypted: true, + }, + ], + }); + }); + cy.ifEnv("Enterprise", () => { + cy.apiUpdateDataSource("postgresql", "development", { + options: [ + { + key: "password", + value: `${Cypress.env("pg_password")}`, + encrypted: true, + }, + ], + }); + }); + + cy.ifEnv("Community", () => { + cy.apiCreateWsConstant( + "pageHeader", + "Import and Export", + ["Global"], + ["production"] + ); + cy.apiCreateWsConstant("db_name", "persons", ["Secret"], ["production"]); + }); + + cy.ifEnv("Enterprise", () => { + cy.apiCreateWsConstant( + "pageHeader", + "Import and Export", + ["Global"], + ["development"] + ); + cy.apiCreateWsConstant("db_name", "persons", ["Secret"], ["development"]); + }); + + // Verify app after setup + cy.wait("@importApp").then((interception) => { + const appId = interception.response.body.imports.app[0].id; + cy.openApp( + "", + Cypress.env("workspaceId"), + appId, + commonWidgetSelector.draggableWidget("text2") + ); + }); + + verifyCommonData({ + text2: "Import and Export", + textInput1: "John", + textInput2: "Leanne Graham", + }); + // cy.get( + // commonWidgetSelector.draggableWidget("textInput3") + // ).verifyVisibleElement("have.value", "India"); + + switchVersionAndVerify("v3", "v1"); + + verifyCommonData({ + text2: "Import and Export", + textInput1: "John", + textInput2: "Leanne Graham", + }); + + cy.wait(1000); + cy.backToApps(); + + // Test single version import + cy.get(importSelectors.dropDownMenu).click(); + importAndVerifyApp(TEST_DATA.appFiles.singleVersion); + + // Verify final state + cy.get(commonSelectors.appNameInput).verifyVisibleElement( + "contain.value", + "one_version" + ); + + verifyCommonData({ + text2: "Import and Export", + textInput1: "John", + textInput2: "Leanne Graham", + }); + }); +}); + +const verifyCommonData = (values) => { + cy.get(commonWidgetSelector.draggableWidget("text2")).verifyVisibleElement( + "have.text", + values.text2 + ); + cy.get( + commonWidgetSelector.draggableWidget("textInput1") + ).verifyVisibleElement("have.value", values.textInput1); + cy.get( + commonWidgetSelector.draggableWidget("textInput2") + ).verifyVisibleElement("have.value", values.textInput2); +}; diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appSlug.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appSlug.cy.js index d23e9d53e8..c4a2c674ff 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appSlug.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/appSlug.cy.js @@ -7,6 +7,7 @@ import { verifyURLs, resolveHost, } from "Support/utils/apps"; +import { appPromote } from "Support/utils/platform/multiEnv"; describe("App Slug", () => { const data = {}; @@ -27,128 +28,139 @@ describe("App Slug", () => { }); it("Verify app slug cases in global settings", () => { - cy.apiLogin("dev@tooljet.io", "password").then(() => { - const workspaceId = Cypress.env("workspaceId"); - const appId = Cypress.env("appId"); + const workspaceId = Cypress.env("workspaceId"); + const appId = Cypress.env("appId"); + const appUrl = `${host}/${Cypress.env("workspaceId")}/apps/${Cypress.env("appId")}/`; - cy.openApp("my-workspace"); - cy.get(commonSelectors.leftSideBarSettingsButton).click(); + cy.apiLogin(); + cy.skipWalkthrough(); - // Verify initial state - cy.get(commonWidgetSelector.appSlugLabel).verifyVisibleElement( - "have.text", - "Unique app slug" - ); - cy.get(commonWidgetSelector.appSlugInput).verifyVisibleElement( - "have.value", - Cypress.env("appId") - ); - cy.get(commonWidgetSelector.appSlugInfoLabel).verifyVisibleElement( - "have.text", - "URL-friendly 'slug' consists of lowercase letters, numbers, and hyphens" - ); - - cy.get(commonWidgetSelector.appLinkLabel).verifyVisibleElement( - "have.text", - "App link" - ); - - cy.get(commonWidgetSelector.appLinkField).verifyVisibleElement( - "have.text", - `${host}/${workspaceId}/apps/${appId}` - ); - - // Validate all error cases - verifySlugValidations(commonWidgetSelector.appSlugInput); - - // Verify successful slug update - cy.clearAndType(commonWidgetSelector.appSlugInput, data.slug); - verifySuccessfulSlugUpdate(workspaceId, data.slug); - - // Verify persistence - cy.get('[data-cy="left-sidebar-debugger-button"]').click(); - cy.get(commonSelectors.leftSideBarSettingsButton).click(); - cy.get(commonWidgetSelector.appSlugInput).should("have.value", data.slug); - - // Release and verify URLs - releaseApp(); - verifyURLs(workspaceId, data.slug, false); - - // Verify duplicate slug validation - cy.visit("/my-workspace"); - cy.apiCreateApp(data.slug); - cy.openApp("my-workspace"); - cy.get(commonSelectors.leftSideBarSettingsButton).click(); - cy.get(commonWidgetSelector.appSlugInput).clear(); - cy.clearAndType(commonWidgetSelector.appSlugInput, data.slug); - cy.get(commonWidgetSelector.appSlugErrorLabel).verifyVisibleElement( - "have.text", - "This app slug is already taken." - ); + cy.visit(appUrl); + cy.url().then((url) => { + if (url !== appUrl) { + cy.visit(appUrl); + } }); + cy.url().should("eq", appUrl); + + cy.wait(1000); + + cy.get(commonSelectors.leftSideBarSettingsButton).click(); + + // Verify initial state + cy.get(commonWidgetSelector.appSlugLabel).verifyVisibleElement( + "have.text", + "Unique app slug" + ); + cy.get(commonWidgetSelector.appSlugInput).verifyVisibleElement( + "have.value", + Cypress.env("appId") + ); + cy.get(commonWidgetSelector.appSlugInfoLabel).verifyVisibleElement( + "have.text", + "URL-friendly 'slug' consists of lowercase letters, numbers, and hyphens" + ); + + cy.get(commonWidgetSelector.appLinkLabel).verifyVisibleElement( + "have.text", + "App link" + ); + + cy.get(commonWidgetSelector.appLinkField).verifyVisibleElement( + "have.text", + `${host}/${workspaceId}/apps/${appId}` + ); + + // Validate all error cases + verifySlugValidations(commonWidgetSelector.appSlugInput); + + // Verify successful slug update + cy.clearAndType(commonWidgetSelector.appSlugInput, data.slug); + verifySuccessfulSlugUpdate(workspaceId, data.slug); + + // Verify persistence + cy.get('[data-cy="left-sidebar-debugger-button"]').click(); + cy.get(commonSelectors.leftSideBarSettingsButton).click(); + cy.get(commonWidgetSelector.appSlugInput).should("have.value", data.slug); + + // Release and verify URLs + releaseApp(); + verifyURLs(workspaceId, data.slug, true); + + // Verify duplicate slug validation + cy.visit("/my-workspace"); + cy.apiCreateApp(data.slug); + cy.openApp("my-workspace"); + cy.get(commonSelectors.leftSideBarSettingsButton).click(); + cy.get(commonWidgetSelector.appSlugInput).clear(); + cy.clearAndType(commonWidgetSelector.appSlugInput, data.slug); + cy.get(commonWidgetSelector.appSlugErrorLabel).verifyVisibleElement( + "have.text", + "This app slug is already taken." + ); }); it("Verify app slug cases in share modal", () => { - cy.apiLogin("dev@tooljet.io", "password").then(() => { - const workspaceId = Cypress.env("workspaceId"); + cy.apiLogin(); + const workspaceId = Cypress.env("workspaceId"); - cy.apiCreateApp(data.appName); - cy.openApp("my-workspace"); + cy.apiCreateApp(data.appName); + cy.openApp("my-workspace"); - // Set up initial slug - cy.get(commonSelectors.leftSideBarSettingsButton).click(); - cy.get(commonWidgetSelector.appSlugInput).clear(); - cy.clearAndType(commonWidgetSelector.appSlugInput, data.slug); + // Set up initial slug + cy.get(commonSelectors.leftSideBarSettingsButton).click(); + cy.get(commonWidgetSelector.appSlugInput).clear(); + cy.clearAndType(commonWidgetSelector.appSlugInput, data.slug); - releaseApp(); + releaseApp(); - // Verify share modal - cy.get(commonWidgetSelector.shareAppButton).click(); - cy.get(commonWidgetSelector.appLink).verifyVisibleElement( - "have.text", - `${host}/applications/` - ); - cy.get(commonWidgetSelector.appNameSlugInput).should( - "have.value", - data.slug - ); + // Verify share modal + cy.get(commonWidgetSelector.shareAppButton).click(); + cy.get(commonWidgetSelector.appLink).verifyVisibleElement( + "have.text", + `${host}/applications/` + ); + cy.get(commonWidgetSelector.appNameSlugInput).should( + "have.value", + data.slug + ); - // Validate all error cases in share modal - verifySlugValidations(commonWidgetSelector.appNameSlugInput); + // Validate all error cases in share modal + verifySlugValidations(commonWidgetSelector.appNameSlugInput); - cy.wait(500); - cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug); - cy.get('[data-cy="app-slug-info-label"]') - .invoke("text") - .then((text) => { - expect(text.trim()).to.eq( - "URL-friendly 'slug' consists of lowercase letters, numbers, and hyphens" - ); - }); + cy.wait(500); + cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug); + cy.get('[data-cy="app-slug-info-label"]') + .invoke("text") + .then((text) => { + expect(text.trim()).to.eq( + "URL-friendly 'slug' consists of lowercase letters, numbers, and hyphens" + ); + }); - // Verify successful slug update in share modal - data.slug = `${fake.companyName.toLowerCase()}-app`; - cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug); - cy.get('[data-cy="app-slug-accepted-label"]').verifyVisibleElement( - "have.text", - "Slug accepted!" - ); + // Verify successful slug update in share modal + data.slug = `${fake.companyName.toLowerCase()}-app`; + cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug); + cy.get('[data-cy="app-slug-accepted-label"]').verifyVisibleElement( + "have.text", + "Slug accepted!" + ); - // Close modal and verify URLs - cy.get(commonWidgetSelector.modalCloseButton).click(); - verifyURLs(workspaceId, data.slug, true); + // Close modal and verify URLs + cy.get(commonWidgetSelector.modalCloseButton).click(); + verifyURLs(workspaceId, data.slug, true); - // Verify duplicate slug validation in share modal - cy.visit("/my-workspace"); - cy.apiCreateApp(data.slug); - cy.openApp("my-workspace"); - releaseApp(); - cy.get(commonWidgetSelector.shareAppButton).click(); - cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug); - cy.get(commonWidgetSelector.appSlugErrorLabel).verifyVisibleElement( - "have.text", - "This app slug is already taken." - ); - }); + // Verify duplicate slug validation in share modal + cy.visit("/my-workspace"); + cy.apiCreateApp(data.slug); + cy.openApp("my-workspace"); + + releaseApp(); + cy.get(commonWidgetSelector.shareAppButton).click(); + cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug); + cy.get(commonWidgetSelector.appSlugErrorLabel).verifyVisibleElement( + "have.text", + "This app slug is already taken." + ); }); }); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/privateAndpublicApps.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/privateAndpublicApps.cy.js index 196113f2d2..a1e3ffadc5 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/privateAndpublicApps.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/privateAndpublicApps.cy.js @@ -5,322 +5,390 @@ import { inviteUserToWorkspace } from "Support/utils/manageUsers"; import { setSignupStatus } from "Support/utils/manageSSO"; import { onboardingSelectors } from "Selectors/onboarding"; import { commonText } from "Texts/common"; -import { - verifyConfirmEmailPage, - userSignUp, - addNewUser, -} from "Support/utils/onboarding"; +import { userSignUp, addNewUser } from "Support/utils/onboarding"; import { setUpSlug, setupAppWithSlug, verifyRestrictedAccess, onboardUserFromAppLink, } from "Support/utils/apps"; +import { appPromote } from "Support/utils/platform/multiEnv"; +import { InstanceSSO } from "Support/utils/platform/eeCommon"; +describe( + "Private and Public apps", + { + retries: { runMode: 2 }, + }, + () => { + let data; -describe("Private and Public apps", { - retries: { runMode: 2 }, -}, () => { - const data = {}; + beforeEach(() => { + data = { + appName: `${fake.companyName} P P App`, + slug: `${fake.companyName} P P App`.toLowerCase().replace(/\s+/g, "-"), + firstName: fake.firstName, + email: fake.email.toLowerCase(), + workspaceName: fake.firstName, + workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"), + }; - beforeEach(() => { - data.appName = `${fake.companyName} P P App`; - data.slug = data.appName.toLowerCase().replace(/\s+/g, "-"); - data.firstName = fake.firstName; - data.email = fake.email.toLowerCase(); - data.workspaceName = fake.firstName; - data.workspaceSlug = fake.firstName.toLowerCase().replace(/\s+/g, "-"); - - cy.defaultWorkspaceLogin(); - cy.skipWalkthrough(); - }); - - it("Verify private and public app share functionality", () => { - cy.apiCreateApp(data.appName); - cy.openApp(); - cy.apiAddComponentToApp(data.appName, "text1"); - - // Check unreleased version state - cy.get('[data-cy="share-button-link"]>span').should("be.visible").click(); - cy.contains("This version has not been released yet").should("be.visible"); - cy.get(commonWidgetSelector.modalCloseButton).click(); - - // Release and verify share modal - releaseApp(); - cy.get(commonWidgetSelector.shareAppButton).click(); - for (const elements in commonWidgetSelector.shareModalElements) { - cy.get(commonWidgetSelector.shareModalElements[elements]) - .verifyVisibleElement("have.text", commonText.shareModalElements[elements]); - } - - // Verify share modal elements - const shareModalSelectors = [ - 'copyAppLinkButton', - 'makePublicAppToggle', - 'appLink', - 'appNameSlugInput', - 'modalCloseButton' - ]; - shareModalSelectors.forEach(selector => { - cy.get(commonWidgetSelector[selector]).should("be.visible"); + cy.defaultWorkspaceLogin(); + cy.skipWalkthrough(); }); - // Configure and verify slug - cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug); - cy.get('[data-cy="app-slug-accepted-label"]') - .should("be.visible") - .and("have.text", "Slug accepted!"); + it("Verify private and public app share functionality", () => { + cy.apiCreateApp(data.appName); + cy.openApp(); + cy.apiAddComponentToApp(data.appName, "text1"); - cy.get(commonWidgetSelector.modalCloseButton).click(); - cy.forceClickOnCanvas(); - cy.backToApps(); + // Check unreleased version state + cy.get('[data-cy="share-button-link"]>span').should("be.visible").click(); + cy.contains("This version has not been released yet").should( + "be.visible" + ); + cy.get(commonWidgetSelector.modalCloseButton).click(); - // Test private access - logout(); - cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible"); + // Release and verify share modal + releaseApp(); + cy.get(commonWidgetSelector.shareAppButton).click(); + for (const elements in commonWidgetSelector.shareModalElements) { + cy.get( + commonWidgetSelector.shareModalElements[elements] + ).verifyVisibleElement( + "have.text", + commonText.shareModalElements[elements] + ); + } - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, - }); - cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible"); - cy.wait(2000); - cy.loginWithCredentials("dev@tooljet.io", "password"); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); + // Verify share modal elements + const shareModalSelectors = [ + "copyAppLinkButton", + "makePublicAppToggle", + "appLink", + "appNameSlugInput", + "modalCloseButton", + ]; + shareModalSelectors.forEach((selector) => { + cy.get(commonWidgetSelector[selector]).should("be.visible"); + }); - // Test public access - cy.get(commonSelectors.viewerPageLogo).click(); - cy.openApp( - "appSlug", - Cypress.env("workspaceId"), - Cypress.env("appId"), - commonWidgetSelector.draggableWidget("text1") - ); - cy.get(commonWidgetSelector.shareAppButton).click(); - cy.get(commonWidgetSelector.makePublicAppToggle).check(); - cy.get(commonWidgetSelector.modalCloseButton).click(); - cy.backToApps(); + // Configure and verify slug + cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug); + cy.get('[data-cy="app-slug-accepted-label"]') + .should("be.visible") + .and("have.text", "Slug accepted!"); - logout(); - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, - }); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); - }); + cy.get(commonWidgetSelector.modalCloseButton).click(); + cy.forceClickOnCanvas(); + cy.backToApps(); - it("Verify app private and public app visibility for the same workspace user", () => { - setupAppWithSlug(data.appName, data.slug); + // Test private access + logout(); - inviteUserToWorkspace(data.firstName, data.email); - logout(); + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); - // Test private access - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should( + "be.visible" + ); + cy.wait(2000); + cy.appUILogin(); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); + + // Test public access + // cy.get(commonSelectors.viewerPageLogo).click(); + cy.openApp( + "appSlug", + Cypress.env("workspaceId"), + Cypress.env("appId"), + commonWidgetSelector.draggableWidget("text1") + ); + cy.get(commonWidgetSelector.shareAppButton).click(); + cy.get(commonWidgetSelector.makePublicAppToggle).check(); + cy.get(commonWidgetSelector.modalCloseButton).click(); + cy.backToApps(); + + logout(); + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); }); - cy.wait(2000); - cy.loginWithCredentials(data.email, "password"); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); + it("Verify app private and public app visibility for the same workspace user", () => { + setupAppWithSlug(data.appName, data.slug); - // Test with private app valid session - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, - }); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); - cy.get(commonSelectors.viewerPageLogo).click(); + inviteUserToWorkspace(data.firstName, data.email); + logout(); + cy.visit("/"); + cy.wait(2000); + cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should( + "be.visible" + ); - // Test public access - cy.defaultWorkspaceLogin(); - cy.apiMakeAppPublic(); - logout(); + // Test private access + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, - }); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); + cy.wait(2000); + cy.appUILogin(data.email, "password"); - // Test with public app with valid session - cy.apiLogin(data.email, "password"); - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, - }); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); - }); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); - it("Verify app private and public app visibility for the same instance user", () => { - setupAppWithSlug(data.appName, data.slug); + // Test with private app valid session + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); - cy.apiLogout(); - userSignUp(data.firstName, data.email, data.workspaceName); - cy.wait(1000); - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + // cy.get(commonSelectors.viewerPageLogo).click(); + + // Test public access + cy.defaultWorkspaceLogin(); + cy.wait(1000); + cy.apiMakeAppPublic(); + logout(); + cy.wait(1000); + cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should( + "be.visible" + ); + + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); + + // Test with public app with valid session + cy.apiLogin(data.email, "password"); + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); }); - cy.visit("/"); - logout(); + it("Verify app private and public app visibility for the same instance user", () => { + setupAppWithSlug(data.appName, data.slug); - // Test public access - cy.defaultWorkspaceLogin(); - cy.apiMakeAppPublic(); - logout(); + cy.apiLogout(); + cy.ifEnv("Enterprise", () => { + InstanceSSO(true, true, true); + }); + userSignUp(data.firstName, data.email, data.workspaceName); + cy.wait(1000); + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, - }); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); + cy.visit("/"); + logout(); - // Verify public app with valid session - cy.apiLogin(data.email, "password"); - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, - }); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); - }); + // Test public access + cy.defaultWorkspaceLogin(); + cy.apiMakeAppPublic(); + logout(); - it("Should redirect to workspace login and handle signup flow of existing and non-existing user", () => { - setSignupStatus(true); - setupAppWithSlug(data.appName, data.slug); + cy.wait(1000); + cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should( + "be.visible" + ); - cy.apiLogout(); - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); + + // Verify public app with valid session + cy.apiLogin(data.email, "password"); + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); }); - cy.get(commonSelectors.workspaceName).verifyVisibleElement( - "have.text", - "My workspace" - ); + it("Should redirect to workspace login and handle signup flow of existing and non-existing user", () => { + setSignupStatus(true); + setupAppWithSlug(data.appName, data.slug); - // Test signup flow - cy.get(commonSelectors.createAnAccountLink).click(); - cy.wait(3000); - cy.clearAndType(commonSelectors.inputFieldFullName, data.firstName); - cy.clearAndType(commonSelectors.inputFieldEmailAddress, data.email); - cy.clearAndType(onboardingSelectors.loginPasswordInput, "password"); - cy.get(commonSelectors.signUpButton).click(); - verifyConfirmEmailPage(data.email); + cy.apiLogout(); + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); - // Process invitation - onboardUserFromAppLink(data.email, data.slug); + cy.get(commonSelectors.workspaceName).verifyVisibleElement( + "have.text", + "My workspace" + ); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); - cy.get('[data-cy="viewer-page-logo"]').click(); - logout(); + // Test signup flow + cy.intercept("POST", "/api/onboarding/signup").as("signup"); + cy.get(commonSelectors.createAnAccountLink).click(); + cy.wait(3000); + cy.clearAndType(commonSelectors.inputFieldFullName, data.firstName); + cy.clearAndType(commonSelectors.inputFieldEmailAddress, data.email); + cy.clearAndType(onboardingSelectors.loginPasswordInput, "password"); + cy.get(commonSelectors.signUpButton).click(); - // Setup new workspace and app - cy.defaultWorkspaceLogin(); - cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); - cy.apiLogout(); - cy.apiLogin(); - cy.visit(`${data.workspaceSlug}`); - setSignupStatus(true, data.workspaceName); + cy.wait("@signup").then((interception) => { + expect(interception.response.statusCode).to.eq(201); + }); - data.slug = fake.firstName.toLowerCase().replace(/\s+/g, "-"); + // Process invitation + onboardUserFromAppLink(data.email, data.slug); - cy.createApp(data.appName); - cy.dragAndDropWidget("Text", 500, 500); - releaseApp(); - setUpSlug(data.slug); - cy.forceClickOnCanvas(); - cy.backToApps(); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); - // Test signup flow in new workspace - cy.apiLogout(); - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + // cy.get('[data-cy="viewer-page-logo"]').click(); + cy.visit("/my-workspace"); + cy.wait(2000); + logout(); + cy.wait(1000); + cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should( + "be.visible" + ); + + // Setup new workspace and app + cy.defaultWorkspaceLogin(); + cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); + cy.apiLogout(); + cy.apiLogin(); + cy.visit(`${data.workspaceSlug}`); + setSignupStatus(true, data.workspaceName); + + data.slug = fake.firstName.toLowerCase().replace(/\s+/g, "-"); + + cy.createApp(data.appName); + + cy.wait(2000); + cy.dragAndDropWidget("Text", 500, 500); + releaseApp(); + setUpSlug(data.slug); + cy.forceClickOnCanvas(); + cy.backToApps(); + + // Test signup flow in new workspace + cy.apiLogout(); + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); + + cy.get(commonSelectors.workspaceName).verifyVisibleElement( + "have.text", + data.workspaceName + ); + + cy.get(commonSelectors.createAnAccountLink).click(); + cy.wait(3000); + cy.clearAndType(commonSelectors.inputFieldFullName, data.firstName); + cy.clearAndType(commonSelectors.inputFieldEmailAddress, data.email); + cy.clearAndType(onboardingSelectors.loginPasswordInput, "password"); + cy.get(commonSelectors.signUpButton).click(); + cy.wait("@signup").then((interception) => { + expect(interception.response.statusCode).to.eq(201); + }); + + onboardUserFromAppLink(data.email, data.slug, data.workspaceName, false); + cy.get(commonWidgetSelector.draggableWidget("text1")).should( + "be.visible" + ); }); - cy.get(commonSelectors.workspaceName).verifyVisibleElement( - "have.text", - data.workspaceName - ); + it("Should verify restricted app access", () => { + data.workspaceName = fake.firstName; + data.workspaceSlug = fake.firstName.toLowerCase().replace(/\s+/g, "-"); - cy.get(commonSelectors.createAnAccountLink).click(); - cy.wait(3000); - cy.clearAndType(commonSelectors.inputFieldFullName, data.firstName); - cy.clearAndType(commonSelectors.inputFieldEmailAddress, data.email); - cy.clearAndType(onboardingSelectors.loginPasswordInput, "password"); - cy.get(commonSelectors.signUpButton).click(); - verifyConfirmEmailPage(data.email); + cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); + cy.apiLogout(); + cy.apiLogin(); + cy.visit(`${data.workspaceSlug}`); - onboardUserFromAppLink(data.email, data.slug, data.workspaceName, false); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); - }); + cy.apiDeleteGranularPermission("end-user", ["app", "workflow"]); - it("Should verify restricted app access", () => { - data.workspaceName = fake.firstName; - data.workspaceSlug = fake.firstName.toLowerCase().replace(/\s+/g, "-"); + setSignupStatus(true, data.workspaceName); - cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); - cy.apiLogout(); - cy.apiLogin(); - cy.visit(`${data.workspaceSlug}`); - cy.apiDeleteGranularPermission("end-user"); - setSignupStatus(true, data.workspaceName); + setupAppWithSlug(data.appName, data.slug); - setupAppWithSlug(data.appName, data.slug); + inviteUserToWorkspace(data.firstName, data.email); - inviteUserToWorkspace(data.firstName, data.email); + // Verify restricted access + cy.visitSlug({ + actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }); + verifyRestrictedAccess(); + cy.get('[data-cy="back-to-home-button"]').click(); + cy.get(commonSelectors.homePageLogo).should("be.visible"); - // Verify restricted access - cy.visitSlug({ - actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + cy.apiLogout(); }); - verifyRestrictedAccess(); - cy.get('[data-cy="back-to-home-button"]').click(); - cy.get(commonSelectors.homePageLogo).should("be.visible"); - cy.apiLogout(); - }); + it.skip("Should verify private app access for different workspace users", () => { + const firstName1 = fake.firstName; + const email1 = fake.email.toLowerCase(); + const permissionName = fake.firstName.toLowerCase(); // Defined but not used in original + const urls = { + editor: `${Cypress.config("baseUrl")}/my-workspace/apps/${data.slug}/home`, + preview: `${Cypress.config("baseUrl")}/applications/${data.slug}/home?version=v1`, + released: `${Cypress.config("baseUrl")}/applications/${data.slug}`, + }; - it.skip("Should verify private app access for different workspace users", () => { - const firstName1 = fake.firstName; - const email1 = fake.email.toLowerCase(); - const permissionName = fake.firstName.toLowerCase(); // Defined but not used in original - const urls = { - editor: `${Cypress.config("baseUrl")}/my-workspace/apps/${data.slug}/home`, - preview: `${Cypress.config("baseUrl")}/applications/${data.slug}/home?version=v1`, - released: `${Cypress.config("baseUrl")}/applications/${data.slug}` - }; + // Setup workspace and app + cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); + cy.apiLogout(); + cy.apiLogin(); + cy.visit(`${data.workspaceSlug}`); + setupAppWithSlug(data.appName, data.slug); - // Setup workspace and app - cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); - cy.apiLogout(); - cy.apiLogin(); - cy.visit(`${data.workspaceSlug}`); - setupAppWithSlug(data.appName, data.slug); + // Invite workspace user + addNewUser(data.firstName, data.email); + cy.wait(500); - // Invite workspace user - addNewUser(data.firstName, data.email); - cy.wait(500); + // Verify access restrictions + cy.visitSlug({ actualUrl: urls.editor }); + verifyRestrictedAccess(); + cy.get('[data-cy="back-to-home-button"]').click(); + cy.get(commonSelectors.homePageLogo).should("be.visible"); - // Verify access restrictions - cy.visitSlug({ actualUrl: urls.editor }); - verifyRestrictedAccess(); - cy.get('[data-cy="back-to-home-button"]').click(); - cy.get(commonSelectors.homePageLogo).should("be.visible"); + cy.visitSlug({ actualUrl: urls.preview }); - cy.visitSlug({ actualUrl: urls.preview }); + // Switch users and verify access + cy.apiLogout(); + cy.apiLogin(); + cy.apiDeleteGranularPermission("end-user"); - // Switch users and verify access - cy.apiLogout(); - cy.apiLogin(); - cy.apiDeleteGranularPermission("end-user"); + cy.apiLogin(data.email, "password"); + cy.visitSlug({ actualUrl: urls.editor }); + verifyRestrictedAccess(); + cy.get('[data-cy="back-to-home-button"]').click(); + cy.get(commonSelectors.homePageLogo).should("be.visible"); + cy.visitSlug({ actualUrl: urls.preview }); - cy.apiLogin(data.email, "password"); - cy.visitSlug({ actualUrl: urls.editor }); - verifyRestrictedAccess(); - cy.get('[data-cy="back-to-home-button"]').click(); - cy.get(commonSelectors.homePageLogo).should("be.visible"); - cy.visitSlug({ actualUrl: urls.preview }); + cy.apiLogout(); - cy.apiLogout(); - - // Test with new user - userSignUp(firstName1, email1, data.workspaceName); - cy.visitSlug({ actualUrl: urls.editor }); - cy.visitSlug({ actualUrl: urls.preview }); - cy.visitSlug({ actualUrl: urls.released }); - }); -}); \ No newline at end of file + // Test with new user + userSignUp(firstName1, email1, data.workspaceName); + cy.visitSlug({ actualUrl: urls.editor }); + cy.visitSlug({ actualUrl: urls.preview }); + cy.visitSlug({ actualUrl: urls.released }); + }); + } +); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/version.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/version.cy.js index d03860574d..433d74b05e 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/version.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/apps/version.cy.js @@ -1,7 +1,6 @@ import { commonSelectors, commonWidgetSelector } from "Selectors/common"; import { fake } from "Fixtures/fake"; import { commonText } from "Texts/common"; - import { editVersionAndVerify, deleteVersionAndVerify, @@ -13,25 +12,20 @@ import { navigateToEditVersionModal, switchVersionAndVerify, } from "Support/utils/version"; - import { appVersionSelectors } from "Selectors/exportImport"; import { editVersionSelectors } from "Selectors/version"; import { editVersionText } from "Texts/version"; import { createNewVersion } from "Support/utils/exportImport"; - import { verifyModal, closeModal } from "Support/utils/common"; - import { verifyComponent, verifyComponentinrightpannel, deleteComponentAndVerify, } from "Support/utils/basicComponents"; - import { deleteVersionText, onlydeleteVersionText } from "Texts/version"; - import { createRestAPIQuery } from "Support/utils/dataSource"; import { deleteQuery } from "Support/utils/queries"; - +import { selectEnv, appPromote } from "Support/utils/platform/multiEnv"; describe("App Version", () => { let data; @@ -50,6 +44,8 @@ describe("App Version", () => { cy.defaultWorkspaceLogin(); cy.apiCreateApp(data.appName); cy.openApp(); + cy.viewport(1400, 1400); + }); it("should verify basic version management operations", () => { @@ -111,17 +107,41 @@ describe("App Version", () => { onlydeleteVersionText.deleteToastMessage("v3") ); cy.get(appVersionSelectors.currentVersionField("v2")).should("be.visible"); - cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible"); + cy.wait(3000); + + // cy.reload(); + cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible", { + timeout: 10000, + }); // Preview and release verification cy.openInCurrentTab(commonWidgetSelector.previewButton); - cy.url().should("include", "/home?version=v2"); - cy.openApp("", Cypress.env("workspaceId"), Cypress.env("appId"), commonWidgetSelector.draggableWidget("text1")); + + cy.ifEnv("Community", () => { + cy.url().should("include", "/home?version=v2"); + }); + + cy.ifEnv("Enterprise", () => { + cy.url().should("include", "/home?env=development&version=v2"); + }); + + cy.openApp( + "", + Cypress.env("workspaceId"), + Cypress.env("appId"), + commonWidgetSelector.draggableWidget("text1") + ); releasedVersionAndVerify("v2"); }); it("should verify version management with components and queries", () => { // Initial setup with component and datasource + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + data.datasourceName, + "restapi", + [{ key: "url", value: "https://jsonplaceholder.typicode.com/users" }] + ); cy.apiAddComponentToApp( data.appName, "text1", @@ -131,19 +151,19 @@ describe("App Version", () => { ); cy.waitForAutoSave(); - cy.apiCreateGDS( - `${Cypress.env("server_host")}/api/v2/data_sources`, - data.datasourceName, - "restapi", - [{ key: "url", value: "https://jsonplaceholder.typicode.com/users" }] - ); createRestAPIQuery(data.query1, data.datasourceName, "", "", "/1", true); - // Version v2 creation and verification + cy.ifEnv("Enterprise", () => { + appPromote("development", "production"); + }); + + // Version v2 creation and verification and v2 is created from v1 production environment navigateToCreateNewVersionModal("v1"); createNewVersion(["v2"], "v1"); - cy.get(commonWidgetSelector.draggableWidget("text1")) - .verifyVisibleElement("have.text", "Leanne Graham"); + cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement( + "have.text", + "Leanne Graham" + ); cy.get(`[data-cy="list-query-${data.query1}"]`).should("be.visible"); // Modify v2 with new components and queries @@ -167,67 +187,156 @@ describe("App Version", () => { create: { version: "v3", from: "v2" }, verify: { component: { selector: "textInput", value: "Ervin Howell" }, - query: data.query2 - } + query: data.query2, + }, }, { create: { version: "v4", from: "v1" }, verify: { component: { selector: "text1", text: "Leanne Graham" }, - query: data.query1 - } + query: data.query1, + }, }, { create: { version: "v5", from: "v3" }, verify: { component: { selector: "textInput", value: "Ervin Howell" }, - query: data.query2 - } - } + query: data.query2, + }, + }, ]; - versionChecks.forEach(check => { + versionChecks.forEach((check) => { navigateToCreateNewVersionModal(check.create.from); createNewVersion([check.create.version], check.create.from); - + cy.waitForAutoSave(); + cy.wait(1000); if (check.verify.component.value) { - cy.get(commonWidgetSelector.draggableWidget(check.verify.component.selector)) - .verifyVisibleElement("have.value", check.verify.component.value); + cy.get( + commonWidgetSelector.draggableWidget(check.verify.component.selector) + ).verifyVisibleElement("have.value", check.verify.component.value); } else { - cy.get(commonWidgetSelector.draggableWidget(check.verify.component.selector)) - .verifyVisibleElement("have.text", check.verify.component.text); + cy.get( + commonWidgetSelector.draggableWidget(check.verify.component.selector) + ).verifyVisibleElement("have.text", check.verify.component.text); } - cy.get(`[data-cy="list-query-${check.verify.query}"]`).should("be.visible"); + cy.get(`[data-cy="list-query-${check.verify.query}"]`).should( + "be.visible" + ); }); // Release and version state verification releasedVersionAndVerify("v5"); - cy.get(appVersionSelectors.currentVersionField("v5")) - .should("have.class", "color-light-green"); + cy.get(appVersionSelectors.currentVersionField("v5")).should( + "have.class", + "color-light-green" + ); // Version switching and component verification + cy.ifEnv("Enterprise", () => { + selectEnv("development"); + }); cy.get(appVersionSelectors.currentVersionField("v5")).click(); cy.contains(`[id*="react-select-"]`, "v4").click(); - cy.get(appVersionSelectors.currentVersionField("v4")) - .should("not.have.class", "color-light-green"); - cy.get(commonWidgetSelector.draggableWidget("text1")) - .verifyVisibleElement("have.text", "Leanne Graham"); + cy.get(appVersionSelectors.currentVersionField("v4")).should( + "not.have.class", + "color-light-green" + ); + cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement( + "have.text", + "Leanne Graham" + ); cy.get(`[data-cy="list-query-${data.query1}"]`).should("be.visible"); // Preview and version switching verification cy.openInCurrentTab(commonWidgetSelector.previewButton); - cy.url().should("include", "/home?version=v4"); - cy.get(commonWidgetSelector.draggableWidget("text1")) - .verifyVisibleElement("have.text", "Leanne Graham"); + + cy.ifEnv("Community", () => { + cy.url().should("include", "/home?version=v4"); + }); + cy.ifEnv("Enterprise", () => { + cy.url().should("include", "/home?env=development&version=v4"); + }); + + cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement( + "have.text", + "Leanne Graham" + ); cy.get('[data-cy="preview-settings"]').click(); switchVersionAndVerify("v4", "v5"); - cy.get(commonWidgetSelector.draggableWidget("textInput")) - .verifyVisibleElement("have.value", "Ervin Howell"); - //url validation should be added after bug fix + cy.get( + commonWidgetSelector.draggableWidget("textInput") + ).verifyVisibleElement("have.value", "Ervin Howell"); - // cy.url().should("include", "/home?version=v5"); + cy.ifEnv("Enterprise", () => { + cy.openApp( + "", + Cypress.env("workspaceId"), + Cypress.env("appId"), + commonWidgetSelector.draggableWidget("textInput") + ); + navigateToCreateNewVersionModal("v5"); + createNewVersion(["v6"], "v5"); + cy.waitForAutoSave(); + cy.wait(1000); + + appPromote("development", "staging"); + cy.get( + commonWidgetSelector.draggableWidget("textInput") + ).verifyVisibleElement("have.value", "Ervin Howell"); + cy.get(`[data-cy="list-query-${data.query2}"]`).should("be.visible"); + + appPromote("staging", "production"); + + cy.get( + commonWidgetSelector.draggableWidget("textInput") + ).verifyVisibleElement("have.value", "Ervin Howell"); + cy.get(`[data-cy="list-query-${data.query2}"]`).should("be.visible"); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + cy.get( + commonWidgetSelector.draggableWidget("textInput") + ).verifyVisibleElement("have.value", "Ervin Howell"); + cy.url().should("include", "/home?env=production&version=v6"); + + cy.wait(1000); + + cy.get('[data-cy="preview-settings"]').click(); + switchVersionAndVerify("v6", "v1"); + + cy.get( + commonWidgetSelector.draggableWidget("text1") + ).verifyVisibleElement("have.text", "Leanne Graham"); + // url bug + // cy.url().should("include", "/home?env=production&version=v1"); + cy.wait(1000); + cy.get('[data-cy="preview-settings"]').click(); + switchVersionAndVerify("v1", "v6"); + + cy.wait(1000); + cy.get('[data-cy="preview-settings"]').click(); + selectEnv("staging"); + + cy.get( + commonWidgetSelector.draggableWidget("textInput") + ).verifyVisibleElement("have.value", "Ervin Howell"); + // cy.url().should("include", "/home?env=staging&version=v6"); + + + cy.wait(1000); + cy.get('[data-cy="preview-settings"]').click(); + selectEnv("development"); + + cy.wait(1000); + cy.get('[data-cy="preview-settings"]').click(); + switchVersionAndVerify("v6", "v1"); + + cy.get( + commonWidgetSelector.draggableWidget("text1") + ).verifyVisibleElement("have.text", "Leanne Graham"); + }); }); }); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/dataSources/dataSourcePermissions.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/dataSources/dataSourcePermissions.cy.js index c985afbfaa..1a54829efd 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/dataSources/dataSourcePermissions.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/dataSources/dataSourcePermissions.cy.js @@ -19,6 +19,7 @@ import { } from "Support/utils/common"; import { inviteUserBasedOnRole } from "Support/utils/manageGroups"; import { resolveHost } from "Support/utils/apps"; +import { addSuccessNotification } from "Support/utils/queries"; const data = {}; data.firstName = fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", ""); @@ -32,7 +33,7 @@ describe("Datasource Manager", () => { beforeEach(() => { cy.apiLogin(); cy.visit(`${workspaceSlug}`); - cy.viewport(1200, 1300); + cy.viewport(1800, 1800); cy.skipWalkthrough(); }); @@ -46,8 +47,8 @@ describe("Datasource Manager", () => { data.dsName1 = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); data.dsName2 = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); - const allDataSources = host.includes("8082") ? "All data sources (42)" : "All data sources (44)"; - const allDatabase = host.includes("8082") ? "Databases (18)" : "Databases (20)"; + const allDataSources = host.includes("8082") ? "All data sources (45)" : "All data sources (45)"; + const allDatabase = host.includes("8082") ? "Databases (20)" : "Databases (20)"; cy.get(commonSelectors.globalDataSourceIcon).click(); cy.get(commonSelectors.pageSectionHeader).verifyVisibleElement( @@ -66,27 +67,27 @@ describe("Datasource Manager", () => { { selector: dataSourceSelector.commonDsLabelAndCount, text: "Commonly used (5)", - title: " Commonly used", + title: "Commonly used", }, { selector: dataSourceSelector.databaseLabelAndCount, text: allDatabase, - title: " Databases", + title: "Databases", }, { selector: dataSourceSelector.apiLabelAndCount, text: dataSourceText.allApis, - title: " APIs", + title: "APIs", }, { selector: dataSourceSelector.cloudStorageLabelAndCount, text: dataSourceText.allCloudStorage, - title: " Cloud Storages", + title: "Cloud Storages", }, { selector: dataSourceSelector.pluginsLabelAndCount, text: dataSourceText.pluginsLabelAndCount, - title: " Plugins", + title: "Plugins", }, ]; @@ -140,7 +141,7 @@ describe("Datasource Manager", () => { cy.get(commonSelectors.yesButton).click(); cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement( "have.text", - " Databases" + "Databases" ); cy.get(`[data-cy="cypress-${data.dsName1}-postgresql-button"]`).click(); cy.clearAndType( @@ -183,7 +184,7 @@ describe("Datasource Manager", () => { data.dsName1 ); - cy.intercept("GET", "/api/v2/data_sources").as("datasource"); + // cy.intercept("GET", "/api/v2/data_sources").as("datasource"); fillConnectionForm( { Host: Cypress.env("pg_host"), @@ -192,13 +193,14 @@ describe("Datasource Manager", () => { Username: Cypress.env("pg_user"), Password: Cypress.env("pg_password"), }, - ".form-switch" + '[data-cy="ssl-enabled-toggle-input"]' ); - cy.wait("@datasource"); + // cy.wait("@datasource"); + cy.wait(1000); cy.apiCreateApp(data.appName); cy.openApp(); - pinInspector(); + // pinInspector(); addQuery( "table_preview", @@ -211,9 +213,11 @@ describe("Datasource Manager", () => { "table_preview " ); - cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get('[data-cy="query-tab-settings"]').click(); + addSuccessNotification("table_preview"); cy.get(dataSourceSelector.queryCreateAndRunButton).click(); - verifyValueOnInspector("table_preview", "7 items "); + cy.verifyToastMessage(commonSelectors.toastMessage, "table_preview"); + cy.get('[data-cy="show-ds-popover-button"]').click(); cy.get(".p-2 > .tj-base-btn") @@ -223,7 +227,7 @@ describe("Datasource Manager", () => { cy.get('[data-cy="databases-datasource-button"]').should("be.visible"); cy.apiCreateGDS( - `${Cypress.env("server_host")}/api/v2/data_sources`, + `${Cypress.env("server_host")}/api/data-sources`, `cypress-${data.dsName2}-postgresql`, "postgresql", [ @@ -246,9 +250,13 @@ describe("Datasource Manager", () => { cy.get("#react-select-4-listbox") .contains(`cypress-${data.dsName2}-postgresql`) .click(); + + cy.get('[data-cy="query-tab-settings"]').click(); + addSuccessNotification("postgresql"); cy.waitForAutoSave(); cy.get(dataSourceSelector.queryCreateAndRunButton).click(); - verifyValueOnInspector("table_preview", "4 items "); + cy.verifyToastMessage(commonSelectors.toastMessage, "postgresql"); + }); it.skip("Should verify the query creation and scope changing functionality.", () => { @@ -274,7 +282,7 @@ describe("Datasource Manager", () => { pinInspector(); cy.get(dataSourceSelector.queryCreateAndRunButton).click(); - verifyValueOnInspector("table_preview", "7 items "); + verifyValueOnInspector("table_preview", "10 items "); //scope changing is pending }); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/groupDuplication.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/groupDuplication.cy.js index f8b39bea7b..ce82392019 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/groupDuplication.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/groupDuplication.cy.js @@ -18,10 +18,17 @@ import { roleBasedOnboarding } from "Support/utils/onboarding"; const data = {}; data.groupName = fake.firstName.replaceAll("[^A-Za-z]", ""); data.appName = `${fake.companyName}-App`; +const workspaceName = fake.firstName; +const workspaceSlug = fake.firstName.toLowerCase().replace(/[^A-Za-z]/g, ""); describe("Groups duplication", () => { beforeEach(() => { cy.defaultWorkspaceLogin(); + cy.apiCreateWorkspace(workspaceName, workspaceSlug); + cy.visit(`${workspaceSlug}`); + cy.apiLogout(); + cy.apiLogin(); + cy.visit(`${workspaceSlug}`); groupPermission( [ "appsCreateCheck", @@ -32,15 +39,18 @@ describe("Groups duplication", () => { "Admin" ); cy.apiCreateApp(data.appName); + }); it("Should verify the group duplication feature", () => { data.firstName = fake.firstName; data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); + cy.visit(`${workspaceSlug}`); roleBasedOnboarding(data.firstName, data.email, "builder"); cy.apiLogout(); - cy.defaultWorkspaceLogin(); + cy.apiLogin(); + cy.visit(`${workspaceSlug}`); navigateToManageGroups(); verifyGroupCardOptions("Admin"); cy.wait(3000); @@ -105,15 +115,19 @@ describe("Groups duplication", () => { cy.apiLogout(); cy.apiLogin(data.email, "password"); - cy.visit("/my-workspace"); + cy.visit(`${workspaceSlug}`); + cy.wait(2000); cy.get(commonSelectors.appCreateButton).should("be.visible"); cy.get(commonSelectors.createNewFolderButton).should("be.visible"); + cy.wait(2000); + cy.reload(); viewAppCardOptions(data.appName); cy.contains("Delete app").should("exist"); cy.get(commonSelectors.workspaceConstantsIcon).should("be.visible"); cy.apiLogout(); - cy.defaultWorkspaceLogin(); + cy.apiLogin(); + cy.visit(`${workspaceSlug}`); navigateToManageGroups(); OpenGroupCardOption(`${data.groupName}_copy`); cy.get(groupsSelector.deleteGroupOption).click(); @@ -121,7 +135,7 @@ describe("Groups duplication", () => { cy.apiLogout(); cy.apiLogin(data.email, "password"); - cy.visit("/my-workspace"); + cy.visit(`${workspaceSlug}`); cy.get(commonSelectors.appCreateButton).should("not.exist"); cy.get(commonSelectors.createNewFolderButton).should("not.exist"); cy.get(commonSelectors.workspaceConstantsIcon).should("not.exist"); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js index adb59c7aeb..6b195766b4 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js @@ -24,6 +24,7 @@ import { } from "Support/utils/dataSource"; import { dataSourceSelector } from "Selectors/dataSource"; +import { setUpSlug } from "Support/utils/apps"; const data = {}; @@ -39,6 +40,8 @@ describe("Workspace constants", () => { beforeEach(() => { cy.defaultWorkspaceLogin(); cy.skipWalkthrough(); + cy.viewport(1800, 1800); + }); it("Verify workspace constants UI and CRUD operations", () => { @@ -66,12 +69,11 @@ describe("Workspace constants", () => { }); }); - it("Verify global and secret constants in the editor, inspector, data sources, static queries, query preview, and preview", () => { + it.only("Verify global and secret constants in the editor, inspector, data sources, static queries, query preview, and preview", () => { data.workspaceName = fake.firstName; data.workspaceSlug = fake.firstName.toLowerCase().replace(/[^A-Za-z]/g, ""); cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); cy.visit(data.workspaceSlug); - cy.viewport(1440, 960); data.appName = `${fake.companyName}-App`; // create global constants @@ -80,8 +82,8 @@ describe("Workspace constants", () => { addNewconstants("restapiHeaderKey", "customHeader"); addNewconstants("restapiHeaderValue", "key=value"); addNewconstants("deleteConst", "deleteconst"); - addNewconstants("gconst", "236"); - addNewconstants("gconstUrl", "http://34.66.166.236:4000/"); + addNewconstants("gconst", "108"); + addNewconstants("gconstUrl", "http://20.29.40.108:4000/"); addNewconstants("gconstEndpoint", "production"); // create secret constants @@ -102,13 +104,23 @@ describe("Workspace constants", () => { .eq(0) .selectFile('cypress/fixtures/templates/workspace_constants.json', { force: true }); cy.get(importSelectors.importAppButton).click(); - cy.wait(5000); - + cy.wait(6000); + cy.get(commonWidgetSelector.draggableWidget('textinput1')).should('be.visible'); //Verify global constant value is resolved in component cy.get(commonWidgetSelector.draggableWidget('textinput1')) .verifyVisibleElement("have.value", "customHeader"); + //Verify all static and datasource queries output in components + cy.wait(8000); + for (let i = 3; i <= 16; i++) { + cy.wait(1000); + cy.log("Verifying textinput" + i); + cy.get(commonWidgetSelector.draggableWidget(`textinput${i}`)) + .verifyVisibleElement("have.value", "Production environment testing"); + } + //Verify secret constant value is not resolved in component and verify error message + cy.openComponentSidebar(); cy.get(commonWidgetSelector.draggableWidget('textinput2')) .verifyVisibleElement("have.value", "").click(); cy.get(commonWidgetSelector.defaultValueInputField).click(); @@ -116,15 +128,10 @@ describe("Workspace constants", () => { "secrets cannot be used in apps" ); - //Verify all static and datasource queries output in components - for (let i = 3; i <= 16; i++) { - cy.get(commonWidgetSelector.draggableWidget(`textinput${i}`)) - .verifyVisibleElement("have.value", "Production environment testing"); - } - //verify global constant is resolved in static query url cy.get('[data-cy="list-query-restapistaticg"]').click(); - cy.get('.rest-api-methods-select-element-container .codehinter-container').click(); + cy.get('.rest-api-methods-select-element-container .codehinter-container').eq(0).click(); + cy.wait(500) cy.get('.text-secondary').should('have.text', Cypress.env("constants_host")); //Verify global constant is resolved in static query preview @@ -141,29 +148,35 @@ describe("Workspace constants", () => { cy.get(dataSourceSelector.previewTabRawContainer).contains("secrets is not defined"); //verify global const should be visible, secrets and deleted const are not in Inspector - cy.get(commonWidgetSelector.inspectorIcon).click(); - cy.get(commonWidgetSelector.constantInspectorIcon).click(); - cy.get('[data-cy="inspector-node-restapiheaderkey"]').should('exist'); - cy.get('[data-cy="inspector-node-deleteconst"]').should('not.exist'); - cy.get('[data-cy="inspector-node-sconst"]').should('not.exist'); + // cy.get(commonWidgetSelector.sidebarinspector).click(); + // cy.get(commonWidgetSelector.constantInspectorIcon).click(); + // cy.get('[data-cy="inspector-node-restapiheaderkey"]').should('exist'); + // cy.get('[data-cy="inspector-node-deleteconst"]').should('not.exist'); + // cy.get('[data-cy="inspector-node-sconst"]').should('not.exist'); //Preview app and verify components cy.openInCurrentTab(commonWidgetSelector.previewButton); - cy.wait(6000); - for (let i = 3; i <= 16; i++) { + cy.wait(8000); + cy.get(commonWidgetSelector.draggableWidget('textinput1')).should('be.visible'); + for (let i = 16; i >= 3; i--) { + cy.wait(1000); + cy.get(commonWidgetSelector.draggableWidget(`textinput${i}`)).should('be.visible'); cy.get(commonWidgetSelector.draggableWidget(`textinput${i}`)) - .verifyVisibleElement("have.value", "Production environment testing"); + .verifyVisibleElement("have.value", "Production environment testing", { timeout: 10000 }); } - //back to dashboard and open app again - cy.get(commonSelectors.viewerPageLogo).click(); - cy.wait(2000); + + cy.visit('/'); + cy.wait(4000); cy.get(commonSelectors.appEditButton).click({ force: true }); + cy.wait(4000); cy.releaseApp(); - + setUpSlug(data.slug); + cy.forceClickOnCanvas(); cy.backToApps(); + //Verify global are getting resolved and secrets are hidded in the data source form cy.get(commonSelectors.globalDataSourceIcon).click(); cy.get('[data-cy="restapig-button"]').click(); @@ -176,5 +189,17 @@ describe("Workspace constants", () => { verifyConstantValueVisibility('[value="{{secrets.restapiHeaderValue}}"]', workspaceConstantsText.secretsHiddenText); cy.get('[data-cy="restapiurlgs-button"]').click(); verifyConstantValueVisibility(dataSourceSelector.baseUrlTextField, workspaceConstantsText.secretsHiddenText); + + + cy.visitSlug({ actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}` }); + cy.wait(8000); + cy.get(commonWidgetSelector.draggableWidget('textinput1')).should('be.visible'); + for (let i = 16; i >= 3; i--) { + cy.wait(1000); + cy.get(commonWidgetSelector.draggableWidget(`textinput${i}`)).should('be.visible'); + cy.get(commonWidgetSelector.draggableWidget(`textinput${i}`)) + .verifyVisibleElement("have.value", "Production environment testing", { timeout: 10000 }); + } + }) }); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/Login.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/Login.cy.js index bc3d5f3894..ad30526421 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/Login.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/Login.cy.js @@ -9,6 +9,7 @@ describe("Login functionality", () => { let user; const invalidEmail = fake.email; const invalidPassword = fake.password; + const envVar = Cypress.env("environment"); beforeEach(() => { cy.fixture("credentials/login.json").then((login) => { diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/Signup.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/Signup.cy.js index 248562f9dc..3cc22ce9b8 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/Signup.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/Signup.cy.js @@ -15,11 +15,19 @@ import { } from "Support/utils/selfHostSignUp"; import { onboardingSelectors } from "Selectors/onboarding"; import { logout } from "Support/utils/common"; +import { enableInstanceSignup } from "Support/utils/manageSSO"; describe("User signup", () => { const data = {}; let invitationLink = ""; + before(() => { + cy.ifEnv("Enterprise", () => { + enableInstanceSignup() + }); + + }); + it("Verify the signup flow and UI elements", () => { data.fullName = fake.fullName; data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); @@ -41,7 +49,7 @@ describe("User signup", () => { cy.wait(500); verifyConfirmEmailPage(data.email); - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select invitation_token from users where email='${data.email}';`, }).then((resp) => { @@ -67,9 +75,8 @@ describe("User signup", () => { data.workspaceName = fake.companyName; cy.visit("/"); - cy.wait(8000); cy.get(onboardingSelectors.createAnAccountLink).click(); - cy.wait(6000); + cy.wait(2000); cy.get(onboardingSelectors.nameInput).clear(); cy.get(onboardingSelectors.nameInput).type(data.fullName); cy.clearAndType(onboardingSelectors.signupEmailInput, data.email); @@ -77,14 +84,17 @@ describe("User signup", () => { onboardingSelectors.loginPasswordInput, commonText.password ); + cy.intercept("POST", "/api/onboarding/signup").as("signup"); cy.get(commonSelectors.signUpButton).click(); - cy.wait(8000); - cy.get(commonSelectors.resendEmailButton).click(); - cy.task("updateId", { + + cy.wait("@signup") + cy.get('[data-cy="check-your-mail-header"]').should("be.visible"); + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select invitation_token from users where email='${data.email}';`, }).then((resp) => { invitationLink = `/invitations/${resp.rows[0].invitation_token}`; + cy.visit(invitationLink); }); }); }); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/UserInviteFlow.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/UserInviteFlow.cy.js index 0851933e1a..be286792a5 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/UserInviteFlow.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/UserInviteFlow.cy.js @@ -11,15 +11,8 @@ import { inviteUserWithUserRole, fetchAndVisitInviteLink, } from "Support/utils/manageUsers"; -import { addNewUser, visitWorkspaceInvitation, addNewUsertoworkspace } from "Support/utils/onboarding"; import { commonText } from "Texts/common"; -import { setSignupStatus } from "Support/utils/manageSSO"; -import { ssoSelector } from "Selectors/manageSSO"; -import { - SignUpPageElements, - signUpLink, - verifyOnboardingQuestions, -} from "Support/utils/onboarding"; +import { visitWorkspaceInvitation, addNewUser } from "Support/utils/onboarding"; import { navigateToManageUsers, @@ -30,6 +23,8 @@ import { import { groupsSelector } from "Selectors/manageGroups"; import { groupsText } from "Texts/manageGroups"; import { onboardingSelectors } from "Selectors/onboarding"; +import { enableInstanceSignup } from "Support/utils/manageSSO"; + let invitationToken, organizationToken, @@ -38,11 +33,16 @@ let invitationToken, url = ""; const data = {}; +const envVar = Cypress.env("environment"); describe("user invite flow cases", () => { beforeEach(() => { cy.defaultWorkspaceLogin(); + cy.ifEnv("Enterprise", () => { + enableInstanceSignup() + }); }); + it("Should verify the Manage users page", () => { data.firstName = fake.firstName; data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); @@ -264,6 +264,8 @@ describe("user invite flow cases", () => { cy.wait(1000); cy.defaultWorkspaceLogin(); + cy.get(commonSelectors.homePageLogo, { timeout: 10000 }).should("be.visible"); + navigateToManageGroups(); cy.get(groupsSelector.groupLink(data.groupName1)).click(); cy.get(groupsSelector.usersLink).click(); @@ -350,15 +352,24 @@ describe("user invite flow cases", () => { "have.text", data.email ); - cy.get('[data-cy="modal-body"]>').verifyVisibleElement( - "have.text", - "Updating the user's details will change their role from end-user to admin. Are you sure you want to continue?" - ); + + if (envVar === "Enterprise") { + cy.get('[data-cy="modal-body"]>').verifyVisibleElement( + "have.text", + "Changing user default group from end-user to admin will affect the count of users covered by your plan.Are you sure you want to continue?" + ); + } else { + cy.get('[data-cy="modal-body"]>').verifyVisibleElement( + "have.text", + "Changing the user role from end-user to admin will grant the user access to all resources and settings.Are you sure you want to continue?" + ); + } + cy.get('.modal-footer > [data-cy="cancel-button"]').verifyVisibleElement( "have.text", "Cancel" ); - cy.get('[data-cy="confim-button"]').verifyVisibleElement( + cy.get('[data-cy="confirm-button"]').verifyVisibleElement( "have.text", "Continue" ); @@ -396,7 +407,7 @@ describe("user invite flow cases", () => { cy.get('[data-cy="group-check-input"]').eq(0).check(); cy.get(usersSelector.buttonInviteUsers).click(); - cy.get('[data-cy="confim-button"]').click(); + cy.get('[data-cy="confirm-button"]').click(); cy.verifyToastMessage( commonSelectors.toastMessage, @@ -415,7 +426,7 @@ describe("user invite flow cases", () => { cy.get('[data-cy="group-check-input"]').eq(0).check(); cy.get(usersSelector.buttonInviteUsers).click(); - cy.get('[data-cy="confim-button"]').click(); + cy.get('[data-cy="confirm-button"]').click(); cy.verifyToastMessage( commonSelectors.toastMessage, @@ -427,153 +438,4 @@ describe("user invite flow cases", () => { "Builder" ); }); - - it("Should verify exisiting user invite flow", () => { - data.firstName = fake.firstName; - data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); - const workspaceName = data.firstName.toLowerCase(); - - addNewUser(data.firstName, data.email); - logout(); - - cy.defaultWorkspaceLogin(); - cy.apiCreateWorkspace(workspaceName, workspaceName); - cy.visit(workspaceName); - - navigateToManageUsers(); - fillUserInviteForm(data.firstName, data.email); - cy.get(usersSelector.buttonInviteUsers).click(); - cy.wait(2000); - visitWorkspaceInvitation(data.email, workspaceName); - cy.wait(3000); - cy.clearAndType(onboardingSelectors.loginEmailInput, data.email); - cy.clearAndType(onboardingSelectors.loginPasswordInput, "password"); - cy.get(onboardingSelectors.signInButton).click(); - cy.get(usersSelector.acceptInvite).click(); - cy.verifyToastMessage(commonSelectors.toastMessage, usersText.inviteToast); - logout(); - - cy.defaultWorkspaceLogin(); - navigateToManageUsers(); - searchUser(data.email); - cy.contains("td", data.email) - .parent() - .within(() => { - cy.get("td small").should("have.text", usersText.activeStatus); - }); - }); - - it("should verify the user signup after invited in a workspace", () => { - data.firstName = fake.firstName; - data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); - data.signUpName = fake.firstName; - data.workspaceName = fake.companyName; - - setSignupStatus(true); - navigateToManageUsers(); - fillUserInviteForm(data.firstName, data.email); - cy.get(usersSelector.buttonInviteUsers).click(); - cy.apiLogout(); - - cy.visit("/"); - cy.get(commonSelectors.createAnAccountLink).click(); - SignUpPageElements(); - cy.wait(3000); - cy.clearAndType(onboardingSelectors.nameInput, data.signUpName); - cy.clearAndType(onboardingSelectors.signupEmailInput, data.email); - cy.clearAndType( - onboardingSelectors.loginPasswordInput, - commonText.password - ); - cy.get(commonSelectors.signUpButton).click(); - cy.wait(1000); - signUpLink(data.email); - cy.wait(1000); - visitWorkspaceInvitation(data.email, "My workspace"); - cy.clearAndType(onboardingSelectors.signupEmailInput, data.email); - cy.clearAndType(onboardingSelectors.loginPasswordInput, usersText.password); - cy.get(onboardingSelectors.signInButton).click(); - cy.wait(3000); - cy.get(commonSelectors.invitedUserName).verifyVisibleElement( - "have.text", - data.signUpName - ); - cy.get(commonSelectors.acceptInviteButton).click(); - }); - - it("should verify the user signup with same creds after invited in a workspace", () => { - data.firstName = fake.firstName; - data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); - data.signUpName = fake.firstName; - data.workspaceName = fake.companyName; - - setSignupStatus(true); - navigateToManageUsers(); - fillUserInviteForm(data.firstName, data.email); - cy.get(usersSelector.buttonInviteUsers).click(); - logout(); - - cy.get(commonSelectors.createAnAccountLink).click(); - SignUpPageElements(); - cy.wait(5000); - - cy.clearAndType(onboardingSelectors.nameInput, data.signUpName); - cy.clearAndType(onboardingSelectors.signupEmailInput, data.email); - cy.clearAndType( - onboardingSelectors.loginPasswordInput, - commonText.password - ); - cy.get(commonSelectors.signUpButton).click(); - cy.verifyToastMessage( - commonSelectors.toastMessage, - "The user is already registered. Please check your inbox for the activation link" - ); - }); - - it("should verify exisiting user workspace signup from instance using form", () => { - data.firstName = fake.firstName; - data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); - data.signUpName = fake.firstName; - data.workspaceName = fake.firstName.toLowerCase(); - - setSignupStatus(true); - navigateToManageUsers(); - addNewUser(data.firstName, data.email); - logout(); - cy.wait(3000); - cy.get(commonSelectors.createAnAccountLink).click(); - cy.wait(1000); - cy.clearAndType(onboardingSelectors.nameInput, data.firstName); - cy.clearAndType(onboardingSelectors.signupEmailInput, data.email); - cy.clearAndType( - onboardingSelectors.loginPasswordInput, - commonText.password - ); - cy.get(commonSelectors.signUpButton).click(); - cy.verifyToastMessage( - commonSelectors.toastMessage, - "User already exists in the workspace." - ); - cy.apiLogin(); - cy.apiCreateWorkspace(data.workspaceName, data.workspaceName); - cy.visit(`${data.workspaceName}`); - cy.wait(3000); - setSignupStatus(true, data.workspaceName); - logout(); - - cy.get(commonSelectors.createAnAccountLink).click(); - cy.wait(3000); - cy.clearAndType(onboardingSelectors.nameInput, data.firstName); - cy.clearAndType(onboardingSelectors.signupEmailInput, data.email); - cy.clearAndType( - onboardingSelectors.loginPasswordInput, - commonText.password - ); - cy.get(commonSelectors.signUpButton).click(); - - cy.defaultWorkspaceLogin(); - visitWorkspaceInvitation(data.email, data.workspaceName); - cy.verifyToastMessage(commonSelectors.toastMessage, usersText.inviteToast); - logout(); - }); }); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/bulkUsersUpload.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/bulkUsersUpload.cy.js index b769f09d64..32e0a6c064 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/bulkUsersUpload.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/bulkUsersUpload.cy.js @@ -1,59 +1,126 @@ import { commonSelectors } from "Selectors/common"; -import { usersText } from "Texts/manageUsers"; import { usersSelector } from "Selectors/manageUsers"; import { groupsSelector } from "Selectors/manageGroups"; import { fake } from "Fixtures/fake"; import * as common from "Support/utils/common"; import { bulkUserUpload } from "Support/utils/manageUsers"; +// Helper to resolve correct test data based on env +const getFile = (fileGroup) => { + const env = Cypress.env("environment"); + return env === "Community" ? fileGroup.default : fileGroup.alt; +}; + + describe("Bulk User Upload", () => { - // Test data configuration const TEST_FILES = { MISSING_NAME: { - path: "cypress/fixtures/bulkUser/without_name.csv", - fileName: "without_name", - error: - "Missing first_name,last_name,groups information in 2 row(s);. No users were uploaded, please update and try again.", + default: { + path: "cypress/fixtures/bulkUser/missing_name.csv", + fileName: "missing_name", + error: + "Missing first_name,last_name,groups information in 2 row(s);. No users were uploaded, please update and try again.", + }, + alt: { + path: "cypress/fixtures/bulkUser/missing_name_ee.csv", + fileName: "missing_name_ee", + error: + "Missing first_name,last_name,groups,metadata,userMetadata information in 2 row(s);. No users were uploaded, please update and try again.", + }, }, MISSING_EMAIL: { - path: "cypress/fixtures/bulkUser/without_email.csv", - fileName: "without_email", - error: - "Missing email,groups information in 2 row(s);. No users were uploaded, please update and try again.", + default: { + path: "cypress/fixtures/bulkUser/missing_email.csv", + fileName: "missing_email", + error: + "Missing email,groups information in 2 row(s);. No users were uploaded, please update and try again.", + }, + alt: { + path: "cypress/fixtures/bulkUser/missing_email_ee.csv", + fileName: "missing_email_ee", + error: + "Missing first_name,last_name,groups,metadata,userMetadata information in 2 row(s);. No users were uploaded, please update and try again.", + }, }, DUPLICATE_EMAIL: { - path: "cypress/fixtures/bulkUser/same_email.csv", - fileName: "same_email", - error: "Duplicate email found. Please provide a unique email address.", - isDuplicate: true, + default: { + path: "cypress/fixtures/bulkUser/same_email.csv", + fileName: "same_email", + error: "Duplicate email found. Please provide a unique email address.", + isDuplicate: true, + }, + alt: { + path: "cypress/fixtures/bulkUser/same_email_ee.csv", + fileName: "same_email_ee", + error: "Duplicate email found. Please provide a unique email address.", + isDuplicate: true, + }, }, EMPTY_NAMES: { - path: "cypress/fixtures/bulkUser/empty_first_and_last_name.csv", - fileName: "empty_first_and_last_name", - error: - "Missing first_name,last_name,groups information in 1 row(s);. No users were uploaded, please update and try again.", + default: { + path: "cypress/fixtures/bulkUser/empty_names.csv", + fileName: "empty_names", + error: + "Missing first_name,last_name,groups information in 1 row(s);. No users were uploaded, please update and try again.", + }, + alt: { + path: "cypress/fixtures/bulkUser/empty_names_ee.csv", + fileName: "empty_names_ee", + error: + "Missing first_name,last_name,groups,metadata,userMetadata information in 1 row(s);. No users were uploaded, please update and try again.", + }, }, LIMIT_EXCEEDED: { - path: "cypress/fixtures/bulkUser/500_invite_users.csv", - fileName: "500_invite_users", - error: "You can only invite 250 users at a time", + default: { + path: "cypress/fixtures/bulkUser/limit_exceeded.csv", + fileName: "limit_exceeded", + error: "You can only invite 250 users at a time", + }, + alt: { + path: "cypress/fixtures/bulkUser/limit_exceeded_ee.csv", + fileName: "limit_exceeded_ee", + error: "You can only invite 250 users at a time", + }, }, MISSING_ROLE: { - path: "cypress/fixtures/bulkUser/without_role.csv", - fileName: "without_role", - error: - "Missing user_role,groups information in 2 row(s);. No users were uploaded, please update and try again.", + default: { + path: "cypress/fixtures/bulkUser/missing_role.csv", + fileName: "missing_role", + error: + "Missing user_role,groups information in 2 row(s);. No users were uploaded, please update and try again.", + }, + alt: { + path: "cypress/fixtures/bulkUser/missing_role_ee.csv", + fileName: "missing_role_ee", + error: + "Missing user_role,groups,metadata,userMetadata information in 2 row(s);. No users were uploaded, please update and try again.", + }, }, NONEXISTENT_GROUP: { - path: "cypress/fixtures/bulkUser/non_existing_group.csv", - fileName: "non_existing_group", - error: "2 groups doesn't exist. No users were uploaded", + default: { + path: "cypress/fixtures/bulkUser/non_existing_group.csv", + fileName: "non_existing_group", + error: "2 groups doesn't exist. No users were uploaded", + }, + alt: { + path: "cypress/fixtures/bulkUser/non_existing_group_ee.csv", + fileName: "non_existing_group_ee", + error: "2 groups doesn't exist. No users were uploaded", + }, }, VALID_USERS: { - path: "cypress/fixtures/bulkUser/3usersupload.csv", - fileName: "3usersupload", - testEmail: "test12@gmail.com", - successMessage: "3 users are being added", + default: { + path: "cypress/fixtures/bulkUser/3_users_upload.csv", + fileName: "3_users_upload", + successMessage: "3 users are being added", + email: "test12@gmail.com", + }, + alt: { + path: "cypress/fixtures/bulkUser/3_users_upload_ee.csv", + fileName: "3_users_upload_ee", + successMessage: "3 users are being added", + email: "test12@gmail.com", + }, }, }; @@ -70,7 +137,6 @@ describe("Bulk User Upload", () => { cy.get(usersSelector.buttonAddUsers).click(); cy.get(usersSelector.buttonUploadCsvFile).click(); - // Test all error cases [ TEST_FILES.MISSING_ROLE, TEST_FILES.MISSING_NAME, @@ -79,7 +145,8 @@ describe("Bulk User Upload", () => { TEST_FILES.EMPTY_NAMES, TEST_FILES.NONEXISTENT_GROUP, TEST_FILES.LIMIT_EXCEEDED, - ].forEach((testCase) => { + ].forEach((testCaseGroup) => { + const testCase = getFile(testCaseGroup); bulkUserUpload( testCase.path, testCase.fileName, @@ -90,32 +157,30 @@ describe("Bulk User Upload", () => { }); it("Should successfully upload valid users", () => { + const file = getFile(TEST_FILES.VALID_USERS); cy.get(usersSelector.buttonAddUsers).click(); cy.get(usersSelector.buttonUploadCsvFile).click(); - cy.get(usersSelector.inputFieldBulkUpload).selectFile( - TEST_FILES.VALID_USERS.path, - { - force: true, - } - ); - cy.get(commonSelectors.fileSelector).should( - "contain", - TEST_FILES.VALID_USERS.fileName - ); + cy.get(usersSelector.inputFieldBulkUpload).selectFile(file.path, { + force: true, + }); + + cy.get(commonSelectors.fileSelector).should("contain", file.fileName); cy.get(usersSelector.buttonUploadUsers).click(); cy.get(".go2072408551") .should("be.visible") - .and("have.text", TEST_FILES.VALID_USERS.successMessage); - common.searchUser("test12@gmail.com"); - cy.contains("td", "test12@gmail.com") + .and("have.text", file.successMessage); + + common.searchUser(file.email); + cy.contains("td", file.email) .parent() .within(() => { cy.get("td small").should("have.text", "invited"); }); + common.navigateToManageGroups(); cy.get(groupsSelector.groupLink("Admin")).click(); cy.get(groupsSelector.usersLink).click(); - cy.contains("test12@gmail.com").should("be.visible"); + cy.contains(file.email).should("be.visible"); }); }); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/resetPassword.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/resetPassword.cy.js index 7805bec5bf..caa67b2544 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/resetPassword.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/resetPassword.cy.js @@ -82,7 +82,7 @@ describe("Password reset functionality", () => { }); // Get and visit reset password link - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select forgot_password_token from users where email='${data.email}';`, }).then((resp) => { diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/userInviteFlowEdgeCases.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/userInviteFlowEdgeCases.cy.js new file mode 100644 index 0000000000..0769f2fbf3 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/userManagment/userInviteFlowEdgeCases.cy.js @@ -0,0 +1,148 @@ +import { commonSelectors } from "Selectors/common"; +import { fake } from "Fixtures/fake"; +import { usersText } from "Texts/manageUsers"; +import { usersSelector } from "Selectors/manageUsers"; +import { fillUserInviteForm } from "Support/utils/manageUsers"; +import { commonText } from "Texts/common"; +import { setSignupStatus } from "Support/utils/manageSSO"; +import { + SignUpPageElements, + signUpLink, + verifyOnboardingQuestions, + visitWorkspaceInvitation, + addNewUser, + enableInstanceSignUp, +} from "Support/utils/onboarding"; + +import { + navigateToManageUsers, + logout, + searchUser, +} from "Support/utils/common"; + +import { onboardingSelectors } from "Selectors/onboarding"; +import { enableInstanceSignup } from "Support/utils/manageSSO"; + +const data = {}; +const envVar = Cypress.env("environment"); + +describe("inviteflow edge cases", () => { + beforeEach(() => { + cy.defaultWorkspaceLogin(); + cy.ifEnv("Enterprise", () => { + enableInstanceSignup(); + }); + }); + + it("Should verify exisiting user invite flow", () => { + data.firstName = fake.firstName; + data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); + const workspaceName = data.firstName.toLowerCase(); + + addNewUser(data.firstName, data.email); + logout(); + + cy.defaultWorkspaceLogin(); + cy.apiCreateWorkspace(workspaceName, workspaceName); + cy.visit(workspaceName); + + navigateToManageUsers(); + fillUserInviteForm(data.firstName, data.email); + cy.get(usersSelector.buttonInviteUsers).click(); + cy.wait(2000); + visitWorkspaceInvitation(data.email, workspaceName); + cy.wait(3000); + cy.clearAndType(onboardingSelectors.loginEmailInput, data.email); + cy.clearAndType(onboardingSelectors.loginPasswordInput, "password"); + cy.get(onboardingSelectors.signInButton).click(); + cy.get(usersSelector.acceptInvite).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, usersText.inviteToast); + logout(); + + cy.apiLogin(); + cy.visit(workspaceName); + navigateToManageUsers(); + searchUser(data.email); + cy.contains("td", data.email) + .parent() + .within(() => { + cy.get("td small").should("have.text", usersText.activeStatus); + }); + }); + + it("should verify the user signup with same creds after invited in a workspace", () => { + data.firstName = fake.firstName; + data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); + data.signUpName = fake.firstName; + data.workspaceName = fake.companyName; + + setSignupStatus(true); + navigateToManageUsers(); + fillUserInviteForm(data.firstName, data.email); + cy.get(usersSelector.buttonInviteUsers).click(); + logout(); + + cy.get(commonSelectors.createAnAccountLink).click(); + SignUpPageElements(); + cy.wait(5000); + + cy.clearAndType(onboardingSelectors.nameInput, data.signUpName); + cy.clearAndType(onboardingSelectors.signupEmailInput, data.email); + cy.clearAndType( + onboardingSelectors.loginPasswordInput, + commonText.password + ); + cy.get(commonSelectors.signUpButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "The user is already registered. Please check your inbox for the activation link" + ); + }); + + it("should verify exisiting user workspace signup from instance using form", () => { + data.firstName = fake.firstName; + data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); + data.signUpName = fake.firstName; + data.workspaceName = fake.firstName.toLowerCase(); + + setSignupStatus(true); + navigateToManageUsers(); + addNewUser(data.firstName, data.email); + logout(); + cy.wait(3000); + cy.get(commonSelectors.createAnAccountLink).click(); + cy.wait(1000); + cy.clearAndType(onboardingSelectors.nameInput, data.firstName); + cy.clearAndType(onboardingSelectors.signupEmailInput, data.email); + cy.clearAndType( + onboardingSelectors.loginPasswordInput, + commonText.password + ); + cy.get(commonSelectors.signUpButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "User already exists in the workspace." + ); + cy.apiLogin(); + cy.apiCreateWorkspace(data.workspaceName, data.workspaceName); + cy.visit(`${data.workspaceName}`); + cy.wait(3000); + setSignupStatus(true, data.workspaceName); + logout(); + + cy.get(commonSelectors.createAnAccountLink).click(); + cy.wait(3000); + cy.clearAndType(onboardingSelectors.nameInput, data.firstName); + cy.clearAndType(onboardingSelectors.signupEmailInput, data.email); + cy.clearAndType( + onboardingSelectors.loginPasswordInput, + commonText.password + ); + cy.get(commonSelectors.signUpButton).click(); + + cy.defaultWorkspaceLogin(); + visitWorkspaceInvitation(data.email, data.workspaceName); + cy.verifyToastMessage(commonSelectors.toastMessage, usersText.inviteToast); + logout(); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/appCreate.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/appCreate.cy.js index 1a40c978c8..c3021b6aa8 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/appCreate.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/appCreate.cy.js @@ -8,7 +8,7 @@ import { importText } from "Texts/exportImport"; describe("App creation", () => { const data = {}; - const appFile = "cypress/fixtures/templates/test-app.json"; + const appFile = "cypress/fixtures/templates/one_version.json"; beforeEach(() => { cy.defaultWorkspaceLogin(); @@ -200,7 +200,7 @@ describe("App creation", () => { force: true, }); - cy.get(commonSelectors.importAppTitle).verifyVisibleElement( + cy.get(importSelectors.importAppTitle).verifyVisibleElement( "have.text", "Import app" ); @@ -210,7 +210,7 @@ describe("App creation", () => { ); cy.get(commonSelectors.appNameInput).verifyVisibleElement( "have.value", - "test-app" + "one_version" ); cy.get(commonSelectors.appNameInfoLabel).verifyVisibleElement( "have.text", @@ -236,7 +236,7 @@ describe("App creation", () => { }); cy.get(commonSelectors.appNameInput).verifyVisibleElement( "have.value", - "test-app" + "one_version" ); cy.clearAndType(commonSelectors.appNameInput, data.appName); cy.get(commonSelectors.cancelButton).click(); @@ -247,7 +247,7 @@ describe("App creation", () => { }); cy.get(commonSelectors.appNameInput).verifyVisibleElement( "have.value", - "test-app" + "one_version" ); cy.clearAndType(commonSelectors.appNameInput, data.appName); cy.get(commonSelectors.importAppButton).should("be.enabled").click(); @@ -262,8 +262,8 @@ describe("App creation", () => { cy.get(importSelectors.dropDownMenu).click(); cy.get(commonSelectors.chooseFromTemplateButton).click(); - cy.clearAndType('[data-cy="search-input-field"]', "Admin portal"); - cy.get('[data-cy="admin-portal-list-item"]').click(); + cy.clearAndType('[data-cy="search-input-field"]', "Admin panel"); + cy.get('[data-cy="admin-panel-tooljet-db-list-item"]').click(); cy.get('[data-cy="create-application-from-template-button"]').click() cy.wait(1000); @@ -277,7 +277,7 @@ describe("App creation", () => { ); cy.get(commonSelectors.appNameInput).verifyVisibleElement( "have.value", - "Admin portal" + "Admin Panel (ToolJet Database)" ); cy.get(commonSelectors.appNameInfoLabel).verifyVisibleElement( "have.text", diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/dashboard.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/dashboard.cy.js index e109c3d4ab..dc0ba39e61 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/dashboard.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/dashboard.cy.js @@ -2,7 +2,6 @@ import { fake } from "Fixtures/fake"; import { createFolder, deleteFolder, - deleteDownloadsFolder, navigateToAppEditor, viewAppCardOptions, verifyModal, @@ -14,49 +13,39 @@ import { } from "Support/utils/common"; import { modifyAndVerifyAppCardIcon, - login, verifyAppDelete, } from "Support/utils/dashboard"; -import { profileSelector } from "Selectors/profile"; -import { profileText } from "Texts/profile"; import { commonSelectors } from "Selectors/common"; import { dashboardSelector } from "Selectors/dashboard"; import { commonText } from "Texts/common"; import { dashboardText } from "Texts/dashboard"; -import { - navigateToManageUsers, - logout, - searchUser, - navigateToManageGroups, -} from "Support/utils/common"; -import { roleBasedOnboarding } from "Support/utils/onboarding"; +import { logout } from "Support/utils/common"; describe("dashboard", () => { - const data = {}; - data.appName = `${fake.companyName}-App`; - data.folderName = `${fake.companyName.toLowerCase()}-folder`; - data.cloneAppName = `cloned-${data.appName}`; - data.updatedFolderName = `new-${data.folderName}`; - data.firstName = fake.firstName; - data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""); - data.workspaceName = fake.firstName; - data.workspaceSlug = fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", ""); + let data = {}; + beforeEach(() => { + data = { + appName: `${fake.companyName}-App`, + folderName: `${fake.companyName.toLowerCase()}-folder`, + cloneAppName: `cloned-${fake.companyName}-App`, + updatedFolderName: `new-${fake.companyName.toLowerCase()}-folder`, + workspaceName: fake.firstName, + workspaceSlug: fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", ""), + }; cy.intercept("GET", "/api/library_apps").as("appLibrary"); cy.intercept("DELETE", "/api/folders/*").as("folderDeleted"); cy.skipWalkthrough(); + + cy.apiLogin(); + cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug); + cy.apiLogout(); + cy.apiLogin(); + cy.visit(`${data.workspaceSlug}`); }); it("should verify the elements on empty dashboard", () => { - cy.intercept("GET", "/api/apps?page=1&folder=&searchKey=", { - fixture: "intercept/emptyDashboard.json", - }).as("emptyDashboard"); - - cy.intercept("GET", "/api/folders?searchKey=", { - body: { folders: [] }, - }).as("folders"); - cy.intercept("GET", "/api/metadata", { body: { installed_version: "2.9.2", @@ -64,15 +53,10 @@ describe("dashboard", () => { }, }).as("version"); - cy.defaultWorkspaceLogin(); - cy.wait("@emptyDashboard"); - cy.wait("@folders"); - cy.wait("@version"); - cy.get(commonSelectors.homePageLogo).should("be.visible"); cy.get(commonSelectors.workspaceName).verifyVisibleElement( "have.text", - "My workspace" + data.workspaceName ); cy.get(commonSelectors.workspaceName).click(); // cy.get(commonSelectors.editRectangleIcon).should("be.visible"); @@ -87,7 +71,7 @@ describe("dashboard", () => { cy.get(commonSelectors.createNewFolderButton).should("be.visible"); cy.get(commonSelectors.allApplicationLink).verifyVisibleElement( "have.text", - commonText.allApplicationLink + commonText.allApplicationsLink ); cy.get(commonSelectors.notificationsIcon).should("be.visible").click(); @@ -118,9 +102,6 @@ describe("dashboard", () => { .should("have.attr", "class") .and("contain", "theme-dark"); cy.get(dashboardSelector.modeToggle).click(); - cy.get(dashboardSelector.homePageContent) - .should("have.attr", "class") - .and("contain", "bg-light-gray"); cy.wait(500); cy.get(commonSelectors.settingsIcon).click(); @@ -189,30 +170,24 @@ describe("dashboard", () => { }); it("Should verify app card elements and app card operations", () => { + cy.exec("mkdir -p ./cypress/downloads/"); + cy.exec("cd ./cypress/downloads/ && rm -rf *"); + const customLayout = { desktop: { top: 100, left: 20 }, mobile: { width: 8, height: 50 }, }; - cy.apiLogin(); + cy.apiCreateApp(data.appName); - cy.openApp(); - cy.apiAddComponentToApp(data.appName, "text1", customLayout); + cy.visit(`${data.workspaceSlug}`); - cy.backToApps(); - - cy.wait(500); - cy.get(commonSelectors.appCard(data.appName)) - .parent() - .within(() => { - cy.get(commonSelectors.appCard(data.appName)).should("be.visible"); - cy.get(commonSelectors.appTitle(data.appName)).verifyVisibleElement( - "have.text", - data.appName - ); - cy.get(commonSelectors.appCreationDetails).should("be.visible"); - - //Add the edited details - }); + cy.wait(2000); + cy.get(commonSelectors.appCreationDetails).should("be.visible"); + cy.get(commonSelectors.appCard(data.appName)).should("be.visible"); + cy.get(commonSelectors.appTitle(data.appName)).verifyVisibleElement( + "have.text", + data.appName + ); viewAppCardOptions(data.appName); cy.get( @@ -253,7 +228,8 @@ describe("dashboard", () => { cy.get(dashboardSelector.addToFolderButton).click(); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.AddedToFolderToast + commonText.AddedToFolderToast, + false ); cy.get(dashboardSelector.folderName(data.folderName)).verifyVisibleElement( @@ -266,7 +242,6 @@ describe("dashboard", () => { .contains(data.appName) .should("be.visible"); - cy.wait(2000); viewAppCardOptions(data.appName); cy.get(commonSelectors.appCardOptions(commonText.removeFromFolderOption)) @@ -283,7 +258,8 @@ describe("dashboard", () => { cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click(); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.appRemovedFromFolderTaost + commonText.appRemovedFromFolderTaost, + false ); cy.get(commonSelectors.modalComponent).should("not.exist"); cy.get(commonSelectors.empytyFolderImage).should("be.visible"); @@ -296,6 +272,18 @@ describe("dashboard", () => { cy.get(commonSelectors.allApplicationsLink).click(); + cy.wait(1000); + viewAppCardOptions(data.appName); + cy.wait(2000); + cy.get(commonSelectors.appCardOptions(commonText.exportAppOption)).click(); + cy.get(commonSelectors.exportAllButton).click(); + + + cy.exec("ls ./cypress/downloads/").then((result) => { + const downloadedAppExportFileName = result.stdout.split("\n")[0]; + expect(downloadedAppExportFileName).to.contain.string("app"); + }); + viewAppCardOptions(data.appName); cy.get(commonSelectors.appCardOptions(commonText.cloneAppOption)).click(); cy.get('[data-cy="clone-app"]').click(); @@ -303,25 +291,17 @@ describe("dashboard", () => { .should("be.visible") .and("have.text", dashboardText.appClonedToast); cy.wait(3000); + cy.renameApp(data.cloneAppName); cy.apiAddComponentToApp(data.cloneAppName, "button", 25, 25); cy.backToApps(); cy.wait("@appLibrary"); cy.wait(1000); - cy.reloadAppForTheElement(data.cloneAppName); cy.get(commonSelectors.appCard(data.cloneAppName)).should("be.visible"); - viewAppCardOptions(data.cloneAppName); - cy.get(commonSelectors.appCardOptions(commonText.exportAppOption)).click(); - cy.get(commonSelectors.exportAllButton).click(); + cy.wait(1000); - cy.exec("ls ./cypress/downloads/").then((result) => { - const downloadedAppExportFileName = result.stdout.split("\n")[0]; - expect(downloadedAppExportFileName).to.contain.string("app"); - }); - - cy.reloadAppForTheElement(data.cloneAppName); viewAppCardOptions(data.cloneAppName); cy.get(commonSelectors.deleteAppOption).click(); cy.get(commonSelectors.modalMessage).verifyVisibleElement( @@ -336,23 +316,20 @@ describe("dashboard", () => { ).verifyVisibleElement("have.text", commonText.modalYesButton); cancelModal(commonText.cancelButton); - cy.reloadAppForTheElement(data.cloneAppName); viewAppCardOptions(data.cloneAppName); cy.get(commonSelectors.deleteAppOption).click(); cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click(); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.appDeletedToast + commonText.appDeletedToast, + false ); - verifyAppDelete(data.cloneAppName); + + cy.get(commonSelectors.appCard(data.cloneAppName)).should("not.exist"); cy.wait("@appLibrary"); cy.deleteApp(data.appName); - cy.verifyToastMessage( - commonSelectors.toastMessage, - commonText.appDeletedToast - ); - verifyAppDelete(data.appName); + cy.get(commonSelectors.appCard(data.appName)).should("not.exist"); }); it("Should verify the app CRUD operation", () => { @@ -361,9 +338,6 @@ describe("dashboard", () => { mobile: { width: 8, height: 50 }, }; - cy.skipWalkthrough(); - data.appName = `${fake.companyName}-App`; - cy.defaultWorkspaceLogin(); cy.createApp(data.appName); cy.apiAddComponentToApp(data.appName, "text1", customLayout); @@ -381,11 +355,8 @@ describe("dashboard", () => { cy.wait("@appLibrary"); cy.deleteApp(data.appName); - cy.verifyToastMessage( - commonSelectors.toastMessage, - commonText.appDeletedToast - ); - verifyAppDelete(data.appName); + + cy.get(commonSelectors.appCard(data.appName)).should("not.exist"); }); it("Should verify the folder CRUD operation", () => { @@ -394,12 +365,8 @@ describe("dashboard", () => { mobile: { width: 8, height: 50 }, }; - data.appName = `${fake.companyName}-App`; - cy.defaultWorkspaceLogin(); cy.createApp(data.appName); - cy.apiAddComponentToApp(data.appName, "text1", customLayout); - cy.backToApps(); cy.get(commonSelectors.createNewFolderButton).click(); @@ -509,20 +476,8 @@ describe("dashboard", () => { cy.get(commonSelectors.allApplicationsLink).click(); cy.deleteApp(data.appName); - cy.verifyToastMessage( - commonSelectors.toastMessage, - commonText.appDeletedToast - ); - verifyAppDelete(data.appName); + + cy.get(commonSelectors.appCard(data.appName)).should("not.exist"); logout(); }); - - it("should verify the elements on empty dashboard for end user", () => { - cy.defaultWorkspaceLogin(); - cy.intercept("GET", "/api/apps?page=1&folder=&searchKey=", { - fixture: "intercept/emptyDashboard.json", - }).as("emptyDashboard") - roleBasedOnboarding(data.firstName, data.email, "end-user"); - cy.get(commonSelectors.dashboardAppCreateButton).should("be.disabled"); - }); }); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.cy.js index 132ddae9b2..681a2d93e6 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.cy.js @@ -24,6 +24,7 @@ import { exportAppModalSelectors, importSelectors, } from "Selectors/exportImport"; +import { dashboardText } from "../../../../../../constants/texts/dashboard"; describe("Manage Groups", () => { let data = {}; @@ -75,16 +76,18 @@ describe("Manage Groups", () => { // App operations cy.createApp(data.appName); - cy.verifyToastMessage( - commonSelectors.toastMessage, - commonText.appCreatedToast - ); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // commonText.appCreatedToast, + // false + // ); cy.backToApps(); cy.deleteApp(data.appName); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.appDeletedToast + commonText.appDeletedToast, + false ); // Folder operations @@ -114,9 +117,10 @@ describe("Manage Groups", () => { cy.get(commonSelectors.cloneAppButton).click(); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.cloneAppErrorToast + dashboardText.appClonedToast, + false ); - cy.get(commonSelectors.cancelButton).click(); + // cy.get(commonSelectors.cancelButton).click(); cy.apiLogout(); cy.apiLogin(); @@ -174,16 +178,18 @@ describe("Manage Groups", () => { // App operations cy.createApp(data.appName); - cy.verifyToastMessage( - commonSelectors.toastMessage, - commonText.appCreatedToast - ); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // commonText.appCreatedToast, + // false + // ); cy.backToApps(); cy.deleteApp(data.appName); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.appDeletedToast + commonText.appDeletedToast, + false ); // Folder operations diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/manageGroups.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/manageGroups.cy.js index 966cbcd930..6a4458a7dc 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/manageGroups.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/manageGroups.cy.js @@ -152,7 +152,7 @@ describe("Manage Groups", () => { cy.get(commonSelectors.breadcrumbTitle).should(($el) => { expect($el.contents().first().text().trim()).to.eq("Workspace settings"); }); - cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement("have.text", " Groups"); + cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement("have.text", "Groups"); // Verify base group elements groups.manageGroupsElements(); @@ -202,7 +202,6 @@ describe("Manage Groups", () => { testDuplicateGroup(); createNewGroup(); - // Verify permissions section cy.get(groupsSelector.permissionsLink).click(); verifyPermissionSection(); @@ -220,8 +219,8 @@ describe("Manage Groups", () => { cy.get(`[data-cy="${groupName.toLowerCase()}-text"]`).click(); cy.get(`${groupsSelector.addEditPermissionModalTitle}:eq(2)`) .verifyVisibleElement("have.text", groupsText.editPermissionModalTitle); - verifyModalFields(true, groupName); cy.get(groupsSelector.editPermissionRadio).check(); + verifyModalFields(true, groupName); cy.get(groupsSelector.confimButton).click(); }; diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/permissions.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/permissions.cy.js index 8fefd2bb5c..e9a4f5c222 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/permissions.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/permissions.cy.js @@ -196,18 +196,15 @@ describe("Manage Groups", () => { // App operations cy.createApp(data.appName); - cy.verifyToastMessage( - commonSelectors.toastMessage, - commonText.appCreatedToast - ); + // cy.verifyToastMessage( + // commonSelectors.toastMessage, + // commonText.appCreatedToast + // ); cy.backToApps(); cy.wait(2500); cy.deleteApp(data.appName); - cy.verifyToastMessage( - commonSelectors.toastMessage, - commonText.appDeletedToast - ); + // Folder operations createFolder(data.folderName); @@ -466,7 +463,7 @@ describe("Manage Groups", () => { cy.wait(500); cy.apiCreateGDS( - `${Cypress.env('server_host')}/api/v2/data_sources`, + `${Cypress.env('server_host')}/api/data-sources`, `cypress-${data.dsName}-qc-postgresql`, "postgresql", [ @@ -522,10 +519,8 @@ describe("Manage Groups", () => { commonSelectors.buttonSelector(exportAppModalText.exportSelectedVersion) ).click(); cy.exec("ls ./cypress/downloads/").then((result) => { - cy.log(result); const downloadedAppExportFileName = result.stdout.split("\n")[0]; exportedFilePath = `cypress/downloads/${downloadedAppExportFileName}`; - cy.log(exportedFilePath); cy.get(importSelectors.dropDownMenu).should("be.visible").click(); cy.get(importSelectors.importOptionInput).selectFile(exportedFilePath, { force: true, diff --git a/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/externalApi/apiUsers.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/externalApi/apiUsers.cy.js new file mode 100644 index 0000000000..b95970d349 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/externalApi/apiUsers.cy.js @@ -0,0 +1,382 @@ +import { fake } from "Fixtures/fake"; +import { + createUser, getAllUsers, getUser, updateUser, createGroup, validateUserInGroup, updateUserRole, + getAllWorkspaces, replaceUserWorkspace, replaceUserWorkspacesRelations +} from 'Support/utils/api'; +import { groupsSelector } from "Selectors/manageGroups"; +import { commonSelectors } from 'Selectors/common'; +import { searchUser, navigateToManageUsers, logout, navigateToManageGroups } from 'Support/utils/common'; +describe("API Test", () => { + + const sanitize = (str) => str.toLowerCase().replace(/[^A-Za-z]/g, ""); + let userId; + let workspaceId; + const data = { + firstName: fake.firstName, + lastName: fake.lastName, + firstName1: fake.firstName, + lastName1: fake.lastName, + firstName2: fake.firstName, + lastName2: fake.lastName, + email: fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""), + email1: fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""), + email2: fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""), + workspaceName: sanitize(fake.lastName), + workspaceSlug: sanitize(fake.lastName), + workspaceName1: sanitize(fake.firstName), + workspaceSlug1: sanitize(fake.firstName), + group1: sanitize(fake.firstName), + group2: sanitize(fake.firstName), + group3: sanitize(fake.firstName), + group4: sanitize(fake.firstName), + group5: sanitize(fake.firstName), + appName: fake.companyName + }; + + //user with all valid details + const userData = { + name: `${data.firstName} ${data.lastName}`, + email: data.email, + password: "password", + status: "active", + workspaces: [ + { + name: "My workspace", + status: "active", + groups: [ + { name: data.group1 }, + { name: data.group2 } + ] + }, + { + name: data.workspaceName, + status: "active", + role: "builder", + groups: [{ name: data.group3 }] + }, + { + name: data.workspaceName1, + status: "archived", + role: "admin", + groups: [{ name: data.group4 }] + } + ] + }; + + beforeEach(() => { + cy.defaultWorkspaceLogin(); + }); + + it("Create user with valid details", () => { + // create multiple groups in different workspaces + navigateToManageGroups(); + [data.group1, data.group2, data.group5].forEach(createGroup); + + //builder group + cy.get(groupsSelector.groupLink(data.group5)).click(); + cy.get(groupsSelector.permissionsLink).click(); + cy.get(groupsSelector.appsCreateCheck).check(); + + [ + { name: data.workspaceName, slug: data.workspaceSlug, group: data.group3 }, + { name: data.workspaceName1, slug: data.workspaceSlug1, group: data.group4 } + ].forEach(({ name, slug, group }) => { + cy.apiCreateWorkspace(name, slug); + cy.visit(slug); + navigateToManageGroups(); + createGroup(group); + }); + + // Added valid user and logged-in in the workpsace + cy.visit("/my-workspace"); + cy.wait(500); + createUser(userData).then((response) => { + expect(response.status).to.eq(201); + userId = response.body.id; + workspaceId = response.body.workspaces[0].id; + navigateToManageUsers(); + searchUser(data.email); + cy.contains("td", data.email) + .parent() + .within(() => { + cy.get("td small").should("have.text", "active"); + }); + + validateUserInGroup(data.email, "my-workspace", "end-user"); + validateUserInGroup(data.email, data.workspaceSlug, "builder"); + validateUserInGroup(data.email, data.workspaceSlug1, "admin", false); + cy.apiLogout(); + + cy.apiLogin(data.email, "password"); + cy.visit("/my-workspace"); + cy.get(commonSelectors.workspaceName).should("have.text", "My workspace"); + logout(); + + //Retrieve all users, a specific user by ID, and all workspaces + cy.defaultWorkspaceLogin(); + navigateToManageUsers(); + let number = 0; + cy.get('[data-cy="title-users-page"]').invoke('text').then((text) => { + number = parseInt(text.match(/\d+/)[0], 10); + }); + + getAllUsers().then((response) => { + expect(response.status).to.eq(200); + //expect(response.body.length).to.eq(number); //error due to removal of user from instance + }); + + getUser(userId).then((response) => { + expect(response.status).to.eq(200); + expect(response.body.name).to.eq(`${data.firstName} ${data.lastName}`); + }); + + getAllWorkspaces().then((response) => { + expect(response.status).to.eq(200); + }); + }); + }); + + it('Handles user creation errors', () => { + const invalidUserData = [ + { // Duplicate user + data: { ...userData }, + expectedStatus: 422, + expectedMessage: 'Already exists!' + }, + { // Invalid email and long password + data: { + ...userData, + name: `${data.firstName1} ${data.lastName1}`, + email: 'invalid-email', + password: 'a'.repeat(101) + }, + expectedStatus: 400, + expectedMessages: ['email must be an email', 'password must be shorter than or equal to 100 characters'] + }, + { // Non-existing group + data: { + ...userData, + name: `${data.firstName1} ${data.lastName1}`, + email: `${data.email1}`, + workspaces: [{ name: 'My workspace', status: 'active', groups: [{ name: 'NonExistingGroup' }] }] + }, + expectedStatus: 400, + expectedMessage: 'Group permission id or name not found:' + }, + { // Non-existing workspace + data: { + ...userData, + name: `${data.firstName1} ${data.lastName1}`, + email: `${data.email1}`, + workspaces: [{ name: 'NonExistingWorkspace', status: 'active' }] + }, + expectedStatus: 400, + expectedMessage: 'The workspaces id or name do not exist:' + } + ]; + + invalidUserData.forEach(({ data, expectedStatus, expectedMessages, expectedMessage }) => { + createUser(data).then((response) => { + expect(response.status).to.eq(expectedStatus); + if (expectedMessages) { + expectedMessages.forEach(msg => expect(response.body.message).to.include(msg)); + } else { + expect(response.body.message).to.include(expectedMessage); + } + }); + }); + //Conflict permission + const enduserData = { + ...userData, + name: `${data.firstName1} ${data.lastName1}`, + email: `${data.email1}`, + workspaces: [{ name: 'My workspace', status: 'active', groups: [{ name: data.group5 }] }] + } + createUser(enduserData).then((response) => { + expect(response.status).to.eq(400); + expect(response.body.message.title).to.include("Conflicting permissions"); + }) + }); + + it("Update user details and workspaces relations", () => { + const updatedUserData = { + name: `${data.firstName1} ${data.lastName1}`, + email: data.email1, + password: "updatedpassword" + } + updateUser(userId, updatedUserData).then((response) => { + expect(response.status).to.eq(200); + }) + cy.apiLogout(); + cy.apiLogin(updatedUserData.email, updatedUserData.password); + cy.apiLogout(); + + // Replace user workspaces relations + cy.apiLogin(); + validateUserInGroup(updatedUserData.email, "my-workspace", data.group2); + validateUserInGroup(updatedUserData.email, data.workspaceSlug, data.group3); + cy.visit(data.workspaceSlug1); + navigateToManageUsers(); + searchUser(updatedUserData.email); + cy.contains("td", updatedUserData.email); + + replaceUserWorkspacesRelations(userId, [ + { name: "My workspace", status: "active", role: "end-user", groups: [{ name: data.group1 }] }, + { name: data.workspaceName, status: "active", role: "builder", groups: [] } + ]).then((response) => { + expect(response.status).to.eq(200); + }); + navigateToManageUsers(); + validateUserInGroup(updatedUserData.email, "my-workspace", data.group2, false); + validateUserInGroup(updatedUserData.email, data.workspaceSlug, data.group3, false); + + cy.visit(data.workspaceSlug1); + navigateToManageUsers(); + searchUser(updatedUserData.email); + cy.get('[data-cy="text-no-result-found"]').contains("No result found"); + replaceUserWorkspacesRelations(userId, []).then((response) => { + expect(response.status).to.eq(200); + }); + cy.visit("my-workspace"); + navigateToManageUsers(); + searchUser(updatedUserData.email); + cy.get('[data-cy="text-no-result-found"]').contains("No result found"); + }); + + it("update user role", () => { + const userData2 = { + name: `${data.firstName} ${data.lastName}`, + email: data.email, + password: "password", + status: "active", + workspaces: [ + { + name: "My workspace", + status: "active" + } + ] + } + let userId1; + let workspaceId1; + createUser(userData2).then((response) => { + expect(response.status).to.eq(201); + userId1 = response.body.id; + workspaceId1 = response.body.workspaces[0].id; + //update role to builder and validate user in builder's group + updateUserRole(workspaceId1, { newRole: "builder", userId: userId1 }) + .then((response) => { + expect(response.status).to.eq(200); + }); + validateUserInGroup(userData2.email, "my-workspace", "builder"); + + //update role to end-user and validate user is removed from builder's group + updateUserRole(workspaceId1, { newRole: "end-user", userId: userId1 }) + .then((response) => { + expect(response.status).to.eq(200); + }); + validateUserInGroup(userData2.email, "my-workspace", data.group5, false); + + // update role to builders and validate app's owner role can't be updated + updateUserRole(workspaceId1, { newRole: "builder", userId: userId1 }) + .then((response) => { + expect(response.status).to.eq(200); + }); + cy.apiLogout(); + cy.apiLogin(userData2.email, userData2.password); + cy.apiCreateApp(data.appName); + cy.apiLogout(); + cy.defaultWorkspaceLogin(); + updateUserRole(workspaceId1, { newRole: "end-user", userId: userId1 }) + .then((response) => { + expect(response.status).to.eq(400); + expect(response.body.message.title).to.include("Can not change user role"); + }); + + }); + }); + const userData3 = { + name: `${data.firstName2} ${data.lastName2}`, + email: data.email2, + password: "password", + status: "active", + workspaces: [ + { + name: "My workspace", + status: "active", + groups: [ + { name: data.group1 }, + { name: data.group2 } + ] + }, + { + name: data.workspaceName, + status: "active", + role: "builder", + groups: [{ name: data.group3 }] + }, + { + name: data.workspaceName1, + status: "archived", + role: "admin", + groups: [{ name: data.group4 }] + } + ] + }; + it("Replace user workspace", () => { + let userId1, workspaceId1; + createUser(userData3).then((response) => { + expect(response.status).to.eq(201); + userId1 = response.body.id; + workspaceId1 = response.body.workspaces[0].id; + + // Helper function to replace user workspace and validate response + const replaceAndValidate = (payload, expectedStatus = 200) => { + return replaceUserWorkspace(userId1, workspaceId1, payload).then((response) => { + expect(response.status).to.eq(expectedStatus); + }); + }; + + // No change if empty request body + replaceAndValidate({}).then(() => { + validateUserInGroup(userData3.email, "my-workspace", data.group1); + validateUserInGroup(userData3.email, "my-workspace", data.group2); + }); + + // Archive the user and verify status + replaceAndValidate({ status: "archived" }).then(() => { + navigateToManageUsers(); + searchUser(userData3.email); + cy.contains("td", userData3.email) + .parent() + .within(() => { + cy.get("td small").should("have.text", "archived"); + }); + }); + + // Reactivate user and validate groups + replaceAndValidate({ status: "active" }).then(() => { + validateUserInGroup(userData3.email, "my-workspace", data.group1); + validateUserInGroup(userData3.email, "my-workspace", data.group2); + }); + + // Update groups and validate removal + replaceAndValidate({ groups: [{ name: data.group1 }] }).then(() => { + validateUserInGroup(userData3.email, "my-workspace", data.group2, false); + }); + + //Empty group array, user removed from groups + replaceAndValidate({ groups: [] }).then(() => { + validateUserInGroup(userData3.email, "my-workspace", data.group1, false); + }); + + //Conflict permission + replaceAndValidate({ groups: [{ name: data.group5 }] }, 400); + + //Add user in groups and validate + replaceAndValidate({ groups: [{ name: data.group1 }, { name: data.group2 }] }); + validateUserInGroup(userData3.email, "my-workspace", data.group1); + validateUserInGroup(userData3.email, "my-workspace", data.group2); + }); + }); +}); + diff --git a/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/externalApi/appImportAndExportAPI.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/externalApi/appImportAndExportAPI.cy.js new file mode 100644 index 0000000000..f2e522b22a --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/externalApi/appImportAndExportAPI.cy.js @@ -0,0 +1,160 @@ +import { importApp, exportApp, allAppsDetails } from 'Support/utils/api'; +import { fake } from "Fixtures/fake"; + +describe("Export and Import API ", () => { + + const sanitize = (str) => str.toLowerCase().replace(/[^A-Za-z]/g, ""); + const data = { + workspaceName: sanitize(fake.lastName), + workspaceSlug: sanitize(fake.lastName), + } + + const fixtureFiles = { + requestData: "templates/import_unnamed_file.json", + requestData2: "templates/import_named_file.json", + requestData3: "templates/three-versions.json", + }; + let requestData, requestData2, requestData3; + + beforeEach(() => { + cy.defaultWorkspaceLogin(); + + const fixturePromises = Object.entries(fixtureFiles).map(([key, file]) => + cy.fixture(file).then((data) => ({ key, data })) + ); + + // Assign loaded data to respective variables + return Promise.all(fixturePromises).then((results) => { + results.forEach(({ key, data }) => { + ({ requestData, requestData2, requestData3 }[key] = data); + }); + }); + + }); + it("Import App API", () => { + const workspaceId = Cypress.env("workspaceId"); + + importApp(workspaceId, requestData).then((response) => { + expect(response.status).to.eq(201); + expect(response.body.message).to.include("App imported successfully into workspace"); + }); + + //Invalid access token and workspace + importApp(workspaceId, requestData, { + Authorization: "Basic xyz", + "Content-Type": "application/json" + }).then((response) => { + expect(response.status).to.eq(403); + }); + + importApp(workspaceId, requestData, { + Authorization: "", + "Content-Type": "application/json" + }).then((response) => { + expect(response.status).to.eq(403); + }); + + importApp(`${workspaceId}ee`, requestData).then((response) => { + expect(response.status).to.eq(400); + }); + + //Import named file + importApp(workspaceId, requestData2).then((response) => { + expect(response.status).to.eq(201); + expect(response.body.message).to.include("App imported successfully into workspace"); + }); + cy.reload(); + cy.get('[data-cy="app_json-title"]').should("exist"); + + //duplicate app + importApp(workspaceId, requestData2).then((response) => { + expect(response.status).to.eq(409); + expect(response.body.message).to.include("App with app_json already exists in the workspace"); + }); + cy.deleteApp("app_json"); + cy.get('[data-cy="app_json-title"]').should("not.exist"); + + //Import app in another workpsace + let newWorkspaceId; + cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug).then((res) => { + newWorkspaceId = res.body.organization_id; + cy.visit(data.workspaceSlug); + + importApp(newWorkspaceId, requestData).then((response) => { + expect(response.status).to.eq(201); + expect(response.body.message).to.include("App imported successfully into workspace"); + }); + }); + }); + + it("Export App API", () => { + const workspaceId = Cypress.env("workspaceId"); + let appId; + importApp(workspaceId, requestData3).then((response) => { + expect(response.status).to.eq(201); + expect(response.body.message).to.include("App imported successfully into workspace"); + }).then(() => { + cy.get('[data-cy^="import-export-app"]') + .first() + .find('[data-cy="edit-button"]') + .click({ force: true }); + cy.skipWalkthrough(); + }); + + cy.get('[data-cy="left-sidebar-settings-button"]').click(); + cy.get('[data-cy="app-slug-input-field"]').invoke('val').then((value) => { + appId = value; + + //export last created version + exportApp(workspaceId, appId, "").then((response) => { + expect(response.status).to.eq(201); + expect(response.body.app[0].definition.appV2.appVersions.length).to.eq(1); + expect(response.body.app[0].definition.appV2.appVersions[0].name).to.eq("v3"); + }); + //export specific versions + exportApp(workspaceId, appId, "?appVersion=v2").then((response) => { + expect(response.status).to.eq(201); + expect(response.body.app[0].definition.appV2.appVersions.length).to.eq(1); + expect(response.body.app[0].definition.appV2.appVersions[0].name).to.eq("v2"); + }); + //export all versions + exportApp(workspaceId, appId, "?exportAllVersions=true").then((response) => { + expect(response.status).to.eq(201); + expect(response.body.app[0].definition.appV2.appVersions.length).to.eq(3); + }); + + //Invalid access token and workspace + /* exportApp(workspaceId, appId, "", { + Authorization: "", + "Content-Type": "application/json" + }).then((response) => { + expect(response.status).to.eq(403); + }); + + exportApp(workspaceId, appId, "", { + Authorization: "", + "Content-Type": "application/json" + }).then((response) => { + expect(response.status).to.eq(403); + }); + + exportApp(`${workspaceId}ee`, appId, "").then((response) => { + expect(response.status).to.eq(400); + }); + */ + //with and without TJDB -x.tooljet_database + exportApp(workspaceId, appId, "?exportTJDB=false").then((response) => { + expect(response.status).to.eq(201); + expect(response.body).not.to.have.property("tooljet_database"); + }); + exportApp(workspaceId, appId, "?exportTJDB=true").then((response) => { + expect(response.status).to.eq(201); + expect(response.body).to.have.property("tooljet_database"); + }); + }); + //All Apps details + allAppsDetails(workspaceId).then((response) => { + expect(response.status).to.eq(200); + }); + }); +}); \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/multi-env/multiEnv.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/multi-env/multiEnv.cy.js new file mode 100644 index 0000000000..4197213244 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/multi-env/multiEnv.cy.js @@ -0,0 +1,662 @@ +import { fake } from "Fixtures/fake"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { commonEeText, ssoEeText } from "Texts/eeCommon"; +import { commonEeSelectors, multiEnvSelector } from "Selectors/eeCommon"; +import { + verifyPromoteModalUI, + verifyTooltipDisabled, +} from "Support/utils/platform/eeCommon"; +import { dataSourceSelector } from "Selectors/dataSource"; +import { + navigateToAppEditor, + pinInspector, + verifyTooltip, +} from "Support/utils/common"; +import { addQuery, selectDatasource } from "Support/utils/dataSource"; +import { + appPromote, + createNewVersion, + selectVersion, + selectEnv, +} from "Support/utils/platform/multiEnv"; +import { appVersionSelectors } from "Selectors/exportImport"; + +import { editAndVerifyWidgetName } from "Support/utils/commonWidget"; +import { deleteVersionAndVerify } from "Support/utils/version"; +import { deleteVersionText } from "Texts/version"; + +describe("Multi env", () => { + const data = {}; + data.appName = `${fake.companyName} App`; + data.ds = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""); + data.constName = fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", ""); + const slug = data.appName.toLowerCase().replace(/\s+/g, "-"); + let currentVersion = ""; + let newVersion = []; + let versionFrom = ""; + + beforeEach(() => { + cy.apiLogin(); + cy.viewport(1800, 1800); + cy.skipWalkthrough(); + }); + + it.only("Verify the datasource configuration and data on each env", () => { + cy.apiCreateGDS( + `${Cypress.env("server_host")}/api/data-sources`, + data.ds, + "restapi", + [ + { key: "url", value: "" }, + { key: "auth_type", value: "none" }, + { key: "grant_type", value: "authorization_code" }, + { key: "add_token_to", value: "header" }, + { key: "header_prefix", value: "Bearer " }, + { key: "access_token_url", value: "" }, + { key: "client_ide", value: "" }, + { key: "client_secret", value: "", encrypted: true }, + { key: "scopes", value: "read, write" }, + { key: "username", value: "", encrypted: false }, + { key: "password", value: "", encrypted: true }, + { key: "bearer_token", value: "", encrypted: true }, + { key: "auth_url", value: "" }, + { key: "client_auth", value: "header" }, + { key: "headers", value: [["", ""]] }, + { key: "custom_query_params", value: [["", ""]], encrypted: false }, + { key: "custom_auth_params", value: [["", ""]] }, + { + key: "access_token_custom_headers", + value: [["", ""]], + encrypted: false, + }, + { key: "multiple_auth_enabled", value: false, encrypted: false }, + { key: "ssl_certificate", value: "none", encrypted: false }, + ] + ); + cy.apiCreateApp(data.appName); + cy.visit("/my-workspace"); + cy.get(commonSelectors.globalDataSourceIcon).click(); + selectDatasource(data.ds); + cy.get('[data-cy="development-label"]').click(); + cy.clearAndType( + '[data-cy="base-url-text-field"]', + "https://reqres.in/api/users?page=1" + ); + cy.get(dataSourceSelector.buttonSave).click(); + cy.wait(2000); + + cy.get(commonSelectors.dashboardIcon).click(); + + cy.openApp(); + // cy.waitForAppLoad(); + cy.wait(2000); + cy.get(`[data-cy="${data.ds}-add-query-card"] > .text-truncate`).click(); + cy.wait(1000); + cy.get(dataSourceSelector.queryCreateAndRunButton).click(); + cy.get('[data-cy="query-tab-settings"]').click(); + cy.get(':nth-child(1) > .custom-toggle-switch > .switch > .slider').click(); + cy.waitForAutoSave(); + + cy.dragAndDropWidget("Text Input", 550, 650); + editAndVerifyWidgetName(data.constName, []); + cy.waitForAutoSave(); + + cy.get( + '[data-cy="default-value-input-field"]' + ).clearAndTypeOnCodeMirror(`{{queries.restapi1.data.data[0].email`); + cy.wait(1000); + cy.forceClickOnCanvas(); + cy.waitForAutoSave(); + cy.get(dataSourceSelector.queryCreateAndRunButton).click(); + cy.get( + commonWidgetSelector.draggableWidget(data.constName) + ).verifyVisibleElement("have.value", "george.bluth@reqres.in"); + + pinInspector(); + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(commonWidgetSelector.inspectorNodeComponents).click(); + cy.get(commonWidgetSelector.nodeComponent(data.constName)).click(); + cy.get('[data-cy="inspector-node-value"] > .mx-2').verifyVisibleElement( + "have.text", + `"george.bluth@reqres.in"` + ); + cy.get('[style="height: 13px; width: 13px;"] > img').should("exist"); + cy.get('[data-cy="inspector-node-globals"] > .node-key').click(); + cy.get('[data-cy="inspector-node-environment"] > .node-key').click(); + cy.get('[data-cy="inspector-node-name"] > .mx-2').verifyVisibleElement( + "have.text", + `"development"` + ); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + cy.wait(4000); + + cy.get( + commonWidgetSelector.draggableWidget(data.constName) + ).verifyVisibleElement("have.value", "george.bluth@reqres.in"); + + cy.go("back"); + cy.waitForAppLoad(); + cy.wait(3000); + cy.get(commonEeSelectors.promoteButton).click(); + cy.get(commonEeSelectors.promoteButton).eq(1).click(); + cy.waitForAppLoad(); + cy.wait(3000); + + cy.get(dataSourceSelector.queryCreateAndRunButton, { + timeout: 20000, + }).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "Query could not be completed" + ); + + cy.backToApps(); + cy.get(commonSelectors.globalDataSourceIcon).click(); + selectDatasource(data.ds); + cy.get('[data-cy="staging-label"]').click(); + cy.clearAndType( + '[data-cy="base-url-text-field"]', + "https://reqres.in/api/users?page=2" + ); + cy.get(dataSourceSelector.buttonSave).click(); + cy.wait(2000); + + cy.get(commonSelectors.dashboardIcon).click(); + navigateToAppEditor(data.appName); + cy.get(dataSourceSelector.queryCreateAndRunButton).click(); + cy.get( + commonWidgetSelector.draggableWidget(data.constName) + ).verifyVisibleElement("have.value", "michael.lawson@reqres.in"); + + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(commonWidgetSelector.inspectorNodeComponents).click(); + cy.get(commonWidgetSelector.nodeComponent(data.constName)).click(); + cy.get('[data-cy="inspector-node-value"] > .mx-2').verifyVisibleElement( + "have.text", + `"michael.lawson@reqres.in"` + ); + cy.get('[style="height: 13px; width: 13px;"] > img').should("not.exist"); + cy.get('[data-cy="inspector-node-globals"] > .node-key').click(); + cy.get('[data-cy="inspector-node-environment"] > .node-key').click(); + cy.get('[data-cy="inspector-node-name"] > .mx-2').verifyVisibleElement( + "have.text", + `"staging"` + ); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + cy.wait(4000); + + cy.get( + commonWidgetSelector.draggableWidget(data.constName) + ).verifyVisibleElement("have.value", "michael.lawson@reqres.in"); + + cy.go("back"); + cy.waitForAppLoad(); + cy.wait(3000); + cy.get(commonEeSelectors.promoteButton).click(); + cy.get(commonEeSelectors.promoteButton).eq(1).click(); + cy.waitForAppLoad(); + cy.wait(3000); + + cy.get(dataSourceSelector.queryCreateAndRunButton, { + timeout: 20000, + }).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "Query could not be completed" + ); + + cy.backToApps(); + cy.get(commonSelectors.globalDataSourceIcon).click(); + selectDatasource(data.ds); + cy.get('[data-cy="production-label"]').click(); + cy.clearAndType( + '[data-cy="base-url-text-field"]', + "https://reqres.in/api/users?page=1" + ); + cy.get(dataSourceSelector.buttonSave).click(); + cy.wait(2000); + + cy.get(commonSelectors.dashboardIcon).click(); + navigateToAppEditor(data.appName); + cy.get(dataSourceSelector.queryCreateAndRunButton).click(); + cy.get( + commonWidgetSelector.draggableWidget(data.constName) + ).verifyVisibleElement("have.value", "george.bluth@reqres.in"); + + cy.get(commonWidgetSelector.sidebarinspector).click(); + cy.get(commonWidgetSelector.inspectorNodeComponents).click(); + cy.get(commonWidgetSelector.nodeComponent(data.constName)).click(); + cy.get('[data-cy="inspector-node-value"] > .mx-2').verifyVisibleElement( + "have.text", + `"george.bluth@reqres.in"` + ); + cy.get('[style="height: 13px; width: 13px;"] > img').should("not.exist"); + cy.get('[data-cy="inspector-node-globals"] > .node-key').click(); + cy.get('[data-cy="inspector-node-environment"] > .node-key').click(); + cy.get('[data-cy="inspector-node-name"] > .mx-2').verifyVisibleElement( + "have.text", + `"production"` + ); + + cy.openInCurrentTab(commonWidgetSelector.previewButton); + cy.wait(4000); + + cy.get( + commonWidgetSelector.draggableWidget(data.constName) + ).verifyVisibleElement("have.value", "george.bluth@reqres.in"); + + cy.go("back"); + cy.waitForAppLoad(); + cy.wait(3000); + cy.get(commonSelectors.releaseButton).click(); + cy.get(commonSelectors.yesButton).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released"); + cy.wait(4000); + + cy.get(commonWidgetSelector.shareAppButton).click(); + cy.clearAndType(commonWidgetSelector.appNameSlugInput, `${slug}`); + cy.wait(2000); + cy.get(commonWidgetSelector.modalCloseButton).click(); + + cy.visit(`/applications/${slug}`); + cy.get( + commonWidgetSelector.draggableWidget(data.constName) + ).verifyVisibleElement("have.value", "george.bluth@reqres.in"); + }); + + it("should verify edit privilages of a promoted version", () => { + data.appName = `${fake.companyName} App`; + cy.apiCreateApp(data.appName); + cy.openApp(); + cy.waitForAppLoad(); + cy.dragAndDropWidget("Text", 550, 650); + appPromote("development", "production"); + + createNewVersion( + (currentVersion = "v1"), + (newVersion = ["v2"]), + (versionFrom = "v1") + ); + appPromote("development", "release"); + + createNewVersion( + (currentVersion = "v2"), + (newVersion = ["v3"]), + (versionFrom = "v2") + ); + appPromote("development", "staging"); + + selectVersion((currentVersion = "v3"), (newVersion = ["v1"])); + cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement( + "have.text", + "App cannot be edited after promotion. Please create a new version from Development to make any changes." + ); + + cy.forceClickOnCanvas(); + cy.get(".datasource-picker").should("have.class", "disabled"); + cy.get(commonEeSelectors.AddQueryButton).should("be.disabled"); + cy.get(".components-container").should("have.class", "disabled"); + + cy.wait(1000); + selectEnv("development"); + cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement( + "have.text", + "App cannot be edited after promotion. Please create a new version from Development to make any changes." + ); + cy.get(".datasource-picker").should("have.class", "disabled"); + cy.get(commonEeSelectors.AddQueryButton).should("be.disabled"); + cy.get(".components-container").should("have.class", "disabled"); + + selectVersion((currentVersion = "v1"), (newVersion = ["v2"])); + cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement( + "have.text", + "This version of the app is released. Please create a new version in development to make any changes." + ); + cy.get(".datasource-picker").should("have.class", "disabled"); + cy.get(commonEeSelectors.AddQueryButton).should("be.disabled"); + cy.get(".components-container").should("have.class", "disabled"); + + cy.wait(1000); + selectEnv("staging"); + cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement( + "have.text", + "This version of the app is released. Please create a new version in development to make any changes." + ); + cy.get(".datasource-picker").should("have.class", "disabled"); + cy.get(commonEeSelectors.AddQueryButton).should("be.disabled"); + cy.get(".components-container").should("have.class", "disabled"); + + cy.wait(1000); + selectEnv("production"); + cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement( + "have.text", + "This version of the app is released. Please create a new version in development to make any changes." + ); + cy.get(".datasource-picker").should("have.class", "disabled"); + cy.get(commonEeSelectors.AddQueryButton).should("be.disabled"); + cy.get(".components-container").should("have.class", "disabled"); + cy.get(commonSelectors.releaseButton).should("be.disabled"); + }); + + it("Should verify last exisiting version", () => { + data.appName = `${fake.companyName} App`; + cy.apiCreateApp(data.appName); + cy.openApp(); + cy.waitForAppLoad(); + cy.dragAndDropWidget("Text", 550, 650); + + appPromote("development", "staging"); + createNewVersion( + (currentVersion = "v1"), + (newVersion = ["v2"]), + (versionFrom = "v1") + ); + + selectVersion((currentVersion = "v2"), (newVersion = ["v1"])); + + cy.wait(1000); + selectEnv("staging"); + + cy.get(appVersionSelectors.currentVersionField(newVersion[0])) + .should("be.visible") + .and("have.text", "v1"); + + appPromote("staging", "production"); + + cy.wait(3000) + deleteVersionAndVerify( + (currentVersion = "v1"), + deleteVersionText.deleteToastMessage((currentVersion = "v1")) + ); + + cy.wait(2000); + cy.get('[data-cy="list-current-env-name"]').click(); + verifyTooltip( + '[data-cy="env-name-dropdown"]:eq(1)', + "There are no versions in this environment" + ); + verifyTooltip( + '[data-cy="env-name-dropdown"]:eq(2)', + "There are no versions in this environment" + ); + }); + + it("Should verify version deletion", () => { + data.appName = `${fake.companyName} App`; + cy.apiCreateApp(data.appName); + cy.openApp(); + cy.waitForAppLoad(); + cy.dragAndDropWidget("Text", 550, 650); + + appPromote("development", "staging"); + createNewVersion( + (currentVersion = "v1"), + (newVersion = ["v2"]), + (versionFrom = "v1") + ); + appPromote("development", "staging"); + + createNewVersion( + (currentVersion = "v2"), + (newVersion = ["v3"]), + (versionFrom = "v2") + ); + appPromote("development", "production"); + + selectEnv("staging"); + selectVersion((currentVersion = "v3"), (newVersion = ["v2"])); + deleteVersionAndVerify( + (currentVersion = "v2"), + deleteVersionText.deleteToastMessage((currentVersion = "v2")) + ); + + cy.get('[data-cy="v3-current-version-text"]') + .should("be.visible") + .and("have.text", "v3"); + + cy.get('[data-cy="list-current-env-name"]').should( + "have.text", + "Staging" + ); + }) + + it("Verify the multi env components UI", () => { + data.appName = `${fake.companyName} App`; + cy.apiCreateApp(data.appName); + cy.openApp(); + cy.waitForAppLoad(); + cy.dragAndDropWidget("Text", 550, 650); + cy.get(multiEnvSelector.envContainer).should("be.visible"); + cy.get(multiEnvSelector.currentEnvName) + .verifyVisibleElement("have.text", "Development") + .click(); + cy.get(multiEnvSelector.envArrow).should("be.visible"); + cy.get(multiEnvSelector.selectedEnvName).verifyVisibleElement( + "have.text", + " Development" + ); + cy.get(multiEnvSelector.envNameList) + .eq(0) + .verifyVisibleElement("have.text", "Development"); + cy.get(multiEnvSelector.envNameList) + .eq(1) + .verifyVisibleElement("have.text", "Staging"); + cy.get(multiEnvSelector.envNameList) + .eq(2) + .verifyVisibleElement("have.text", "Production"); + + verifyTooltip( + '[data-cy="env-name-dropdown"]:eq(1)', + "There are no versions in this environment" + ); + verifyTooltip( + '[data-cy="env-name-dropdown"]:eq(2)', + "There are no versions in this environment" + ); + + cy.get(multiEnvSelector.appVersionLabel).should("be.visible"); + cy.get('[data-cy="v1-current-version-text"]') + .verifyVisibleElement("have.text", "v1") + .click(); + cy.get(multiEnvSelector.currentVersion).verifyVisibleElement( + "have.text", + "v1" + ); + cy.get(".col-10 > .app-version-name").verifyVisibleElement( + "have.text", + "v1" + ); + cy.get(multiEnvSelector.createNewVersionButton).verifyVisibleElement( + "have.text", + "Create new version" + ); + + verifyPromoteModalUI("v1", "Development", "Staging"); + cy.get('[data-cy="env-change-info-text"]').verifyVisibleElement( + "have.text", + "You won’t be able to edit this version after promotion. Are you sure you want to continue?" + ); + cy.get(commonSelectors.closeButton).click(); + cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement( + "have.text", + "Development" + ); + + cy.get(commonEeSelectors.promoteButton).click(); + cy.get(commonSelectors.cancelButton).click(); + cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement( + "have.text", + "Development" + ); + + cy.get(commonEeSelectors.promoteButton).click(); + cy.get(commonEeSelectors.promoteButton).eq(1).click(); + + cy.waitForAppLoad(); + cy.wait(3000); + + cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement( + "have.text", + "App cannot be edited after promotion. Please create a new version from Development to make any changes." + ); + cy.get(multiEnvSelector.envContainer).should("be.visible"); + cy.get(multiEnvSelector.currentEnvName) + .verifyVisibleElement("have.text", "Staging") + .click(); + cy.get(multiEnvSelector.envArrow).should("be.visible"); + cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement( + "have.text", + "Staging" + ); + cy.get(multiEnvSelector.envNameList) + .eq(0) + .verifyVisibleElement("have.text", "Development"); + cy.get(multiEnvSelector.envNameList) + .eq(1) + .verifyVisibleElement("have.text", "Staging"); + cy.get(multiEnvSelector.envNameList) + .eq(2) + .verifyVisibleElement("have.text", "Production"); + cy.wait(2000) + verifyTooltip( + '[data-cy="env-name-dropdown"]:eq(2)', + "There are no versions in this environment" + ); + + cy.get(multiEnvSelector.appVersionLabel).should("be.visible"); + cy.get('[data-cy="v1-current-version-text"]') + .verifyVisibleElement("have.text", "v1") + .click(); + cy.get(multiEnvSelector.currentVersion).verifyVisibleElement( + "have.text", + "v1" + ); + cy.get(".col-10 > .app-version-name").verifyVisibleElement( + "have.text", + "v1" + ); + cy.get(multiEnvSelector.createNewVersionButton).verifyVisibleElement( + "have.text", + "Create new version" + ); + + verifyTooltip( + multiEnvSelector.createNewVersionButton, + "New versions can only be created in development" + ); + cy.forceClickOnCanvas(); + cy.get(".datasource-picker").should("have.class", "disabled"); + cy.get(commonEeSelectors.AddQueryButton).should("be.disabled"); + cy.get(".components-container").should("have.class", "disabled"); + + verifyPromoteModalUI("v1", "Staging", "Production"); + cy.get(commonSelectors.closeButton).click(); + cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement( + "have.text", + "Staging" + ); + + cy.get(commonEeSelectors.promoteButton).click(); + cy.get(commonSelectors.cancelButton).click(); + cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement( + "have.text", + "Staging" + ); + + cy.get(commonEeSelectors.promoteButton).click(); + cy.get(commonEeSelectors.promoteButton).eq(1).click(); + cy.waitForAppLoad(); + cy.wait(3000); + + cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement( + "have.text", + "App cannot be edited after promotion. Please create a new version from Development to make any changes." + ); + cy.get(multiEnvSelector.envContainer).should("be.visible"); + cy.get(multiEnvSelector.currentEnvName) + .verifyVisibleElement("have.text", "Production") + .click(); + cy.get(multiEnvSelector.envArrow).should("be.visible"); + cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement( + "have.text", + "Production" + ); + cy.get(multiEnvSelector.envNameList) + .eq(0) + .verifyVisibleElement("have.text", "Development"); + cy.get(multiEnvSelector.envNameList) + .eq(1) + .verifyVisibleElement("have.text", "Staging"); + cy.get(multiEnvSelector.envNameList) + .eq(2) + .verifyVisibleElement("have.text", "Production"); + + cy.get(multiEnvSelector.appVersionLabel).should("be.visible"); + cy.get('[data-cy="v1-current-version-text"]') + .verifyVisibleElement("have.text", "v1") + .click(); + cy.get(multiEnvSelector.currentVersion).verifyVisibleElement( + "have.text", + "v1" + ); + cy.get(".col-10 > .app-version-name").verifyVisibleElement( + "have.text", + "v1" + ); + cy.get(multiEnvSelector.createNewVersionButton).verifyVisibleElement( + "have.text", + "Create new version" + ); + + cy.get(commonSelectors.releaseButton) + .verifyVisibleElement("have.text", "Release") + .click(); + cy.get('[data-cy="modal-title"]').verifyVisibleElement( + "have.text", + "Release Version" + ); + cy.get(commonSelectors.closeButton).should("be.visible"); + cy.get('[data-cy="confirm-dialogue-box-text"]').verifyVisibleElement( + "have.text", + "Are you sure you want to release this version?" + ); + cy.get(commonSelectors.cancelButton).verifyVisibleElement( + "have.text", + "Cancel" + ); + cy.get(commonSelectors.yesButton).verifyVisibleElement("have.text", "Yes"); + + cy.get(commonSelectors.closeButton).click(); + cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement( + "have.text", + "Production" + ); + + cy.get(commonSelectors.releaseButton).click(); + cy.get(commonSelectors.cancelButton).click(); + cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement( + "have.text", + "Production" + ); + + cy.get(commonSelectors.releaseButton).click(); + cy.get(commonSelectors.yesButton).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released"); + cy.wait(500); + cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement( + "have.text", + "This version of the app is released. Please create a new version in development to make any changes." + ); + cy.get('[data-cy="v1-current-version-text"]').click(); + verifyTooltip( + multiEnvSelector.createNewVersionButton, + "New versions can only be created in development" + ); + cy.get(".datasource-picker").should("have.class", "disabled"); + cy.get(commonEeSelectors.AddQueryButton).should("be.disabled"); + cy.get(".components-container").should("have.class", "disabled"); + cy.get(commonSelectors.releaseButton).should("be.disabled"); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/workspace/ldapOnboarding.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/workspace/ldapOnboarding.cy.js new file mode 100644 index 0000000000..7cc5692226 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/workspace/ldapOnboarding.cy.js @@ -0,0 +1,250 @@ +import { commonSelectors } from "Selectors/common"; +import { commonEeSelectors, ssoEeSelector } from "Selectors/eeCommon"; +import { ssoEeText } from "Texts/eeCommon"; +import { setSSOStatus, setSignupStatus } from "Support/utils/manageSSO"; +import { usersText } from "Texts/manageUsers"; +import { fake } from "Fixtures/fake"; +import { + logout, + navigateToManageSSO, + navigateToManageUsers, + searchUser, + pinInspector, + navigateToAppEditor, + navigateToManageGroups, +} from "Support/utils/common"; +import { ssoText } from "Texts/manageSSO"; +import { enableToggle, disableToggle } from "Support/utils/platform/eeCommon"; +import { setupAndUpdateRole } from "Support/utils/manageGroups"; + +describe("LDAP flow", () => { + const TEST_DATA = { + appName: `${fake.companyName} App`, + ldapUser: { + username: "Hubert J. Farnsworth", + password: "professor", + email: "professor@planetexpress.com", + }, + ldapConfig: { + name: "Tooljet LDAP Auth", + host: Cypress.env("ldap_host"), + port: "10389", + baseDn: Cypress.env("ldap_base_dn"), + }, + }; + + const ldapLogin = ( + username = TEST_DATA.ldapUser.username, + password = TEST_DATA.ldapUser.password + ) => { + cy.get(ssoEeSelector.ldapSSOText).click(); + cy.clearAndType(commonSelectors.inputFieldName, username); + cy.clearAndType(ssoEeSelector.passwordInputField, password); + cy.get(commonSelectors.signUpButton).click(); + }; + + const toggleUserArchiveStatus = (shouldArchive = true) => { + navigateToManageUsers(); + searchUser(TEST_DATA.ldapUser.email); + cy.get('[data-cy="user-actions-button"]').click(); + cy.get('[data-cy="archive-button"]').click(); + + const expectedToast = shouldArchive + ? usersText.archivedToast + : usersText.unarchivedToast; + cy.verifyToastMessage(commonSelectors.toastMessage, expectedToast); + + if (shouldArchive) { + cy.contains("td", TEST_DATA.ldapUser.email) + .parent() + .within(() => { + cy.get("td small").should("have.text", usersText.archivedStatus); + }); + } + }; + + beforeEach(() => { + cy.visit("/"); + cy.appUILogin(); + }); + + it("Verify complete LDAP flow: UI, user onboarding, inspector SSO info, and archive functionality", () => { + cy.intercept("GET", "api/library_apps").as("apps"); + + // ========== SECTION 1: LDAP Configuration and UI Verification ========== + setSSOStatus("My workspace", "ldap", false); + navigateToManageSSO(); + cy.wait(1000); + + cy.get('[data-cy="ldap-sso-card"]') + .verifyVisibleElement("have.text", "LDAP") + .click(); + + cy.get(ssoEeSelector.ldapToggle).should("be.visible"); + + for (const element in ssoEeSelector.ldapPageElements) { + cy.get(ssoEeSelector.ldapPageElements[element]).verifyVisibleElement( + "have.text", + ssoEeText.ldapPageElements[element] + ); + } + + const formElements = [ + ssoEeSelector.statusLabel, + ssoEeSelector.nameInput, + ssoEeSelector.hostInput, + ssoEeSelector.portInput, + ssoEeSelector.baseDnInput, + ssoEeSelector.sslToggleInput, + ]; + + formElements.forEach((selector) => { + cy.get(selector).should("be.visible"); + }); + + cy.get(commonSelectors.cancelButton) + .eq(1) + .verifyVisibleElement("have.text", "Cancel"); + cy.get(commonEeSelectors.saveButton) + .eq(1) + .verifyVisibleElement("have.text", "Save changes"); + + enableToggle(ssoEeSelector.sslToggleInput); + cy.get(ssoEeSelector.ldapPageElements.sslLabel) + .eq(1) + .verifyVisibleElement("have.text", "SSL certificate"); + cy.get(".css-1x65k0v-control").should("be.visible"); + + cy.clearAndType(ssoEeSelector.nameInput, TEST_DATA.ldapConfig.name); + cy.clearAndType(ssoEeSelector.hostInput, TEST_DATA.ldapConfig.host); + cy.clearAndType(ssoEeSelector.portInput, TEST_DATA.ldapConfig.port); + cy.clearAndType(ssoEeSelector.baseDnInput, TEST_DATA.ldapConfig.baseDn); + + cy.get(ssoEeSelector.sslToggleInput).uncheck(); + cy.get(ssoEeSelector.ldapToggle).click(); + disableToggle(ssoEeSelector.sslToggleInput); + + cy.get(commonEeSelectors.saveButton).eq(1).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + ssoText.toggleUpdateToast("LDAP") + ); + cy.get(commonSelectors.cancelButton).eq(1).click(); + logout(); + + // ========== SECTION 2: LDAP Login Page and User Onboarding ========== + cy.get(ssoEeSelector.ldapSSOText).verifyVisibleElement( + "have.text", + ssoEeText.ldapSSOText + ); + + cy.get(ssoEeSelector.ldapSSOText).click(); + + const loginPageElements = [ + { selector: '[data-cy="key-logo"]', assertion: "be.visible" }, + { + selector: ssoEeSelector.userNameInputLabel, + text: ssoEeText.userNameInputLabel, + }, + { selector: commonSelectors.inputFieldName, assertion: "be.visible" }, + { selector: ssoEeSelector.passwordInputLabel, text: "Password" }, + { selector: ssoEeSelector.passwordInputField, assertion: "be.visible" }, + { selector: commonSelectors.signUpButton, text: "Sign in" }, + ]; + + loginPageElements.forEach((element) => { + if (element.text) { + cy.get(element.selector).verifyVisibleElement( + "have.text", + element.text + ); + } else { + cy.get(element.selector).should(element.assertion); + } + }); + + // Test failed login (user doesn't exist in workspace) + cy.clearAndType( + commonSelectors.inputFieldName, + TEST_DATA.ldapUser.username + ); + cy.clearAndType( + ssoEeSelector.passwordInputField, + TEST_DATA.ldapUser.password + ); + cy.get(commonSelectors.signUpButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "LDAP login failed - User does not exist in the workspace" + ); + + cy.defaultWorkspaceLogin(); + setSignupStatus(true); + logout(); + + ldapLogin(); + cy.get(commonSelectors.pageSectionHeader).verifyVisibleElement( + "have.text", + "Applications" + ); + logout(); + + // ========== SECTION 3: Setup App and User Permissions for Inspector Test ========== + cy.defaultWorkspaceLogin(); + cy.apiCreateApp(TEST_DATA.appName); + + navigateToManageGroups(); + setupAndUpdateRole("End-user", "Builder", TEST_DATA.ldapUser.email); + logout(); + + // ========== SECTION 4: Verify SSO User Info in Inspector ========== + ldapLogin(); + + cy.wait("@apps"); + cy.wait(1000); + + navigateToAppEditor(TEST_DATA.appName); + pinInspector(); + + const inspectorPath = [ + '[data-cy="inspector-node-globals"] > .node-key', + '[data-cy="inspector-node-currentuser"] > .node-key', + '[data-cy="inspector-node-ssouserinfo"] > .node-key', + '[data-cy="inspector-node-mail"] > .node-key', + ]; + + inspectorPath.forEach((selector) => cy.get(selector).click()); + + cy.get('[data-cy="inspector-node-0"] > .mx-2').verifyVisibleElement( + "have.text", + `"${TEST_DATA.ldapUser.email}"` + ); + cy.backToApps(); + logout(); + + // ========== SECTION 5: Archive/Unarchive Functionality ========== + cy.defaultWorkspaceLogin(); + + // Archive user and verify status + toggleUserArchiveStatus(true); + logout(); + + ldapLogin(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "LDAP login failed - User is archived in the workspace" + ); + + // Unarchive user + cy.go("back"); + cy.appUILogin(); + toggleUserArchiveStatus(false); + logout(); + + ldapLogin(); + cy.get(commonSelectors.pageSectionHeader).verifyVisibleElement( + "have.text", + "Applications" + ); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/workspace/openId.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/workspace/openId.cy.js new file mode 100644 index 0000000000..d304d76a07 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/platform/eeTestcases/workspace/openId.cy.js @@ -0,0 +1,239 @@ +import * as common from "Support/utils/common"; +import { ssoText } from "Texts/manageSSO"; +import { + inviteUser, + WorkspaceInvitationLink, +} from "Support/utils/platform/eeCommon.js"; +import { commonSelectors } from "Selectors/common"; +import { + commonEeSelectors, + ssoEeSelector, + instanceSettingsSelector, +} from "Selectors/eeCommon"; +import { commonEeText } from "Texts/eeCommon"; +import { + setSignupStatus, + defaultSSO, + deleteOrganisationSSO, +} from "Support/utils/manageSSO"; +import { confirmInviteElements } from "Support/utils/manageUsers"; +import { usersText } from "Texts/manageUsers"; +import { usersSelector } from "Selectors/manageUsers"; + +import { fetchAndVisitInviteLink } from "Support/utils/manageUsers"; +import { enableInstanceSignup } from "Support/utils/manageSSO"; + +describe("Verify OIDC user onboarding", () => { + const envVar = Cypress.env("environment"); + + beforeEach(() => { + cy.defaultWorkspaceLogin(); + cy.intercept("GET", "api/library_apps").as("apps"); + cy.wait(2000); + defaultSSO(true); + }); + + it("Verify user onboarding using workspace OIDC", () => { + deleteOrganisationSSO("My workspace", ["openid"]); + common.navigateToManageSSO(); + defaultSSO(false); + setSignupStatus(false); + cy.wait(1000); + + cy.get(ssoEeSelector.oidc).click(); + cy.get(ssoEeSelector.oidcToggle).click(); + cy.clearAndType(ssoEeSelector.nameInput, "Tooljet OIDC"); + cy.clearAndType( + ssoEeSelector.clientIdInput, + Cypress.env("SSO_OPENID_CLIENT_ID") + ); + cy.clearAndType( + ssoEeSelector.clientSecretInput, + Cypress.env("SSO_OPENID_CLIENT_SECRET") + ); + cy.clearAndType( + ssoEeSelector.WellKnownUrlInput, + Cypress.env("SSO_OPENID_WELL_KNOWN_URL") + ); + cy.get(commonEeSelectors.saveButton).eq(1).click(); + cy.get('[data-cy="enable-button"]').click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + ssoText.toggleUpdateToast("OpenID") + ); + + cy.apiLogout(); + cy.visit("/login/my-workspace"); + cy.get(ssoEeSelector.oidcSSOText).verifyVisibleElement( + "have.text", + "Sign in with Tooljet OIDC" + ); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".superadmin-button").click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + "Open ID login failed - User does not exist in the workspace" + ); + + cy.apiLogin(); + setSignupStatus(true); + cy.apiLogout(); + + cy.visit("/login/my-workspace"); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".superadmin-button").click(); + + common.logout(); + + cy.defaultWorkspaceLogin(); + common.navigateToManageUsers(); + common.searchUser("superadmin@tooljet.com"); + + cy.contains("td", "superadmin@tooljet.com") + .parent() + .within(() => { + cy.get("td small").should("have.text", usersText.activeStatus); + }); + + cy.apiLogout(); + cy.visit("/my-workspace"); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".superadmin-button").click(); + }); + + it("Verify invited user onboarding using instance level OIDC", () => { + setSignupStatus(true); + + cy.ifEnv("Enterprise", () => { + enableInstanceSignup(); + }); + + common.navigateToManageUsers(); + inviteUser("user", "user@tooljet.com"); + confirmInviteElements("user@tooljet.com"); + cy.wait(2000); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".user-button").click(); + cy.wait(1000); + + cy.get(commonSelectors.acceptInviteButton).click(); + cy.wait("@apps"); + cy.contains("My workspace").should("be.visible"); + common.logout(); + + cy.defaultWorkspaceLogin(); + setSignupStatus(false); + + common.navigateToManageUsers(); + cy.wait(500); + inviteUser("user", "userthree@tooljet.com"); + cy.wait(2000); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".user-four-button").click(); + + cy.get(commonSelectors.toastMessage) + .should("be.visible") + .and( + "have.text", + "Open ID login failed - Invalid Email: Please use the email address provided in the invitation." + ); + cy.wait(500); + cy.defaultWorkspaceLogin(); + + setSignupStatus(true); + fetchAndVisitInviteLink("userthree@tooljet.com"); + cy.wait(2000); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".user-four-button").click(); + cy.get(commonSelectors.toastMessage) + .should("be.visible") + .and( + "have.text", + "Open ID login failed - Invalid Email: Please use the email address provided in the invitation." + ); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".superadmin-button").click(); + cy.get(commonSelectors.toastMessage) + .should("be.visible") + .and( + "have.text", + "Open ID login failed - Invalid Email: Please use the email address provided in the invitation." + ); + }); + + if (envVar === "Enterprise") { + it("Verify user onboarding using instance level OIDC", () => { + enableInstanceSignup(); + cy.apiLogout(); + + cy.visit("/"); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".admin-button").click(); + cy.wait(3000); + common.logout(); + + cy.defaultWorkspaceLogin(); + cy.get(commonSelectors.settingsIcon).click(); + cy.get(commonEeSelectors.instanceSettingIcon).click(); + cy.clearAndType(commonSelectors.inputUserSearch, "admin@tooljet.com"); + + cy.get(instanceSettingsSelector.userStatus("admin")).verifyVisibleElement( + "have.text", + usersText.activeStatus + ); + + cy.apiLogout(); + cy.visit("/"); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".admin-button").click(); + }); + } + + + it("Verify archived user login using OIDC", () => { + setSignupStatus(true); + cy.ifEnv("Enterprise", () => { + enableInstanceSignup(); + }); + common.navigateToManageUsers(); + cy.get(usersSelector.buttonAddUsers).click(); + cy.get(commonSelectors.inputFieldFullName).type("user two"); + cy.get(commonSelectors.inputFieldEmailAddress).type("usertwo@tooljet.com"); + cy.get(usersSelector.buttonInviteUsers).click(); + WorkspaceInvitationLink("usertwo@tooljet.com"); + + cy.wait(2000); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".user-two-button").click(); + + cy.get(commonSelectors.acceptInviteButton).click(); + cy.wait("@apps"); + cy.contains("My workspace").should("be.visible"); + common.logout(); + + cy.defaultWorkspaceLogin(); + common.navigateToManageUsers(); + common.searchUser("usertwo@tooljet.com"); + cy.get('[data-cy="user-actions-button"]').click(); + cy.get('[data-cy="archive-button"]').click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + usersText.archivedToast + ); + cy.get(instanceSettingsSelector.userStatus("user two"), { + timeout: 9000, + }).should("have.text", usersText.archivedStatus); + cy.apiLogout(); + cy.visit("/my-workspace"); + cy.wait(2000); + cy.get(ssoEeSelector.oidcSSOText).realClick(); + cy.get(".user-two-button").click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + "Open ID login failed - User is archived in the workspace" + ); + }); +}); diff --git a/cypress-tests/cypress/e2e/happyPath/platform/firstUser/firstUserOnboarding.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/firstUser/firstUserOnboarding.cy.js new file mode 100644 index 0000000000..0c536c8ec3 --- /dev/null +++ b/cypress-tests/cypress/e2e/happyPath/platform/firstUser/firstUserOnboarding.cy.js @@ -0,0 +1,279 @@ +import { commonSelectors } from "Selectors/common"; +import { commonText } from "Texts/common"; +import { onboardingSelectors } from "Selectors/onboarding"; +import { onboardingText } from "Texts/onboarding"; +import { logout } from "Support/utils/common"; +import { + bannerElementsVerification, + onboardingStepOne, + onboardingStepTwo, + onboardingStepThree, +} from "Support/utils/onboarding"; + +describe("Self host onboarding", () => { + const envVar = Cypress.env("environment"); + + beforeEach(() => { + cy.visit("/setup"); + }); + + it("verify elements on self host onboarding page", () => { + cy.ifEnv("Enterprise", () => { + cy.get(commonSelectors.HostBanner).should("be.visible"); + cy.get(commonSelectors.pageLogo).should("be.visible"); + cy.get('[data-cy="welcome-to-tooljet!-header"]').verifyVisibleElement( + "have.text", + "Welcome to ToolJet!" + ); + cy.get(onboardingSelectors.pageDescription).verifyVisibleElement( + "have.text", + "Let's set up your admin account and workspace to get started!" + ); + cy.get('[data-cy="set-up-tooljet-button"]').verifyVisibleElement( + "have.text", + "Set up ToolJet" + ); + cy.get('[data-cy="set-up-tooljet-button"]').click(); + }); + + const commonElements = [ + { selector: commonSelectors.HostBanner }, + { selector: commonSelectors.pageLogo }, + { selector: commonSelectors.signupTerms }, + { selector: commonSelectors.nameInputField }, + { selector: onboardingSelectors.emailInput }, + { selector: onboardingSelectors.passwordInput }, + ]; + + commonElements.forEach((element) => { + cy.get(element.selector).should("be.visible"); + }); + + const labelChecks = [ + { + selector: commonSelectors.adminSetup, + text: "Set up your admin account", + }, + { + selector: commonSelectors.userNameInputLabel, + text: commonText.userNameInputLabel, + }, + { + selector: commonSelectors.emailInputLabel, + text: commonText.emailInputLabel, + }, + { + selector: commonSelectors.passwordLabel, + text: commonText.passwordLabel, + }, + { + selector: commonSelectors.passwordHelperTextSignup, + text: commonText.passwordHelperText, + }, + ]; + + labelChecks.forEach((check) => { + cy.get(check.selector).verifyVisibleElement("have.text", check.text); + }); + + cy.ifEnv("Community", () => { + cy.get(commonSelectors.signUpTermsHelperText).should(($el) => { + expect($el.contents().first().text().trim()).to.eq( + // commonText.selfHostSignUpTermsHelperText + "By signing up you are agreeing to the" + ); + }); + }); + + cy.ifEnv("Enterprise", () => { + cy.get(commonSelectors.signUpTermsHelperText).should(($el) => { + expect($el.contents().first().text().trim()).to.eq( + "By signing up you are agreeing to the" + ); + }); + }); + + const links = [ + { + selector: commonSelectors.termsOfServiceLink, + text: commonText.termsOfServiceLink, + href: "https://www.tooljet.com/terms", + }, + { + selector: commonSelectors.privacyPolicyLink, + text: commonText.privacyPolicyLink, + href: "https://www.tooljet.com/privacy", + }, + ]; + + links.forEach((link) => { + cy.get(link.selector) + .verifyVisibleElement("have.text", link.text) + .and("have.attr", "href") + .and("equal", link.href); + }); + + cy.get(commonSelectors.nameInputField).type("The Developer"); + cy.get(onboardingSelectors.emailInput).type("dev@tooljet.io"); + cy.get(onboardingSelectors.passwordInput).type("password"); + cy.get(commonSelectors.continueButton).click(); + + cy.ifEnv("Enterprise", () => { + bannerElementsVerification(); + onboardingStepOne(); + }); + + bannerElementsVerification(); + onboardingStepTwo(); + + cy.ifEnv("Enterprise", () => { + bannerElementsVerification(); + + const trialPageTexts = [ + { + selector: onboardingSelectors.beforeDiveInHeader, + text: onboardingText.freeTrialHeaderText, + }, + { + selector: onboardingSelectors.infoDescription, + text: onboardingText.infoDescriptionText, + }, + { + selector: onboardingSelectors.noCreditCardBanner, + text: onboardingText.noCreditCardText, + }, + { + selector: onboardingSelectors.trialButton, + text: "Start your 14-day trial", + }, + { selector: onboardingSelectors.declineButton, text: "No, thanks" }, + ]; + + trialPageTexts.forEach((item) => { + cy.get(item.selector).should("be.visible").and("have.text", item.text); + }); + + cy.get(onboardingSelectors.comparePlansTitle).verifyVisibleElement( + "have.text", + onboardingText.comparePlansText + ); + + cy.get(onboardingSelectors.comparePlanDescription) + .invoke("text") + .then((text) => { + const normalizedText = text.replace(/\s+/g, " ").trim(); + expect(normalizedText).to.equal( + "The plan reflects the features available in the latest version of ToolJet and some feature may not be available in your version. Click here to check out the details plan comparison & prices on our website." + ); + }); + + cy.get(onboardingSelectors.onPremiseLink) + .verifyVisibleElement("have.text", "Click here") + .and("have.attr", "href") + .and("equal", "https://tooljet.ai/pricing?payment=onpremise"); + + const planTitles = [ + { + selector: onboardingSelectors.basicPlanTitle, + text: onboardingText.basicPlanText, + }, + { + selector: onboardingSelectors.flexibleTitle, + text: onboardingText.flexibleText, + }, + { + selector: onboardingSelectors.businessTitle, + text: onboardingText.businessText, + }, + ]; + + planTitles.forEach((item) => { + cy.get(item.selector).should("be.visible").and("have.text", item.text); + }); + + const prices = [ + { selector: `${onboardingSelectors.planPrice}:eq(0)`, text: "$0" }, + { + selector: '[data-cy="pro-plan-price"]:eq(0)', + text: "$79/monthper builder", + }, + { + selector: '[data-cy="pro-plan-price"]:eq(1)', + text: "$199/monthper builder", + }, + { + selector: `${onboardingSelectors.planToggleLabel}:eq(0)`, + text: "Yearly20% off", + }, + { + selector: `${onboardingSelectors.planToggleLabel}:eq(1)`, + text: "Yearly20% off", + }, + ]; + + prices.forEach((item) => { + cy.get(item.selector).should("be.visible").and("have.text", item.text); + }); + + cy.get(onboardingSelectors.planToggleInput).eq(0).should("be.visible"); + cy.get(onboardingSelectors.planToggleInput).eq(1).should("be.visible"); + + cy.get(onboardingSelectors.pricingPlanToggle) + .eq(0) + .uncheck({ force: true }); + + cy.get(onboardingSelectors.planToggleLabel) + .eq(0) + .verifyVisibleElement("have.text", "Monthly20% off"); + cy.get(onboardingSelectors.discountDetails) + .should("have.css", "text-decoration") + .and("include", "line-through"); + + cy.get('[data-cy="pro-plan-price"]') + .eq(0) + .verifyVisibleElement("have.text", "$99/monthper builder"); + + cy.get(onboardingSelectors.pricingPlanToggle) + .eq(1) + .uncheck({ force: true }); + cy.get(onboardingSelectors.planToggleLabel) + .eq(1) + .verifyVisibleElement("have.text", "Monthly20% off"); + cy.get(onboardingSelectors.discountDetails) + .should("have.css", "text-decoration") + .and("include", "line-through"); + + cy.get('[data-cy="pro-plan-price"]') + .eq(1) + .verifyVisibleElement("have.text", "$249/monthper builder"); + + cy.get(onboardingSelectors.enterpriseTitle).verifyVisibleElement( + "have.text", + "Enterprise" + ); + cy.get(onboardingSelectors.customPricingHeader).verifyVisibleElement( + "have.text", + "Custom pricing" + ); + cy.get('[data-cy="schedule-a-call-button"]').verifyVisibleElement( + "have.text", + "Schedule a call" + ); + + cy.get(onboardingSelectors.declineButton).click(); + + bannerElementsVerification(); + onboardingStepThree(); + }); + + cy.get(commonSelectors.skipbutton).click(); + cy.backToApps(); + + logout(); + cy.appUILogin(); + + cy.get(commonSelectors.workspaceName) + .should("be.visible") + .and("have.text", "My workspace"); + }); +}); diff --git a/cypress-tests/cypress/fixtures/bulkUser/3_users_upload.csv b/cypress-tests/cypress/fixtures/bulkUser/3_users_upload.csv new file mode 100644 index 0000000000..9109b69d4e --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/3_users_upload.csv @@ -0,0 +1,4 @@ +First Name,Last Name,Email,User Role,Group +test1,user,test1@gmail.com,Builder, +test2,user,test3@gmail.com,End User, +Test3,Example,test12@gmail.com,Admin, diff --git a/cypress-tests/cypress/fixtures/bulkUser/3_users_upload_ee.csv b/cypress-tests/cypress/fixtures/bulkUser/3_users_upload_ee.csv new file mode 100644 index 0000000000..5a7996a60a --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/3_users_upload_ee.csv @@ -0,0 +1,4 @@ +First Name,Last Name,Email,User Role,Group,Metadata +test1,user,test1@gmail.com,Builder,, +test2,user,test3@gmail.com,End User,, +Test3,Example,test12@gmail.com,Admin,, diff --git a/cypress-tests/cypress/fixtures/bulkUser/empty_names.csv b/cypress-tests/cypress/fixtures/bulkUser/empty_names.csv new file mode 100644 index 0000000000..56fcda708f --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/empty_names.csv @@ -0,0 +1,3 @@ +First Name,Last Name,Email,User Role,Group +,,test12empty@gmail.com,Admin,Admin +Test,Example,test12empty@gmail.com,Builder,Builder diff --git a/cypress-tests/cypress/fixtures/bulkUser/empty_names_ee.csv b/cypress-tests/cypress/fixtures/bulkUser/empty_names_ee.csv new file mode 100644 index 0000000000..5718fe99f2 --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/empty_names_ee.csv @@ -0,0 +1,3 @@ +First Name,Last Name,Email,User Role,Group,Metadata +,,test12empty@gmail.com,Admin,Admin, +Test,Example,test12empty@gmail.com,Builder,Builder, diff --git a/cypress-tests/cypress/fixtures/bulkUser/limit_exceeded.csv b/cypress-tests/cypress/fixtures/bulkUser/limit_exceeded.csv new file mode 100644 index 0000000000..23e3f2e728 --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/limit_exceeded.csv @@ -0,0 +1,252 @@ +First Name,Last Name,Email,User Role,Group +Vijay,Yadav,vjyaav1@gmail.com,Admin, +Vijay,Yadav,vjyaav2@gmail.com,Builder, +Vijay,Yadav,vjyaav3@gmail.com,Builder, +Vijay,Yadav,vjyaav4@gmail.com,End User, +Vijay,Yadav,vjyaav5@gmail.com,End User, +Vijay,Yadav,vjyaav6@gmail.com,End User, +Vijay,Yadav,vjyaav7@gmail.com,End User, +Vijay,Yadav,vjyaav8@gmail.com,End User, +Vijay,Yadav,vjyaav9@gmail.com,End User, +Vijay,Yadav,vjyaav10@gmail.com,End User, +Vijay,Yadav,vjyaav11@gmail.com,End User, +Vijay,Yadav,vjyaav12@gmail.com,End User, +Vijay,Yadav,vjyaav13@gmail.com,End User, +Vijay,Yadav,vjyaav14@gmail.com,End User, +Vijay,Yadav,vjyaav15@gmail.com,End User, +Vijay,Yadav,vjyaav16@gmail.com,End User, +Vijay,Yadav,vjyaav17@gmail.com,End User, +Vijay,Yadav,vjyaav18@gmail.com,End User, +Vijay,Yadav,vjyaav19@gmail.com,End User, +Vijay,Yadav,vjyaav20@gmail.com,End User, +Vijay,Yadav,vjyaav21@gmail.com,End User, +Vijay,Yadav,vjyaav22@gmail.com,End User, +Vijay,Yadav,vjyaav23@gmail.com,End User, +Vijay,Yadav,vjyaav24@gmail.com,End User, +Vijay,Yadav,vjyaav25@gmail.com,End User, +Vijay,Yadav,vjyaav26@gmail.com,End User, +Vijay,Yadav,vjyaav27@gmail.com,End User, +Vijay,Yadav,vjyaav28@gmail.com,End User, +Vijay,Yadav,vjyaav29@gmail.com,End User, +Vijay,Yadav,vjyaav30@gmail.com,End User, +Vijay,Yadav,vjyaav31@gmail.com,End User, +Vijay,Yadav,vjyaav32@gmail.com,End User, +Vijay,Yadav,vjyaav33@gmail.com,End User, +Vijay,Yadav,vjyaav34@gmail.com,End User, +Vijay,Yadav,vjyaav35@gmail.com,End User, +Vijay,Yadav,vjyaav36@gmail.com,End User, +Vijay,Yadav,vjyaav37@gmail.com,End User, +Vijay,Yadav,vjyaav38@gmail.com,End User, +Vijay,Yadav,vjyaav39@gmail.com,End User, +Vijay,Yadav,vjyaav40@gmail.com,End User, +Vijay,Yadav,vjyaav41@gmail.com,End User, +Vijay,Yadav,vjyaav42@gmail.com,End User, +Vijay,Yadav,vjyaav43@gmail.com,End User, +Vijay,Yadav,vjyaav44@gmail.com,End User, +Vijay,Yadav,vjyaav45@gmail.com,End User, +Vijay,Yadav,vjyaav46@gmail.com,End User, +Vijay,Yadav,vjyaav47@gmail.com,End User, +Vijay,Yadav,vjyaav48@gmail.com,End User, +Vijay,Yadav,vjyaav49@gmail.com,End User, +Vijay,Yadav,vjyaav50@gmail.com,End User, +Vijay,Yadav,vjyaav51@gmail.com,End User, +Vijay,Yadav,vjyaav52@gmail.com,End User, +Vijay,Yadav,vjyaav53@gmail.com,End User, +Vijay,Yadav,vjyaav54@gmail.com,End User, +Vijay,Yadav,vjyaav55@gmail.com,End User, +Vijay,Yadav,vjyaav56@gmail.com,End User, +Vijay,Yadav,vjyaav57@gmail.com,End User, +Vijay,Yadav,vjyaav58@gmail.com,End User, +Vijay,Yadav,vjyaav59@gmail.com,End User, +Vijay,Yadav,vjyaav60@gmail.com,End User, +Vijay,Yadav,vjyaav61@gmail.com,End User, +Vijay,Yadav,vjyaav62@gmail.com,End User, +Vijay,Yadav,vjyaav63@gmail.com,End User, +Vijay,Yadav,vjyaav64@gmail.com,End User, +Vijay,Yadav,vjyaav65@gmail.com,End User, +Vijay,Yadav,vjyaav66@gmail.com,End User, +Vijay,Yadav,vjyaav67@gmail.com,End User, +Vijay,Yadav,vjyaav68@gmail.com,End User, +Vijay,Yadav,vjyaav69@gmail.com,End User, +Vijay,Yadav,vjyaav70@gmail.com,End User, +Vijay,Yadav,vjyaav71@gmail.com,End User, +Vijay,Yadav,vjyaav72@gmail.com,End User, +Vijay,Yadav,vjyaav73@gmail.com,End User, +Vijay,Yadav,vjyaav74@gmail.com,End User, +Vijay,Yadav,vjyaav75@gmail.com,End User, +Vijay,Yadav,vjyaav76@gmail.com,End User, +Vijay,Yadav,vjyaav77@gmail.com,End User, +Vijay,Yadav,vjyaav78@gmail.com,End User, +Vijay,Yadav,vjyaav79@gmail.com,End User, +Vijay,Yadav,vjyaav80@gmail.com,End User, +Vijay,Yadav,vjyaav81@gmail.com,End User, +Vijay,Yadav,vjyaav82@gmail.com,End User, +Vijay,Yadav,vjyaav83@gmail.com,End User, +Vijay,Yadav,vjyaav84@gmail.com,End User, +Vijay,Yadav,vjyaav85@gmail.com,End User, +Vijay,Yadav,vjyaav86@gmail.com,End User, +Vijay,Yadav,vjyaav87@gmail.com,End User, +Vijay,Yadav,vjyaav88@gmail.com,End User, +Vijay,Yadav,vjyaav89@gmail.com,End User, +Vijay,Yadav,vjyaav90@gmail.com,End User, +Vijay,Yadav,vjyaav91@gmail.com,End User, +Vijay,Yadav,vjyaav92@gmail.com,End User, +Vijay,Yadav,vjyaav93@gmail.com,End User, +Vijay,Yadav,vjyaav94@gmail.com,End User, +Vijay,Yadav,vjyaav95@gmail.com,End User, +Vijay,Yadav,vjyaav96@gmail.com,End User, +Vijay,Yadav,vjyaav97@gmail.com,End User, +Vijay,Yadav,vjyaav98@gmail.com,End User, +Vijay,Yadav,vjyaav99@gmail.com,End User, +Vijay,Yadav,vjyaav100@gmail.com,End User, +Vijay,Yadav,vjyaav101@gmail.com,End User, +Vijay,Yadav,vjyaav102@gmail.com,End User, +Vijay,Yadav,vjyaav103@gmail.com,End User, +Vijay,Yadav,vjyaav104@gmail.com,End User, +Vijay,Yadav,vjyaav105@gmail.com,End User, +Vijay,Yadav,vjyaav106@gmail.com,End User, +Vijay,Yadav,vjyaav107@gmail.com,End User, +Vijay,Yadav,vjyaav108@gmail.com,End User, +Vijay,Yadav,vjyaav109@gmail.com,End User, +Vijay,Yadav,vjyaav110@gmail.com,End User, +Vijay,Yadav,vjyaav111@gmail.com,End User, +Vijay,Yadav,vjyaav112@gmail.com,End User, +Vijay,Yadav,vjyaav113@gmail.com,End User, +Vijay,Yadav,vjyaav114@gmail.com,End User, +Vijay,Yadav,vjyaav115@gmail.com,End User, +Vijay,Yadav,vjyaav116@gmail.com,End User, +Vijay,Yadav,vjyaav117@gmail.com,End User, +Vijay,Yadav,vjyaav118@gmail.com,End User, +Vijay,Yadav,vjyaav119@gmail.com,End User, +Vijay,Yadav,vjyaav120@gmail.com,End User, +Vijay,Yadav,vjyaav121@gmail.com,End User, +Vijay,Yadav,vjyaav122@gmail.com,End User, +Vijay,Yadav,vjyaav123@gmail.com,End User, +Vijay,Yadav,vjyaav124@gmail.com,End User, +Vijay,Yadav,vjyaav125@gmail.com,End User, +Vijay,Yadav,vjyaav126@gmail.com,End User, +Vijay,Yadav,vjyaav127@gmail.com,End User, +Vijay,Yadav,vjyaav128@gmail.com,End User, +Vijay,Yadav,vjyaav129@gmail.com,End User, +Vijay,Yadav,vjyaav130@gmail.com,End User, +Vijay,Yadav,vjyaav131@gmail.com,End User, +Vijay,Yadav,vjyaav132@gmail.com,End User, +Vijay,Yadav,vjyaav133@gmail.com,End User, +Vijay,Yadav,vjyaav134@gmail.com,End User, +Vijay,Yadav,vjyaav135@gmail.com,End User, +Vijay,Yadav,vjyaav136@gmail.com,End User, +Vijay,Yadav,vjyaav137@gmail.com,End User, +Vijay,Yadav,vjyaav138@gmail.com,End User, +Vijay,Yadav,vjyaav139@gmail.com,End User, +Vijay,Yadav,vjyaav140@gmail.com,End User, +Vijay,Yadav,vjyaav141@gmail.com,End User, +Vijay,Yadav,vjyaav142@gmail.com,End User, +Vijay,Yadav,vjyaav143@gmail.com,End User, +Vijay,Yadav,vjyaav144@gmail.com,End User, +Vijay,Yadav,vjyaav145@gmail.com,End User, +Vijay,Yadav,vjyaav146@gmail.com,End User, +Vijay,Yadav,vjyaav147@gmail.com,End User, +Vijay,Yadav,vjyaav148@gmail.com,End User, +Vijay,Yadav,vjyaav149@gmail.com,End User, +Vijay,Yadav,vjyaav150@gmail.com,End User, +Vijay,Yadav,vjyaav151@gmail.com,End User, +Vijay,Yadav,vjyaav152@gmail.com,End User, +Vijay,Yadav,vjyaav153@gmail.com,End User, +Vijay,Yadav,vjyaav154@gmail.com,End User, +Vijay,Yadav,vjyaav155@gmail.com,End User, +Vijay,Yadav,vjyaav156@gmail.com,End User, +Vijay,Yadav,vjyaav157@gmail.com,End User, +Vijay,Yadav,vjyaav158@gmail.com,End User, +Vijay,Yadav,vjyaav159@gmail.com,End User, +Vijay,Yadav,vjyaav160@gmail.com,End User, +Vijay,Yadav,vjyaav161@gmail.com,End User, +Vijay,Yadav,vjyaav162@gmail.com,End User, +Vijay,Yadav,vjyaav163@gmail.com,End User, +Vijay,Yadav,vjyaav164@gmail.com,End User, +Vijay,Yadav,vjyaav165@gmail.com,End User, +Vijay,Yadav,vjyaav166@gmail.com,End User, +Vijay,Yadav,vjyaav167@gmail.com,End User, +Vijay,Yadav,vjyaav168@gmail.com,End User, +Vijay,Yadav,vjyaav169@gmail.com,End User, +Vijay,Yadav,vjyaav170@gmail.com,End User, +Vijay,Yadav,vjyaav171@gmail.com,End User, +Vijay,Yadav,vjyaav172@gmail.com,End User, +Vijay,Yadav,vjyaav173@gmail.com,End User, +Vijay,Yadav,vjyaav174@gmail.com,End User, +Vijay,Yadav,vjyaav175@gmail.com,End User, +Vijay,Yadav,vjyaav176@gmail.com,End User, +Vijay,Yadav,vjyaav177@gmail.com,End User, +Vijay,Yadav,vjyaav178@gmail.com,End User, +Vijay,Yadav,vjyaav179@gmail.com,End User, +Vijay,Yadav,vjyaav180@gmail.com,End User, +Vijay,Yadav,vjyaav181@gmail.com,End User, +Vijay,Yadav,vjyaav182@gmail.com,End User, +Vijay,Yadav,vjyaav183@gmail.com,End User, +Vijay,Yadav,vjyaav184@gmail.com,End User, +Vijay,Yadav,vjyaav185@gmail.com,End User, +Vijay,Yadav,vjyaav186@gmail.com,End User, +Vijay,Yadav,vjyaav187@gmail.com,End User, +Vijay,Yadav,vjyaav188@gmail.com,End User, +Vijay,Yadav,vjyaav189@gmail.com,End User, +Vijay,Yadav,vjyaav190@gmail.com,End User, +Vijay,Yadav,vjyaav191@gmail.com,End User, +Vijay,Yadav,vjyaav192@gmail.com,End User, +Vijay,Yadav,vjyaav193@gmail.com,End User, +Vijay,Yadav,vjyaav194@gmail.com,End User, +Vijay,Yadav,vjyaav195@gmail.com,End User, +Vijay,Yadav,vjyaav196@gmail.com,End User, +Vijay,Yadav,vjyaav197@gmail.com,End User, +Vijay,Yadav,vjyaav198@gmail.com,End User, +Vijay,Yadav,vjyaav199@gmail.com,End User, +Vijay,Yadav,vjyaav200@gmail.com,End User, +Vijay,Yadav,vjyaav201@gmail.com,End User, +Vijay,Yadav,vjyaav202@gmail.com,End User, +Vijay,Yadav,vjyaav203@gmail.com,End User, +Vijay,Yadav,vjyaav204@gmail.com,End User, +Vijay,Yadav,vjyaav205@gmail.com,End User, +Vijay,Yadav,vjyaav206@gmail.com,End User, +Vijay,Yadav,vjyaav207@gmail.com,End User, +Vijay,Yadav,vjyaav208@gmail.com,End User, +Vijay,Yadav,vjyaav209@gmail.com,End User, +Vijay,Yadav,vjyaav210@gmail.com,End User, +Vijay,Yadav,vjyaav211@gmail.com,End User, +Vijay,Yadav,vjyaav212@gmail.com,End User, +Vijay,Yadav,vjyaav213@gmail.com,End User, +Vijay,Yadav,vjyaav214@gmail.com,End User, +Vijay,Yadav,vjyaav215@gmail.com,End User, +Vijay,Yadav,vjyaav216@gmail.com,End User, +Vijay,Yadav,vjyaav217@gmail.com,End User, +Vijay,Yadav,vjyaav218@gmail.com,End User, +Vijay,Yadav,vjyaav219@gmail.com,End User, +Vijay,Yadav,vjyaav220@gmail.com,End User, +Vijay,Yadav,vjyaav221@gmail.com,End User, +Vijay,Yadav,vjyaav222@gmail.com,End User, +Vijay,Yadav,vjyaav223@gmail.com,End User, +Vijay,Yadav,vjyaav224@gmail.com,End User, +Vijay,Yadav,vjyaav225@gmail.com,End User, +Vijay,Yadav,vjyaav226@gmail.com,End User, +Vijay,Yadav,vjyaav227@gmail.com,End User, +Vijay,Yadav,vjyaav228@gmail.com,End User, +Vijay,Yadav,vjyaav229@gmail.com,End User, +Vijay,Yadav,vjyaav230@gmail.com,End User, +Vijay,Yadav,vjyaav231@gmail.com,End User, +Vijay,Yadav,vjyaav232@gmail.com,End User, +Vijay,Yadav,vjyaav233@gmail.com,End User, +Vijay,Yadav,vjyaav234@gmail.com,End User, +Vijay,Yadav,vjyaav235@gmail.com,End User, +Vijay,Yadav,vjyaav236@gmail.com,End User, +Vijay,Yadav,vjyaav237@gmail.com,End User, +Vijay,Yadav,vjyaav238@gmail.com,End User, +Vijay,Yadav,vjyaav239@gmail.com,End User, +Vijay,Yadav,vjyaav240@gmail.com,End User, +Vijay,Yadav,vjyaav241@gmail.com,End User, +Vijay,Yadav,vjyaav242@gmail.com,End User, +Vijay,Yadav,vjyaav243@gmail.com,End User, +Vijay,Yadav,vjyaav244@gmail.com,End User, +Vijay,Yadav,vjyaav245@gmail.com,End User, +Vijay,Yadav,vjyaav246@gmail.com,End User, +Vijay,Yadav,vjyaav247@gmail.com,End User, +Vijay,Yadav,vjyaav248@gmail.com,End User, +Vijay,Yadav,vjyaav249@gmail.com,End User, +Vijay,Yadav,vjyaav250@gmail.com,End User, +Vijay,Yadav,vjyaav251@gmail.com,End User, \ No newline at end of file diff --git a/cypress-tests/cypress/fixtures/bulkUser/limit_exceeded_ee.csv b/cypress-tests/cypress/fixtures/bulkUser/limit_exceeded_ee.csv new file mode 100644 index 0000000000..5d5de9460f --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/limit_exceeded_ee.csv @@ -0,0 +1,252 @@ +First Name,Last Name,Email,User Role,Group,Metadata +Vijay,Yadav,vjyaav1@gmail.com,Admin,, +Vijay,Yadav,vjyaav2@gmail.com,Builder,, +Vijay,Yadav,vjyaav3@gmail.com,Builder,, +Vijay,Yadav,vjyaav4@gmail.com,End User,, +Vijay,Yadav,vjyaav5@gmail.com,End User,, +Vijay,Yadav,vjyaav6@gmail.com,End User,, +Vijay,Yadav,vjyaav7@gmail.com,End User,, +Vijay,Yadav,vjyaav8@gmail.com,End User,, +Vijay,Yadav,vjyaav9@gmail.com,End User,, +Vijay,Yadav,vjyaav10@gmail.com,End User,, +Vijay,Yadav,vjyaav11@gmail.com,End User,, +Vijay,Yadav,vjyaav12@gmail.com,End User,, +Vijay,Yadav,vjyaav13@gmail.com,End User,, +Vijay,Yadav,vjyaav14@gmail.com,End User,, +Vijay,Yadav,vjyaav15@gmail.com,End User,, +Vijay,Yadav,vjyaav16@gmail.com,End User,, +Vijay,Yadav,vjyaav17@gmail.com,End User,, +Vijay,Yadav,vjyaav18@gmail.com,End User,, +Vijay,Yadav,vjyaav19@gmail.com,End User,, +Vijay,Yadav,vjyaav20@gmail.com,End User,, +Vijay,Yadav,vjyaav21@gmail.com,End User,, +Vijay,Yadav,vjyaav22@gmail.com,End User,, +Vijay,Yadav,vjyaav23@gmail.com,End User,, +Vijay,Yadav,vjyaav24@gmail.com,End User,, +Vijay,Yadav,vjyaav25@gmail.com,End User,, +Vijay,Yadav,vjyaav26@gmail.com,End User,, +Vijay,Yadav,vjyaav27@gmail.com,End User,, +Vijay,Yadav,vjyaav28@gmail.com,End User,, +Vijay,Yadav,vjyaav29@gmail.com,End User,, +Vijay,Yadav,vjyaav30@gmail.com,End User,, +Vijay,Yadav,vjyaav31@gmail.com,End User,, +Vijay,Yadav,vjyaav32@gmail.com,End User,, +Vijay,Yadav,vjyaav33@gmail.com,End User,, +Vijay,Yadav,vjyaav34@gmail.com,End User,, +Vijay,Yadav,vjyaav35@gmail.com,End User,, +Vijay,Yadav,vjyaav36@gmail.com,End User,, +Vijay,Yadav,vjyaav37@gmail.com,End User,, +Vijay,Yadav,vjyaav38@gmail.com,End User,, +Vijay,Yadav,vjyaav39@gmail.com,End User,, +Vijay,Yadav,vjyaav40@gmail.com,End User,, +Vijay,Yadav,vjyaav41@gmail.com,End User,, +Vijay,Yadav,vjyaav42@gmail.com,End User,, +Vijay,Yadav,vjyaav43@gmail.com,End User,, +Vijay,Yadav,vjyaav44@gmail.com,End User,, +Vijay,Yadav,vjyaav45@gmail.com,End User,, +Vijay,Yadav,vjyaav46@gmail.com,End User,, +Vijay,Yadav,vjyaav47@gmail.com,End User,, +Vijay,Yadav,vjyaav48@gmail.com,End User,, +Vijay,Yadav,vjyaav49@gmail.com,End User,, +Vijay,Yadav,vjyaav50@gmail.com,End User,, +Vijay,Yadav,vjyaav51@gmail.com,End User,, +Vijay,Yadav,vjyaav52@gmail.com,End User,, +Vijay,Yadav,vjyaav53@gmail.com,End User,, +Vijay,Yadav,vjyaav54@gmail.com,End User,, +Vijay,Yadav,vjyaav55@gmail.com,End User,, +Vijay,Yadav,vjyaav56@gmail.com,End User,, +Vijay,Yadav,vjyaav57@gmail.com,End User,, +Vijay,Yadav,vjyaav58@gmail.com,End User,, +Vijay,Yadav,vjyaav59@gmail.com,End User,, +Vijay,Yadav,vjyaav60@gmail.com,End User,, +Vijay,Yadav,vjyaav61@gmail.com,End User,, +Vijay,Yadav,vjyaav62@gmail.com,End User,, +Vijay,Yadav,vjyaav63@gmail.com,End User,, +Vijay,Yadav,vjyaav64@gmail.com,End User,, +Vijay,Yadav,vjyaav65@gmail.com,End User,, +Vijay,Yadav,vjyaav66@gmail.com,End User,, +Vijay,Yadav,vjyaav67@gmail.com,End User,, +Vijay,Yadav,vjyaav68@gmail.com,End User,, +Vijay,Yadav,vjyaav69@gmail.com,End User,, +Vijay,Yadav,vjyaav70@gmail.com,End User,, +Vijay,Yadav,vjyaav71@gmail.com,End User,, +Vijay,Yadav,vjyaav72@gmail.com,End User,, +Vijay,Yadav,vjyaav73@gmail.com,End User,, +Vijay,Yadav,vjyaav74@gmail.com,End User,, +Vijay,Yadav,vjyaav75@gmail.com,End User,, +Vijay,Yadav,vjyaav76@gmail.com,End User,, +Vijay,Yadav,vjyaav77@gmail.com,End User,, +Vijay,Yadav,vjyaav78@gmail.com,End User,, +Vijay,Yadav,vjyaav79@gmail.com,End User,, +Vijay,Yadav,vjyaav80@gmail.com,End User,, +Vijay,Yadav,vjyaav81@gmail.com,End User,, +Vijay,Yadav,vjyaav82@gmail.com,End User,, +Vijay,Yadav,vjyaav83@gmail.com,End User,, +Vijay,Yadav,vjyaav84@gmail.com,End User,, +Vijay,Yadav,vjyaav85@gmail.com,End User,, +Vijay,Yadav,vjyaav86@gmail.com,End User,, +Vijay,Yadav,vjyaav87@gmail.com,End User,, +Vijay,Yadav,vjyaav88@gmail.com,End User,, +Vijay,Yadav,vjyaav89@gmail.com,End User,, +Vijay,Yadav,vjyaav90@gmail.com,End User,, +Vijay,Yadav,vjyaav91@gmail.com,End User,, +Vijay,Yadav,vjyaav92@gmail.com,End User,, +Vijay,Yadav,vjyaav93@gmail.com,End User,, +Vijay,Yadav,vjyaav94@gmail.com,End User,, +Vijay,Yadav,vjyaav95@gmail.com,End User,, +Vijay,Yadav,vjyaav96@gmail.com,End User,, +Vijay,Yadav,vjyaav97@gmail.com,End User,, +Vijay,Yadav,vjyaav98@gmail.com,End User,, +Vijay,Yadav,vjyaav99@gmail.com,End User,, +Vijay,Yadav,vjyaav100@gmail.com,End User,, +Vijay,Yadav,vjyaav101@gmail.com,End User,, +Vijay,Yadav,vjyaav102@gmail.com,End User,, +Vijay,Yadav,vjyaav103@gmail.com,End User,, +Vijay,Yadav,vjyaav104@gmail.com,End User,, +Vijay,Yadav,vjyaav105@gmail.com,End User,, +Vijay,Yadav,vjyaav106@gmail.com,End User,, +Vijay,Yadav,vjyaav107@gmail.com,End User,, +Vijay,Yadav,vjyaav108@gmail.com,End User,, +Vijay,Yadav,vjyaav109@gmail.com,End User,, +Vijay,Yadav,vjyaav110@gmail.com,End User,, +Vijay,Yadav,vjyaav111@gmail.com,End User,, +Vijay,Yadav,vjyaav112@gmail.com,End User,, +Vijay,Yadav,vjyaav113@gmail.com,End User,, +Vijay,Yadav,vjyaav114@gmail.com,End User,, +Vijay,Yadav,vjyaav115@gmail.com,End User,, +Vijay,Yadav,vjyaav116@gmail.com,End User,, +Vijay,Yadav,vjyaav117@gmail.com,End User,, +Vijay,Yadav,vjyaav118@gmail.com,End User,, +Vijay,Yadav,vjyaav119@gmail.com,End User,, +Vijay,Yadav,vjyaav120@gmail.com,End User,, +Vijay,Yadav,vjyaav121@gmail.com,End User,, +Vijay,Yadav,vjyaav122@gmail.com,End User,, +Vijay,Yadav,vjyaav123@gmail.com,End User,, +Vijay,Yadav,vjyaav124@gmail.com,End User,, +Vijay,Yadav,vjyaav125@gmail.com,End User,, +Vijay,Yadav,vjyaav126@gmail.com,End User,, +Vijay,Yadav,vjyaav127@gmail.com,End User,, +Vijay,Yadav,vjyaav128@gmail.com,End User,, +Vijay,Yadav,vjyaav129@gmail.com,End User,, +Vijay,Yadav,vjyaav130@gmail.com,End User,, +Vijay,Yadav,vjyaav131@gmail.com,End User,, +Vijay,Yadav,vjyaav132@gmail.com,End User,, +Vijay,Yadav,vjyaav133@gmail.com,End User,, +Vijay,Yadav,vjyaav134@gmail.com,End User,, +Vijay,Yadav,vjyaav135@gmail.com,End User,, +Vijay,Yadav,vjyaav136@gmail.com,End User,, +Vijay,Yadav,vjyaav137@gmail.com,End User,, +Vijay,Yadav,vjyaav138@gmail.com,End User,, +Vijay,Yadav,vjyaav139@gmail.com,End User,, +Vijay,Yadav,vjyaav140@gmail.com,End User,, +Vijay,Yadav,vjyaav141@gmail.com,End User,, +Vijay,Yadav,vjyaav142@gmail.com,End User,, +Vijay,Yadav,vjyaav143@gmail.com,End User,, +Vijay,Yadav,vjyaav144@gmail.com,End User,, +Vijay,Yadav,vjyaav145@gmail.com,End User,, +Vijay,Yadav,vjyaav146@gmail.com,End User,, +Vijay,Yadav,vjyaav147@gmail.com,End User,, +Vijay,Yadav,vjyaav148@gmail.com,End User,, +Vijay,Yadav,vjyaav149@gmail.com,End User,, +Vijay,Yadav,vjyaav150@gmail.com,End User,, +Vijay,Yadav,vjyaav151@gmail.com,End User,, +Vijay,Yadav,vjyaav152@gmail.com,End User,, +Vijay,Yadav,vjyaav153@gmail.com,End User,, +Vijay,Yadav,vjyaav154@gmail.com,End User,, +Vijay,Yadav,vjyaav155@gmail.com,End User,, +Vijay,Yadav,vjyaav156@gmail.com,End User,, +Vijay,Yadav,vjyaav157@gmail.com,End User,, +Vijay,Yadav,vjyaav158@gmail.com,End User,, +Vijay,Yadav,vjyaav159@gmail.com,End User,, +Vijay,Yadav,vjyaav160@gmail.com,End User,, +Vijay,Yadav,vjyaav161@gmail.com,End User,, +Vijay,Yadav,vjyaav162@gmail.com,End User,, +Vijay,Yadav,vjyaav163@gmail.com,End User,, +Vijay,Yadav,vjyaav164@gmail.com,End User,, +Vijay,Yadav,vjyaav165@gmail.com,End User,, +Vijay,Yadav,vjyaav166@gmail.com,End User,, +Vijay,Yadav,vjyaav167@gmail.com,End User,, +Vijay,Yadav,vjyaav168@gmail.com,End User,, +Vijay,Yadav,vjyaav169@gmail.com,End User,, +Vijay,Yadav,vjyaav170@gmail.com,End User,, +Vijay,Yadav,vjyaav171@gmail.com,End User,, +Vijay,Yadav,vjyaav172@gmail.com,End User,, +Vijay,Yadav,vjyaav173@gmail.com,End User,, +Vijay,Yadav,vjyaav174@gmail.com,End User,, +Vijay,Yadav,vjyaav175@gmail.com,End User,, +Vijay,Yadav,vjyaav176@gmail.com,End User,, +Vijay,Yadav,vjyaav177@gmail.com,End User,, +Vijay,Yadav,vjyaav178@gmail.com,End User,, +Vijay,Yadav,vjyaav179@gmail.com,End User,, +Vijay,Yadav,vjyaav180@gmail.com,End User,, +Vijay,Yadav,vjyaav181@gmail.com,End User,, +Vijay,Yadav,vjyaav182@gmail.com,End User,, +Vijay,Yadav,vjyaav183@gmail.com,End User,, +Vijay,Yadav,vjyaav184@gmail.com,End User,, +Vijay,Yadav,vjyaav185@gmail.com,End User,, +Vijay,Yadav,vjyaav186@gmail.com,End User,, +Vijay,Yadav,vjyaav187@gmail.com,End User,, +Vijay,Yadav,vjyaav188@gmail.com,End User,, +Vijay,Yadav,vjyaav189@gmail.com,End User,, +Vijay,Yadav,vjyaav190@gmail.com,End User,, +Vijay,Yadav,vjyaav191@gmail.com,End User,, +Vijay,Yadav,vjyaav192@gmail.com,End User,, +Vijay,Yadav,vjyaav193@gmail.com,End User,, +Vijay,Yadav,vjyaav194@gmail.com,End User,, +Vijay,Yadav,vjyaav195@gmail.com,End User,, +Vijay,Yadav,vjyaav196@gmail.com,End User,, +Vijay,Yadav,vjyaav197@gmail.com,End User,, +Vijay,Yadav,vjyaav198@gmail.com,End User,, +Vijay,Yadav,vjyaav199@gmail.com,End User,, +Vijay,Yadav,vjyaav200@gmail.com,End User,, +Vijay,Yadav,vjyaav201@gmail.com,End User,, +Vijay,Yadav,vjyaav202@gmail.com,End User,, +Vijay,Yadav,vjyaav203@gmail.com,End User,, +Vijay,Yadav,vjyaav204@gmail.com,End User,, +Vijay,Yadav,vjyaav205@gmail.com,End User,, +Vijay,Yadav,vjyaav206@gmail.com,End User,, +Vijay,Yadav,vjyaav207@gmail.com,End User,, +Vijay,Yadav,vjyaav208@gmail.com,End User,, +Vijay,Yadav,vjyaav209@gmail.com,End User,, +Vijay,Yadav,vjyaav210@gmail.com,End User,, +Vijay,Yadav,vjyaav211@gmail.com,End User,, +Vijay,Yadav,vjyaav212@gmail.com,End User,, +Vijay,Yadav,vjyaav213@gmail.com,End User,, +Vijay,Yadav,vjyaav214@gmail.com,End User,, +Vijay,Yadav,vjyaav215@gmail.com,End User,, +Vijay,Yadav,vjyaav216@gmail.com,End User,, +Vijay,Yadav,vjyaav217@gmail.com,End User,, +Vijay,Yadav,vjyaav218@gmail.com,End User,, +Vijay,Yadav,vjyaav219@gmail.com,End User,, +Vijay,Yadav,vjyaav220@gmail.com,End User,, +Vijay,Yadav,vjyaav221@gmail.com,End User,, +Vijay,Yadav,vjyaav222@gmail.com,End User,, +Vijay,Yadav,vjyaav223@gmail.com,End User,, +Vijay,Yadav,vjyaav224@gmail.com,End User,, +Vijay,Yadav,vjyaav225@gmail.com,End User,, +Vijay,Yadav,vjyaav226@gmail.com,End User,, +Vijay,Yadav,vjyaav227@gmail.com,End User,, +Vijay,Yadav,vjyaav228@gmail.com,End User,, +Vijay,Yadav,vjyaav229@gmail.com,End User,, +Vijay,Yadav,vjyaav230@gmail.com,End User,, +Vijay,Yadav,vjyaav231@gmail.com,End User,, +Vijay,Yadav,vjyaav232@gmail.com,End User,, +Vijay,Yadav,vjyaav233@gmail.com,End User,, +Vijay,Yadav,vjyaav234@gmail.com,End User,, +Vijay,Yadav,vjyaav235@gmail.com,End User,, +Vijay,Yadav,vjyaav236@gmail.com,End User,, +Vijay,Yadav,vjyaav237@gmail.com,End User,, +Vijay,Yadav,vjyaav238@gmail.com,End User,, +Vijay,Yadav,vjyaav239@gmail.com,End User,, +Vijay,Yadav,vjyaav240@gmail.com,End User,, +Vijay,Yadav,vjyaav241@gmail.com,End User,, +Vijay,Yadav,vjyaav242@gmail.com,End User,, +Vijay,Yadav,vjyaav243@gmail.com,End User,, +Vijay,Yadav,vjyaav244@gmail.com,End User,, +Vijay,Yadav,vjyaav245@gmail.com,End User,, +Vijay,Yadav,vjyaav246@gmail.com,End User,, +Vijay,Yadav,vjyaav247@gmail.com,End User,, +Vijay,Yadav,vjyaav248@gmail.com,End User,, +Vijay,Yadav,vjyaav249@gmail.com,End User,, +Vijay,Yadav,vjyaav250@gmail.com,End User,, +Vijay,Yadav,vjyaav251@gmail.com,End User,, \ No newline at end of file diff --git a/cypress-tests/cypress/fixtures/bulkUser/missing_email.csv b/cypress-tests/cypress/fixtures/bulkUser/missing_email.csv new file mode 100644 index 0000000000..dd1b93fca2 --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/missing_email.csv @@ -0,0 +1,3 @@ +First Name,Last Name,Email,User Role,Group +test,test,,Admin,Admin +test,test,,Builder,Builder diff --git a/cypress-tests/cypress/fixtures/bulkUser/missing_email_ee.csv b/cypress-tests/cypress/fixtures/bulkUser/missing_email_ee.csv new file mode 100644 index 0000000000..c9fa53181a --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/missing_email_ee.csv @@ -0,0 +1,3 @@ +First Name,Last Name,Email,User Role,Group,Metadata +,,withoutname1@gmail.com,Admin,Admin, +,,withoutname2@gmail.com,Builder,Builder, diff --git a/cypress-tests/cypress/fixtures/bulkUser/missing_name.csv b/cypress-tests/cypress/fixtures/bulkUser/missing_name.csv new file mode 100644 index 0000000000..b4bc6d323d --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/missing_name.csv @@ -0,0 +1,3 @@ +First Name,Last Name,Email,User Role,Group +,,withoutname1@gmail.com,Admin,Admin +,,withoutname2@gmail.com,Builder,Builder diff --git a/cypress-tests/cypress/fixtures/bulkUser/missing_name_ee.csv b/cypress-tests/cypress/fixtures/bulkUser/missing_name_ee.csv new file mode 100644 index 0000000000..c9fa53181a --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/missing_name_ee.csv @@ -0,0 +1,3 @@ +First Name,Last Name,Email,User Role,Group,Metadata +,,withoutname1@gmail.com,Admin,Admin, +,,withoutname2@gmail.com,Builder,Builder, diff --git a/cypress-tests/cypress/fixtures/bulkUser/missing_role.csv b/cypress-tests/cypress/fixtures/bulkUser/missing_role.csv new file mode 100644 index 0000000000..2330a5f7df --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/missing_role.csv @@ -0,0 +1,3 @@ +First Name,Last Name,Email,User Role,Group +Test,Example,test12@gmail.com,, +Test,Example,test13@gmail.com,, diff --git a/cypress-tests/cypress/fixtures/bulkUser/missing_role_ee.csv b/cypress-tests/cypress/fixtures/bulkUser/missing_role_ee.csv new file mode 100644 index 0000000000..aec9c2b880 --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/missing_role_ee.csv @@ -0,0 +1,3 @@ +First Name,Last Name,Email,User Role,Group,Metadata +Test,Example,test12@gmail.com,,, +Test,Example,test13@gmail.com,,, diff --git a/cypress-tests/cypress/fixtures/bulkUser/non_existing_group_ee.csv b/cypress-tests/cypress/fixtures/bulkUser/non_existing_group_ee.csv new file mode 100644 index 0000000000..16620eb8cc --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/non_existing_group_ee.csv @@ -0,0 +1,3 @@ +First Name,Last Name,Email,User Role,Group,Metadata +test,test,demo1@gmail.com,Admin,test, +test,test,demo2@gmail.com,Builder,abc, diff --git a/cypress-tests/cypress/fixtures/bulkUser/same_email_ee.csv b/cypress-tests/cypress/fixtures/bulkUser/same_email_ee.csv new file mode 100644 index 0000000000..634585c86e --- /dev/null +++ b/cypress-tests/cypress/fixtures/bulkUser/same_email_ee.csv @@ -0,0 +1,4 @@ +First Name,Last Name,Email,User Role,Group,Metadata +test,test,demo11@gmail.com,Admin,, +test,test,demo11@gmail.com,Builder,, + diff --git a/cypress-tests/cypress/fixtures/restAPI/storedId.json b/cypress-tests/cypress/fixtures/restAPI/storedId.json new file mode 100644 index 0000000000..13a74c1a42 --- /dev/null +++ b/cypress-tests/cypress/fixtures/restAPI/storedId.json @@ -0,0 +1,3 @@ +{ + "id": "bff6583db942c77249ba" +} \ No newline at end of file diff --git a/cypress-tests/cypress/fixtures/templates/import_named_file.json b/cypress-tests/cypress/fixtures/templates/import_named_file.json new file mode 100644 index 0000000000..0636a8b3b3 --- /dev/null +++ b/cypress-tests/cypress/fixtures/templates/import_named_file.json @@ -0,0 +1,1198 @@ +{ + "app": [ + { + "definition": { + "appV2": { + "type": "front-end", + "id": "8819afae-57b6-447d-93dd-6dc108169bfe", + "name": "AI powered code explainer", + "slug": "8819afae-57b6-447d-93dd-6dc108169bfe", + "isPublic": false, + "isMaintenanceOn": false, + "icon": "apps", + "organizationId": "a51da635-3a28-4b10-a6f4-7ba34e254987", + "currentVersionId": null, + "userId": "988bb9f5-e577-4065-8d3c-4fcf731ee15d", + "workflowApiToken": null, + "workflowEnabled": false, + "createdAt": "2025-02-27T07:28:52.129Z", + "creationMode": "DEFAULT", + "updatedAt": "2025-02-27T07:28:52.281Z", + "editingVersion": { + "id": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "name": "v1", + "definition": null, + "globalSettings": { + "hideHeader": true, + "appInMaintenance": false, + "canvasMaxWidth": 100, + "canvasMaxWidthType": "%", + "canvasMaxHeight": 2400, + "canvasBackgroundColor": "#edeff5", + "backgroundFxQuery": "", + "appMode": "auto" + }, + "pageSettings": { + "properties": { + "disableMenu": { + "value": "{{true}}", + "fxActive": false + } + } + }, + "showViewerNavigation": false, + "homePageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "appId": "8819afae-57b6-447d-93dd-6dc108169bfe", + "currentEnvironmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "promotedFrom": null, + "createdAt": "2025-02-27T07:28:52.144Z", + "updatedAt": "2025-02-27T07:28:52.274Z" + }, + "components": [ + { + "id": "7bf37542-4eaa-42d8-9827-1cf1f1649791", + "name": "container1", + "type": "Container", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": null, + "properties": {}, + "general": {}, + "styles": { + "backgroundColor": { + "value": "#ffffffff" + }, + "borderRadius": { + "value": "10" + }, + "borderColor": { + "value": "#ffffff00", + "fxActive": false + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "7367ab91-9541-4bd9-96f7-32da8bb61cf5", + "type": "desktop", + "top": 20, + "left": 1, + "width": 41, + "height": 70, + "componentId": "7bf37542-4eaa-42d8-9827-1cf1f1649791", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "9b1d3bec-c586-4f2b-acdf-09cea7addecc", + "name": "text1", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "7bf37542-4eaa-42d8-9827-1cf1f1649791", + "properties": { + "text": { + "value": "B R A N D" + } + }, + "general": {}, + "styles": { + "textColor": { + "value": "#000", + "fxActive": false + }, + "textSize": { + "value": "{{24}}" + }, + "fontWeight": { + "value": "bold" + }, + "boxShadow": { + "value": "0px 0px 0px 0px #00000040" + }, + "isScrollRequired": { + "value": "disabled" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "8c07e506-b1f5-4715-ab02-718fcce9295b", + "type": "desktop", + "top": 10, + "left": 1, + "width": 6, + "height": 40, + "componentId": "9b1d3bec-c586-4f2b-acdf-09cea7addecc", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "38100944-4325-49b7-8c70-de75cf5ce63d", + "name": "text2", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "7bf37542-4eaa-42d8-9827-1cf1f1649791", + "properties": { + "text": { + "value": "
AI Code Explainer
" + } + }, + "general": {}, + "styles": { + "textColor": { + "value": "#000", + "fxActive": false + }, + "textSize": { + "value": "{{20}}" + }, + "textAlign": { + "value": "right" + }, + "boxShadow": { + "value": "0px 0px 0px 0px #00000040" + }, + "isScrollRequired": { + "value": "disabled" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "129e63b6-9456-4cb7-94b0-7379743fec88", + "type": "desktop", + "top": 10, + "left": 25, + "width": 17, + "height": 40, + "componentId": "38100944-4325-49b7-8c70-de75cf5ce63d", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "name": "container2", + "type": "Container", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": null, + "properties": {}, + "general": {}, + "styles": { + "borderRadius": { + "value": "10" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "14b7706c-cebf-45dc-a555-3a60fdf01f3b", + "type": "desktop", + "top": 110, + "left": 1, + "width": 41, + "height": 620, + "componentId": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "ef46f35d-bf64-4ea0-8c9b-c525b87d6120", + "type": "mobile", + "top": 110, + "left": 1, + "width": 5, + "height": 200, + "componentId": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "fbdab782-4a3b-4811-9f43-35f6dfae8735", + "name": "text3", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Code to be explained" + } + }, + "general": {}, + "styles": { + "textSize": { + "value": "24" + }, + "fontWeight": { + "value": "bold" + }, + "isScrollRequired": { + "value": "disabled" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "cfc25680-87fe-45d1-8431-f009ba351ae2", + "type": "desktop", + "top": 20, + "left": 1, + "width": 20, + "height": 40, + "componentId": "fbdab782-4a3b-4811-9f43-35f6dfae8735", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "bbb13f33-25ee-4c97-8c08-7d7df99ab431", + "type": "mobile", + "top": 20, + "left": 9, + "width": 13.953488372093023, + "height": 40, + "componentId": "fbdab782-4a3b-4811-9f43-35f6dfae8735", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "dca350e6-c9f8-44aa-94d5-e6245cfb0ae2", + "name": "dropdown1", + "type": "DropDown", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "label": { + "value": "" + }, + "value": { + "value": "" + }, + "values": { + "value": "{{queries.32ff6874-7da0-4b88-ae05-3c9cda4a07dc.data.models.map(item => item.name)}}" + }, + "display_values": { + "value": "{{queries.32ff6874-7da0-4b88-ae05-3c9cda4a07dc.data.models.map(item => item.displayName)}}" + }, + "loadingState": { + "value": "{{queries.32ff6874-7da0-4b88-ae05-3c9cda4a07dc.isLoading}}", + "fxActive": true + }, + "placeholder": { + "value": "Select a model" + } + }, + "general": {}, + "styles": { + "borderRadius": { + "value": "5" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.267Z", + "layouts": [ + { + "id": "91cae02e-6d00-48ad-9750-1ae74bc9fd7f", + "type": "mobile", + "top": 10, + "left": 27, + "width": 18.6046511627907, + "height": 30, + "componentId": "dca350e6-c9f8-44aa-94d5-e6245cfb0ae2", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "bb56cbd1-57f7-4917-8a32-046a8e77ed33", + "type": "desktop", + "top": 480, + "left": 1, + "width": 20, + "height": 40, + "componentId": "dca350e6-c9f8-44aa-94d5-e6245cfb0ae2", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "98a7f66e-d446-4be5-b2e8-6bde808e9461", + "name": "textarea1", + "type": "TextArea", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "value": { + "value": "function addNumbers(a, b) {\n return a + b;\n}\n\nconst sum = addNumbers(5, 3);\nconsole.log(sum);" + }, + "placeholder": { + "value": "function addNumbers(a, b) {\n return a + b;\n}\n\nconst sum = addNumbers(5, 3);\nconsole.log(sum);" + } + }, + "general": {}, + "styles": { + "borderRadius": { + "value": "{{5}}" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "db411aca-2e7e-46ae-8bf6-75f664a636ea", + "type": "mobile", + "top": 100, + "left": 3, + "width": 13.953488372093023, + "height": 100, + "componentId": "98a7f66e-d446-4be5-b2e8-6bde808e9461", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "abdc0362-e37b-48d6-9cad-ccbdfcf6fd55", + "type": "desktop", + "top": 70, + "left": 1, + "width": 20, + "height": 270, + "componentId": "98a7f66e-d446-4be5-b2e8-6bde808e9461", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "37fbb9ed-5ac6-439b-82cb-35e276c89f49", + "name": "button1", + "type": "Button", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Generate explanation >>" + }, + "loadingState": { + "value": "{{false}}", + "fxActive": false + }, + "disabledState": { + "value": "{{components.dca350e6-c9f8-44aa-94d5-e6245cfb0ae2.value == undefined || queries.getCodeExplanation.isLoading}}", + "fxActive": true + } + }, + "general": {}, + "styles": { + "backgroundColor": { + "value": "#ffffff00" + }, + "textColor": { + "value": "#3e63ddff" + }, + "loaderColor": { + "value": "#3e63ddff" + }, + "borderRadius": { + "value": "{{5}}" + }, + "borderColor": { + "value": "#3e63ddff" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.267Z", + "layouts": [ + { + "id": "c110426c-5994-4d04-901d-883aafb9d2eb", + "type": "mobile", + "top": 420, + "left": 7, + "width": 6.976744186046512, + "height": 30, + "componentId": "37fbb9ed-5ac6-439b-82cb-35e276c89f49", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "a6a6b92e-0984-4e21-87fc-462a307e06dd", + "type": "desktop", + "top": 550, + "left": 1, + "width": 20, + "height": 40, + "componentId": "37fbb9ed-5ac6-439b-82cb-35e276c89f49", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "b56cb989-9b4f-4dcf-a6c4-70dcfe6aac1a", + "name": "dropdown2", + "type": "DropDown", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "values": { + "value": "{{[\n \"\",\n \"C#\",\n \"C++\",\n \"Dart\",\n \"Elixir\",\n \"Erlang\",\n \"F#\",\n \"Go\",\n \"Groovy\",\n \"Haskell\",\n \"Java\",\n \"JavaScript\",\n \"Kotlin\",\n \"Lua\",\n \"MATLAB\",\n \"Objective-C\",\n \"Perl\",\n \"PHP\",\n \"Python\",\n \"R\",\n \"Ruby\",\n \"Rust\",\n \"Scala\",\n \"Shell\",\n \"SQL\",\n \"Swift\",\n \"TypeScript\"\n]}}" + }, + "display_values": { + "value": "{{[\n \"Any language\",\n \"C#\",\n \"C++\",\n \"Dart\",\n \"Elixir\",\n \"Erlang\",\n \"F#\",\n \"Go\",\n \"Groovy\",\n \"Haskell\",\n \"Java\",\n \"JavaScript\",\n \"Kotlin\",\n \"Lua\",\n \"MATLAB\",\n \"Objective-C\",\n \"Perl\",\n \"PHP\",\n \"Python\",\n \"R\",\n \"Ruby\",\n \"Rust\",\n \"Scala\",\n \"Shell\",\n \"SQL\",\n \"Swift\",\n \"TypeScript\"\n]}}" + }, + "value": { + "value": "" + }, + "placeholder": { + "value": "Select a language" + }, + "label": { + "value": "" + } + }, + "general": {}, + "styles": {}, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "553e50a3-c9d4-4ae7-9b8d-da129cb2f32d", + "type": "mobile", + "top": 420, + "left": 2, + "width": 8, + "height": 30, + "componentId": "b56cb989-9b4f-4dcf-a6c4-70dcfe6aac1a", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "abeda4d6-0111-4b9a-bfb1-234c986ee777", + "type": "desktop", + "top": 390, + "left": 1, + "width": 20, + "height": 40, + "componentId": "b56cb989-9b4f-4dcf-a6c4-70dcfe6aac1a", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "7112d3b6-f7d5-4da8-84ad-f2db9ab962d8", + "name": "text6", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Language" + } + }, + "general": {}, + "styles": { + "fontWeight": { + "value": "bold" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "3b6ce6c5-bb8b-4145-991b-b4dc659ac9ae", + "type": "desktop", + "top": 360, + "left": 1, + "width": 14, + "height": 30, + "componentId": "7112d3b6-f7d5-4da8-84ad-f2db9ab962d8", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "d907a75f-162c-4e89-8bef-8ad4a6b10f6a", + "type": "mobile", + "top": 70, + "left": 4, + "width": 13.953488372093023, + "height": 40, + "componentId": "7112d3b6-f7d5-4da8-84ad-f2db9ab962d8", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "052d73d1-c415-4720-8963-36c94ce54b19", + "name": "text7", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "{{`
${queries.getCodeExplanation.data.candidates ? queries.getCodeExplanation.data.candidates[0].content.parts[0].text : \"\"}
`}}" + }, + "textFormat": { + "value": "html" + }, + "loadingState": { + "fxActive": true, + "value": "{{queries.5774aa01-0931-4036-8bfa-4d12e0b6bc8b.isLoading}}" + } + }, + "general": {}, + "styles": { + "borderColor": { + "value": "#ddddddff" + }, + "borderRadius": { + "value": "5" + }, + "verticalAlignment": { + "value": "top" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.267Z", + "layouts": [ + { + "id": "388a36e0-95ac-49f3-a97b-ad413efafb3a", + "type": "desktop", + "top": 70, + "left": 22, + "width": 20, + "height": 520, + "componentId": "052d73d1-c415-4720-8963-36c94ce54b19", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "1f2da6ab-3493-4ace-b5ae-ceadbd3e2fb0", + "type": "mobile", + "top": 290, + "left": 23, + "width": 6, + "height": 40, + "componentId": "052d73d1-c415-4720-8963-36c94ce54b19", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "3c5278e8-8cc5-442a-bc70-19d4cd72cec7", + "name": "text8", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Gemini Model" + } + }, + "general": {}, + "styles": { + "fontWeight": { + "value": "bold" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "e41fd57d-29e6-49e2-b3ee-75b3769f6d27", + "type": "mobile", + "top": 70, + "left": 4, + "width": 13.953488372093023, + "height": 40, + "componentId": "3c5278e8-8cc5-442a-bc70-19d4cd72cec7", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "7a1d68a9-e9f5-496e-9947-3d6e12c55b0c", + "type": "desktop", + "top": 450, + "left": 1, + "width": 14, + "height": 30, + "componentId": "3c5278e8-8cc5-442a-bc70-19d4cd72cec7", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "521f0268-02d8-4571-9efe-030c9816bdea", + "name": "text9", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Explanation" + } + }, + "general": {}, + "styles": { + "textSize": { + "value": "24" + }, + "fontWeight": { + "value": "bold" + }, + "isScrollRequired": { + "value": "disabled" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "81f9d6a1-90c4-422c-8602-1675a76f6de4", + "type": "desktop", + "top": 20, + "left": 22, + "width": 20, + "height": 40, + "componentId": "521f0268-02d8-4571-9efe-030c9816bdea", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "da2abfa1-769b-4784-87fb-5020e6610fed", + "type": "mobile", + "top": 20, + "left": 9, + "width": 13.953488372093023, + "height": 40, + "componentId": "521f0268-02d8-4571-9efe-030c9816bdea", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + } + ], + "pages": [ + { + "id": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "name": "Home", + "handle": "home", + "index": 1, + "disabled": false, + "hidden": false, + "icon": null, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.366Z", + "autoComputeLayout": true, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "pageGroupIndex": 1, + "pageGroupId": null, + "isPageGroup": false + } + ], + "events": [ + { + "id": "9417716e-b415-4532-aea4-8a2afa224f10", + "name": "onClick", + "index": 0, + "event": { + "eventId": "onClick", + "message": "Hello world!", + "queryId": "5774aa01-0931-4036-8bfa-4d12e0b6bc8b", + "actionId": "run-query", + "alertType": "info", + "queryName": "getCodeExplanation", + "parameters": {} + }, + "sourceId": "37fbb9ed-5ac6-439b-82cb-35e276c89f49", + "target": "component", + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.260Z" + } + ], + "dataQueries": [ + { + "id": "5774aa01-0931-4036-8bfa-4d12e0b6bc8b", + "name": "getCodeExplanation", + "options": { + "method": "post", + "url": "https://generativelanguage.googleapis.com/v1beta/{{components.dropdown1.value}}:generateContent", + "url_params": [ + [ + "key", + "{{constants.GEMINI_API_KEY}}" + ], + [ + "", + "" + ] + ], + "headers": [ + [ + "Content-Type", + "application/json" + ], + [ + "", + "" + ] + ], + "body": [ + [ + "", + "" + ] + ], + "json_body": "{\n \"contents\": [\n {\n \"parts\": [\n {\n \"text\": \"{{components.textarea1.value.replaceAll('\\n','\\\\n')}} - Generate a point-wise line by line explanation of this code in html formatting only. Keep only the explanation, and nothing else. {{components.dropdown2.value ? `The code is in ${components.dropdown2.value} language.` : 'Also identify the language of the code.'}}\"\n }\n ]\n }\n ]\n}", + "body_toggle": true, + "transformationLanguage": "javascript", + "enableTransformation": false, + "arrayValuesChanged": false, + "transformation": "// write your code here\n// return value will be set as data and the original data will be available as rawData\nreturn data.filter(row => row.amount > 1000);\n " + }, + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "32ff6874-7da0-4b88-ae05-3c9cda4a07dc", + "name": "getGeminiModels", + "options": { + "method": "get", + "url": "https://generativelanguage.googleapis.com/v1beta/models?key={{constants.GEMINI_API_KEY}}", + "url_params": [ + [ + "", + "" + ] + ], + "headers": [ + [ + "", + "" + ] + ], + "body": [ + [ + "", + "" + ] + ], + "json_body": null, + "body_toggle": false, + "transformationLanguage": "javascript", + "enableTransformation": false, + "runOnPageLoad": true + }, + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.983Z" + } + ], + "dataSources": [ + { + "id": "489072da-3239-4bd5-91b9-dee5f5da5335", + "name": "restapidefault", + "kind": "restapi", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.153Z", + "updatedAt": "2025-02-27T07:28:52.153Z" + }, + { + "id": "c462a40f-c7fa-4d55-98fe-9bc445271cab", + "name": "runjsdefault", + "kind": "runjs", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.163Z", + "updatedAt": "2025-02-27T07:28:52.163Z" + }, + { + "id": "cc0b5feb-29ea-47c0-9a9a-62c0e0b89ccb", + "name": "runpydefault", + "kind": "runpy", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.170Z", + "updatedAt": "2025-02-27T07:28:52.170Z" + }, + { + "id": "907dde15-1ac2-4f53-ba72-8e4e1d066f0e", + "name": "tooljetdbdefault", + "kind": "tooljetdb", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.176Z", + "updatedAt": "2025-02-27T07:28:52.176Z" + }, + { + "id": "66ad4e35-f981-47a6-99cd-31e9e7bbd9b3", + "name": "workflowsdefault", + "kind": "workflows", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.183Z", + "updatedAt": "2025-02-27T07:28:52.183Z" + } + ], + "appVersions": [ + { + "id": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "name": "v1", + "definition": null, + "globalSettings": { + "hideHeader": true, + "appInMaintenance": false, + "canvasMaxWidth": 100, + "canvasMaxWidthType": "%", + "canvasMaxHeight": 2400, + "canvasBackgroundColor": "#edeff5", + "backgroundFxQuery": "", + "appMode": "auto" + }, + "pageSettings": { + "properties": { + "disableMenu": { + "value": "{{true}}", + "fxActive": false + } + } + }, + "showViewerNavigation": false, + "homePageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "appId": "8819afae-57b6-447d-93dd-6dc108169bfe", + "currentEnvironmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "promotedFrom": null, + "createdAt": "2025-02-27T07:28:52.144Z", + "updatedAt": "2025-02-27T07:28:52.274Z" + } + ], + "appEnvironments": [ + { + "id": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "organizationId": "a51da635-3a28-4b10-a6f4-7ba34e254987", + "name": "development", + "isDefault": false, + "priority": 1, + "enabled": true, + "createdAt": "2025-02-27T07:28:36.425Z", + "updatedAt": "2025-02-27T07:28:36.425Z" + }, + { + "id": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "organizationId": "a51da635-3a28-4b10-a6f4-7ba34e254987", + "name": "staging", + "isDefault": false, + "priority": 2, + "enabled": true, + "createdAt": "2025-02-27T07:28:36.425Z", + "updatedAt": "2025-02-27T07:28:36.425Z" + }, + { + "id": "eb006618-493c-48ac-8a4d-e80d208d29de", + "organizationId": "a51da635-3a28-4b10-a6f4-7ba34e254987", + "name": "production", + "isDefault": true, + "priority": 3, + "enabled": true, + "createdAt": "2025-02-27T07:28:36.425Z", + "updatedAt": "2025-02-27T07:28:36.425Z" + } + ], + "dataSourceOptions": [ + { + "id": "98e3239b-e54b-4b59-992b-d9bbfb68d1e6", + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.159Z", + "updatedAt": "2025-02-27T07:28:52.159Z" + }, + { + "id": "c997ee43-2f17-46aa-bc35-32af668d546b", + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.159Z", + "updatedAt": "2025-02-27T07:28:52.159Z" + }, + { + "id": "01bbd244-59c9-4965-8024-401c8f1961fd", + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.159Z", + "updatedAt": "2025-02-27T07:28:52.159Z" + }, + { + "id": "8a8096f7-6b6f-49ca-95de-596c5670c832", + "dataSourceId": "c462a40f-c7fa-4d55-98fe-9bc445271cab", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.167Z", + "updatedAt": "2025-02-27T07:28:52.167Z" + }, + { + "id": "1e2dcc6d-8944-4b5b-abd4-72f7201bf657", + "dataSourceId": "c462a40f-c7fa-4d55-98fe-9bc445271cab", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.167Z", + "updatedAt": "2025-02-27T07:28:52.167Z" + }, + { + "id": "24bfe83d-b2d3-4bf0-8730-1bd14d96cf7a", + "dataSourceId": "c462a40f-c7fa-4d55-98fe-9bc445271cab", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.167Z", + "updatedAt": "2025-02-27T07:28:52.167Z" + }, + { + "id": "44600a77-04bf-4b30-8613-bd7a7bff6508", + "dataSourceId": "cc0b5feb-29ea-47c0-9a9a-62c0e0b89ccb", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.174Z", + "updatedAt": "2025-02-27T07:28:52.174Z" + }, + { + "id": "b70c436e-70e5-48d3-84d2-ca2c784c7425", + "dataSourceId": "cc0b5feb-29ea-47c0-9a9a-62c0e0b89ccb", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.174Z", + "updatedAt": "2025-02-27T07:28:52.174Z" + }, + { + "id": "7bd1351e-61b9-4ba6-9e57-8eca1a3a0df3", + "dataSourceId": "cc0b5feb-29ea-47c0-9a9a-62c0e0b89ccb", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.174Z", + "updatedAt": "2025-02-27T07:28:52.174Z" + }, + { + "id": "f8268c18-8453-48ac-acf7-46c2d0c75c75", + "dataSourceId": "907dde15-1ac2-4f53-ba72-8e4e1d066f0e", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.181Z", + "updatedAt": "2025-02-27T07:28:52.181Z" + }, + { + "id": "cd59c697-6ac2-4594-b8f6-493676e8b3c7", + "dataSourceId": "907dde15-1ac2-4f53-ba72-8e4e1d066f0e", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.181Z", + "updatedAt": "2025-02-27T07:28:52.181Z" + }, + { + "id": "7270ef0a-f6d6-498e-9959-4b646a30d5d1", + "dataSourceId": "907dde15-1ac2-4f53-ba72-8e4e1d066f0e", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.181Z", + "updatedAt": "2025-02-27T07:28:52.181Z" + }, + { + "id": "fe4824d5-57fa-42dc-9812-4af634737898", + "dataSourceId": "66ad4e35-f981-47a6-99cd-31e9e7bbd9b3", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.189Z", + "updatedAt": "2025-02-27T07:28:52.189Z" + }, + { + "id": "521a6d91-484b-45bc-af8f-aaceb0dc515c", + "dataSourceId": "66ad4e35-f981-47a6-99cd-31e9e7bbd9b3", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.189Z", + "updatedAt": "2025-02-27T07:28:52.189Z" + }, + { + "id": "f5955bf2-c148-4544-acd9-a9a92a943e5b", + "dataSourceId": "66ad4e35-f981-47a6-99cd-31e9e7bbd9b3", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.189Z", + "updatedAt": "2025-02-27T07:28:52.189Z" + } + ], + "schemaDetails": { + "multiPages": true, + "multiEnv": true, + "globalDataSources": true + } + } + } + } + ], + "tooljet_version": "3.5.3-ee-lts", + "appName": "app_json" +} \ No newline at end of file diff --git a/cypress-tests/cypress/fixtures/templates/import_unnamed_file.json b/cypress-tests/cypress/fixtures/templates/import_unnamed_file.json new file mode 100644 index 0000000000..93c2501a51 --- /dev/null +++ b/cypress-tests/cypress/fixtures/templates/import_unnamed_file.json @@ -0,0 +1,1197 @@ +{ + "app": [ + { + "definition": { + "appV2": { + "type": "front-end", + "id": "8819afae-57b6-447d-93dd-6dc108169bfe", + "name": "AI powered code explainer", + "slug": "8819afae-57b6-447d-93dd-6dc108169bfe", + "isPublic": false, + "isMaintenanceOn": false, + "icon": "apps", + "organizationId": "a51da635-3a28-4b10-a6f4-7ba34e254987", + "currentVersionId": null, + "userId": "988bb9f5-e577-4065-8d3c-4fcf731ee15d", + "workflowApiToken": null, + "workflowEnabled": false, + "createdAt": "2025-02-27T07:28:52.129Z", + "creationMode": "DEFAULT", + "updatedAt": "2025-02-27T07:28:52.281Z", + "editingVersion": { + "id": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "name": "v1", + "definition": null, + "globalSettings": { + "hideHeader": true, + "appInMaintenance": false, + "canvasMaxWidth": 100, + "canvasMaxWidthType": "%", + "canvasMaxHeight": 2400, + "canvasBackgroundColor": "#edeff5", + "backgroundFxQuery": "", + "appMode": "auto" + }, + "pageSettings": { + "properties": { + "disableMenu": { + "value": "{{true}}", + "fxActive": false + } + } + }, + "showViewerNavigation": false, + "homePageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "appId": "8819afae-57b6-447d-93dd-6dc108169bfe", + "currentEnvironmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "promotedFrom": null, + "createdAt": "2025-02-27T07:28:52.144Z", + "updatedAt": "2025-02-27T07:28:52.274Z" + }, + "components": [ + { + "id": "7bf37542-4eaa-42d8-9827-1cf1f1649791", + "name": "container1", + "type": "Container", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": null, + "properties": {}, + "general": {}, + "styles": { + "backgroundColor": { + "value": "#ffffffff" + }, + "borderRadius": { + "value": "10" + }, + "borderColor": { + "value": "#ffffff00", + "fxActive": false + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "7367ab91-9541-4bd9-96f7-32da8bb61cf5", + "type": "desktop", + "top": 20, + "left": 1, + "width": 41, + "height": 70, + "componentId": "7bf37542-4eaa-42d8-9827-1cf1f1649791", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "9b1d3bec-c586-4f2b-acdf-09cea7addecc", + "name": "text1", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "7bf37542-4eaa-42d8-9827-1cf1f1649791", + "properties": { + "text": { + "value": "B R A N D" + } + }, + "general": {}, + "styles": { + "textColor": { + "value": "#000", + "fxActive": false + }, + "textSize": { + "value": "{{24}}" + }, + "fontWeight": { + "value": "bold" + }, + "boxShadow": { + "value": "0px 0px 0px 0px #00000040" + }, + "isScrollRequired": { + "value": "disabled" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "8c07e506-b1f5-4715-ab02-718fcce9295b", + "type": "desktop", + "top": 10, + "left": 1, + "width": 6, + "height": 40, + "componentId": "9b1d3bec-c586-4f2b-acdf-09cea7addecc", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "38100944-4325-49b7-8c70-de75cf5ce63d", + "name": "text2", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "7bf37542-4eaa-42d8-9827-1cf1f1649791", + "properties": { + "text": { + "value": "
AI Code Explainer
" + } + }, + "general": {}, + "styles": { + "textColor": { + "value": "#000", + "fxActive": false + }, + "textSize": { + "value": "{{20}}" + }, + "textAlign": { + "value": "right" + }, + "boxShadow": { + "value": "0px 0px 0px 0px #00000040" + }, + "isScrollRequired": { + "value": "disabled" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "129e63b6-9456-4cb7-94b0-7379743fec88", + "type": "desktop", + "top": 10, + "left": 25, + "width": 17, + "height": 40, + "componentId": "38100944-4325-49b7-8c70-de75cf5ce63d", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "name": "container2", + "type": "Container", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": null, + "properties": {}, + "general": {}, + "styles": { + "borderRadius": { + "value": "10" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "14b7706c-cebf-45dc-a555-3a60fdf01f3b", + "type": "desktop", + "top": 110, + "left": 1, + "width": 41, + "height": 620, + "componentId": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "ef46f35d-bf64-4ea0-8c9b-c525b87d6120", + "type": "mobile", + "top": 110, + "left": 1, + "width": 5, + "height": 200, + "componentId": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "fbdab782-4a3b-4811-9f43-35f6dfae8735", + "name": "text3", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Code to be explained" + } + }, + "general": {}, + "styles": { + "textSize": { + "value": "24" + }, + "fontWeight": { + "value": "bold" + }, + "isScrollRequired": { + "value": "disabled" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "cfc25680-87fe-45d1-8431-f009ba351ae2", + "type": "desktop", + "top": 20, + "left": 1, + "width": 20, + "height": 40, + "componentId": "fbdab782-4a3b-4811-9f43-35f6dfae8735", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "bbb13f33-25ee-4c97-8c08-7d7df99ab431", + "type": "mobile", + "top": 20, + "left": 9, + "width": 13.953488372093023, + "height": 40, + "componentId": "fbdab782-4a3b-4811-9f43-35f6dfae8735", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "dca350e6-c9f8-44aa-94d5-e6245cfb0ae2", + "name": "dropdown1", + "type": "DropDown", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "label": { + "value": "" + }, + "value": { + "value": "" + }, + "values": { + "value": "{{queries.32ff6874-7da0-4b88-ae05-3c9cda4a07dc.data.models.map(item => item.name)}}" + }, + "display_values": { + "value": "{{queries.32ff6874-7da0-4b88-ae05-3c9cda4a07dc.data.models.map(item => item.displayName)}}" + }, + "loadingState": { + "value": "{{queries.32ff6874-7da0-4b88-ae05-3c9cda4a07dc.isLoading}}", + "fxActive": true + }, + "placeholder": { + "value": "Select a model" + } + }, + "general": {}, + "styles": { + "borderRadius": { + "value": "5" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.267Z", + "layouts": [ + { + "id": "91cae02e-6d00-48ad-9750-1ae74bc9fd7f", + "type": "mobile", + "top": 10, + "left": 27, + "width": 18.6046511627907, + "height": 30, + "componentId": "dca350e6-c9f8-44aa-94d5-e6245cfb0ae2", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "bb56cbd1-57f7-4917-8a32-046a8e77ed33", + "type": "desktop", + "top": 480, + "left": 1, + "width": 20, + "height": 40, + "componentId": "dca350e6-c9f8-44aa-94d5-e6245cfb0ae2", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "98a7f66e-d446-4be5-b2e8-6bde808e9461", + "name": "textarea1", + "type": "TextArea", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "value": { + "value": "function addNumbers(a, b) {\n return a + b;\n}\n\nconst sum = addNumbers(5, 3);\nconsole.log(sum);" + }, + "placeholder": { + "value": "function addNumbers(a, b) {\n return a + b;\n}\n\nconst sum = addNumbers(5, 3);\nconsole.log(sum);" + } + }, + "general": {}, + "styles": { + "borderRadius": { + "value": "{{5}}" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "db411aca-2e7e-46ae-8bf6-75f664a636ea", + "type": "mobile", + "top": 100, + "left": 3, + "width": 13.953488372093023, + "height": 100, + "componentId": "98a7f66e-d446-4be5-b2e8-6bde808e9461", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "abdc0362-e37b-48d6-9cad-ccbdfcf6fd55", + "type": "desktop", + "top": 70, + "left": 1, + "width": 20, + "height": 270, + "componentId": "98a7f66e-d446-4be5-b2e8-6bde808e9461", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "37fbb9ed-5ac6-439b-82cb-35e276c89f49", + "name": "button1", + "type": "Button", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Generate explanation >>" + }, + "loadingState": { + "value": "{{false}}", + "fxActive": false + }, + "disabledState": { + "value": "{{components.dca350e6-c9f8-44aa-94d5-e6245cfb0ae2.value == undefined || queries.getCodeExplanation.isLoading}}", + "fxActive": true + } + }, + "general": {}, + "styles": { + "backgroundColor": { + "value": "#ffffff00" + }, + "textColor": { + "value": "#3e63ddff" + }, + "loaderColor": { + "value": "#3e63ddff" + }, + "borderRadius": { + "value": "{{5}}" + }, + "borderColor": { + "value": "#3e63ddff" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.267Z", + "layouts": [ + { + "id": "c110426c-5994-4d04-901d-883aafb9d2eb", + "type": "mobile", + "top": 420, + "left": 7, + "width": 6.976744186046512, + "height": 30, + "componentId": "37fbb9ed-5ac6-439b-82cb-35e276c89f49", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "a6a6b92e-0984-4e21-87fc-462a307e06dd", + "type": "desktop", + "top": 550, + "left": 1, + "width": 20, + "height": 40, + "componentId": "37fbb9ed-5ac6-439b-82cb-35e276c89f49", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "b56cb989-9b4f-4dcf-a6c4-70dcfe6aac1a", + "name": "dropdown2", + "type": "DropDown", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "values": { + "value": "{{[\n \"\",\n \"C#\",\n \"C++\",\n \"Dart\",\n \"Elixir\",\n \"Erlang\",\n \"F#\",\n \"Go\",\n \"Groovy\",\n \"Haskell\",\n \"Java\",\n \"JavaScript\",\n \"Kotlin\",\n \"Lua\",\n \"MATLAB\",\n \"Objective-C\",\n \"Perl\",\n \"PHP\",\n \"Python\",\n \"R\",\n \"Ruby\",\n \"Rust\",\n \"Scala\",\n \"Shell\",\n \"SQL\",\n \"Swift\",\n \"TypeScript\"\n]}}" + }, + "display_values": { + "value": "{{[\n \"Any language\",\n \"C#\",\n \"C++\",\n \"Dart\",\n \"Elixir\",\n \"Erlang\",\n \"F#\",\n \"Go\",\n \"Groovy\",\n \"Haskell\",\n \"Java\",\n \"JavaScript\",\n \"Kotlin\",\n \"Lua\",\n \"MATLAB\",\n \"Objective-C\",\n \"Perl\",\n \"PHP\",\n \"Python\",\n \"R\",\n \"Ruby\",\n \"Rust\",\n \"Scala\",\n \"Shell\",\n \"SQL\",\n \"Swift\",\n \"TypeScript\"\n]}}" + }, + "value": { + "value": "" + }, + "placeholder": { + "value": "Select a language" + }, + "label": { + "value": "" + } + }, + "general": {}, + "styles": {}, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "553e50a3-c9d4-4ae7-9b8d-da129cb2f32d", + "type": "mobile", + "top": 420, + "left": 2, + "width": 8, + "height": 30, + "componentId": "b56cb989-9b4f-4dcf-a6c4-70dcfe6aac1a", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "abeda4d6-0111-4b9a-bfb1-234c986ee777", + "type": "desktop", + "top": 390, + "left": 1, + "width": 20, + "height": 40, + "componentId": "b56cb989-9b4f-4dcf-a6c4-70dcfe6aac1a", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "7112d3b6-f7d5-4da8-84ad-f2db9ab962d8", + "name": "text6", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Language" + } + }, + "general": {}, + "styles": { + "fontWeight": { + "value": "bold" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "3b6ce6c5-bb8b-4145-991b-b4dc659ac9ae", + "type": "desktop", + "top": 360, + "left": 1, + "width": 14, + "height": 30, + "componentId": "7112d3b6-f7d5-4da8-84ad-f2db9ab962d8", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "d907a75f-162c-4e89-8bef-8ad4a6b10f6a", + "type": "mobile", + "top": 70, + "left": 4, + "width": 13.953488372093023, + "height": 40, + "componentId": "7112d3b6-f7d5-4da8-84ad-f2db9ab962d8", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "052d73d1-c415-4720-8963-36c94ce54b19", + "name": "text7", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "{{`
${queries.getCodeExplanation.data.candidates ? queries.getCodeExplanation.data.candidates[0].content.parts[0].text : \"\"}
`}}" + }, + "textFormat": { + "value": "html" + }, + "loadingState": { + "fxActive": true, + "value": "{{queries.5774aa01-0931-4036-8bfa-4d12e0b6bc8b.isLoading}}" + } + }, + "general": {}, + "styles": { + "borderColor": { + "value": "#ddddddff" + }, + "borderRadius": { + "value": "5" + }, + "verticalAlignment": { + "value": "top" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.267Z", + "layouts": [ + { + "id": "388a36e0-95ac-49f3-a97b-ad413efafb3a", + "type": "desktop", + "top": 70, + "left": 22, + "width": 20, + "height": 520, + "componentId": "052d73d1-c415-4720-8963-36c94ce54b19", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "1f2da6ab-3493-4ace-b5ae-ceadbd3e2fb0", + "type": "mobile", + "top": 290, + "left": 23, + "width": 6, + "height": 40, + "componentId": "052d73d1-c415-4720-8963-36c94ce54b19", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "3c5278e8-8cc5-442a-bc70-19d4cd72cec7", + "name": "text8", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Gemini Model" + } + }, + "general": {}, + "styles": { + "fontWeight": { + "value": "bold" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "e41fd57d-29e6-49e2-b3ee-75b3769f6d27", + "type": "mobile", + "top": 70, + "left": 4, + "width": 13.953488372093023, + "height": 40, + "componentId": "3c5278e8-8cc5-442a-bc70-19d4cd72cec7", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "7a1d68a9-e9f5-496e-9947-3d6e12c55b0c", + "type": "desktop", + "top": 450, + "left": 1, + "width": 14, + "height": 30, + "componentId": "3c5278e8-8cc5-442a-bc70-19d4cd72cec7", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + }, + { + "id": "521f0268-02d8-4571-9efe-030c9816bdea", + "name": "text9", + "type": "Text", + "pageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "parent": "2727cf8a-1856-41cb-b716-c25afc2a15ad", + "properties": { + "text": { + "value": "Explanation" + } + }, + "general": {}, + "styles": { + "textSize": { + "value": "24" + }, + "fontWeight": { + "value": "bold" + }, + "isScrollRequired": { + "value": "disabled" + } + }, + "generalStyles": {}, + "displayPreferences": { + "showOnDesktop": { + "value": "{{true}}" + }, + "showOnMobile": { + "value": "{{false}}" + } + }, + "validation": {}, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z", + "layouts": [ + { + "id": "81f9d6a1-90c4-422c-8602-1675a76f6de4", + "type": "desktop", + "top": 20, + "left": 22, + "width": 20, + "height": 40, + "componentId": "521f0268-02d8-4571-9efe-030c9816bdea", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "da2abfa1-769b-4784-87fb-5020e6610fed", + "type": "mobile", + "top": 20, + "left": 9, + "width": 13.953488372093023, + "height": 40, + "componentId": "521f0268-02d8-4571-9efe-030c9816bdea", + "dimensionUnit": "count", + "updatedAt": "2025-02-27T07:28:52.148Z" + } + ] + } + ], + "pages": [ + { + "id": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "name": "Home", + "handle": "home", + "index": 1, + "disabled": false, + "hidden": false, + "icon": null, + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.366Z", + "autoComputeLayout": true, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "pageGroupIndex": 1, + "pageGroupId": null, + "isPageGroup": false + } + ], + "events": [ + { + "id": "9417716e-b415-4532-aea4-8a2afa224f10", + "name": "onClick", + "index": 0, + "event": { + "eventId": "onClick", + "message": "Hello world!", + "queryId": "5774aa01-0931-4036-8bfa-4d12e0b6bc8b", + "actionId": "run-query", + "alertType": "info", + "queryName": "getCodeExplanation", + "parameters": {} + }, + "sourceId": "37fbb9ed-5ac6-439b-82cb-35e276c89f49", + "target": "component", + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.260Z" + } + ], + "dataQueries": [ + { + "id": "5774aa01-0931-4036-8bfa-4d12e0b6bc8b", + "name": "getCodeExplanation", + "options": { + "method": "post", + "url": "https://generativelanguage.googleapis.com/v1beta/{{components.dropdown1.value}}:generateContent", + "url_params": [ + [ + "key", + "{{constants.GEMINI_API_KEY}}" + ], + [ + "", + "" + ] + ], + "headers": [ + [ + "Content-Type", + "application/json" + ], + [ + "", + "" + ] + ], + "body": [ + [ + "", + "" + ] + ], + "json_body": "{\n \"contents\": [\n {\n \"parts\": [\n {\n \"text\": \"{{components.textarea1.value.replaceAll('\\n','\\\\n')}} - Generate a point-wise line by line explanation of this code in html formatting only. Keep only the explanation, and nothing else. {{components.dropdown2.value ? `The code is in ${components.dropdown2.value} language.` : 'Also identify the language of the code.'}}\"\n }\n ]\n }\n ]\n}", + "body_toggle": true, + "transformationLanguage": "javascript", + "enableTransformation": false, + "arrayValuesChanged": false, + "transformation": "// write your code here\n// return value will be set as data and the original data will be available as rawData\nreturn data.filter(row => row.amount > 1000);\n " + }, + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.148Z" + }, + { + "id": "32ff6874-7da0-4b88-ae05-3c9cda4a07dc", + "name": "getGeminiModels", + "options": { + "method": "get", + "url": "https://generativelanguage.googleapis.com/v1beta/models?key={{constants.GEMINI_API_KEY}}", + "url_params": [ + [ + "", + "" + ] + ], + "headers": [ + [ + "", + "" + ] + ], + "body": [ + [ + "", + "" + ] + ], + "json_body": null, + "body_toggle": false, + "transformationLanguage": "javascript", + "enableTransformation": false, + "runOnPageLoad": true + }, + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "createdAt": "2025-02-27T07:28:52.148Z", + "updatedAt": "2025-02-27T07:28:52.983Z" + } + ], + "dataSources": [ + { + "id": "489072da-3239-4bd5-91b9-dee5f5da5335", + "name": "restapidefault", + "kind": "restapi", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.153Z", + "updatedAt": "2025-02-27T07:28:52.153Z" + }, + { + "id": "c462a40f-c7fa-4d55-98fe-9bc445271cab", + "name": "runjsdefault", + "kind": "runjs", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.163Z", + "updatedAt": "2025-02-27T07:28:52.163Z" + }, + { + "id": "cc0b5feb-29ea-47c0-9a9a-62c0e0b89ccb", + "name": "runpydefault", + "kind": "runpy", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.170Z", + "updatedAt": "2025-02-27T07:28:52.170Z" + }, + { + "id": "907dde15-1ac2-4f53-ba72-8e4e1d066f0e", + "name": "tooljetdbdefault", + "kind": "tooljetdb", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.176Z", + "updatedAt": "2025-02-27T07:28:52.176Z" + }, + { + "id": "66ad4e35-f981-47a6-99cd-31e9e7bbd9b3", + "name": "workflowsdefault", + "kind": "workflows", + "type": "static", + "pluginId": null, + "appVersionId": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "organizationId": null, + "scope": "local", + "createdAt": "2025-02-27T07:28:52.183Z", + "updatedAt": "2025-02-27T07:28:52.183Z" + } + ], + "appVersions": [ + { + "id": "430dd7d7-1cd1-4c36-975f-229a1aa7dcb8", + "name": "v1", + "definition": null, + "globalSettings": { + "hideHeader": true, + "appInMaintenance": false, + "canvasMaxWidth": 100, + "canvasMaxWidthType": "%", + "canvasMaxHeight": 2400, + "canvasBackgroundColor": "#edeff5", + "backgroundFxQuery": "", + "appMode": "auto" + }, + "pageSettings": { + "properties": { + "disableMenu": { + "value": "{{true}}", + "fxActive": false + } + } + }, + "showViewerNavigation": false, + "homePageId": "93c0473f-6ada-4f1d-9c05-8a4775466aab", + "appId": "8819afae-57b6-447d-93dd-6dc108169bfe", + "currentEnvironmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "promotedFrom": null, + "createdAt": "2025-02-27T07:28:52.144Z", + "updatedAt": "2025-02-27T07:28:52.274Z" + } + ], + "appEnvironments": [ + { + "id": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "organizationId": "a51da635-3a28-4b10-a6f4-7ba34e254987", + "name": "development", + "isDefault": false, + "priority": 1, + "enabled": true, + "createdAt": "2025-02-27T07:28:36.425Z", + "updatedAt": "2025-02-27T07:28:36.425Z" + }, + { + "id": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "organizationId": "a51da635-3a28-4b10-a6f4-7ba34e254987", + "name": "staging", + "isDefault": false, + "priority": 2, + "enabled": true, + "createdAt": "2025-02-27T07:28:36.425Z", + "updatedAt": "2025-02-27T07:28:36.425Z" + }, + { + "id": "eb006618-493c-48ac-8a4d-e80d208d29de", + "organizationId": "a51da635-3a28-4b10-a6f4-7ba34e254987", + "name": "production", + "isDefault": true, + "priority": 3, + "enabled": true, + "createdAt": "2025-02-27T07:28:36.425Z", + "updatedAt": "2025-02-27T07:28:36.425Z" + } + ], + "dataSourceOptions": [ + { + "id": "98e3239b-e54b-4b59-992b-d9bbfb68d1e6", + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.159Z", + "updatedAt": "2025-02-27T07:28:52.159Z" + }, + { + "id": "c997ee43-2f17-46aa-bc35-32af668d546b", + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.159Z", + "updatedAt": "2025-02-27T07:28:52.159Z" + }, + { + "id": "01bbd244-59c9-4965-8024-401c8f1961fd", + "dataSourceId": "489072da-3239-4bd5-91b9-dee5f5da5335", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.159Z", + "updatedAt": "2025-02-27T07:28:52.159Z" + }, + { + "id": "8a8096f7-6b6f-49ca-95de-596c5670c832", + "dataSourceId": "c462a40f-c7fa-4d55-98fe-9bc445271cab", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.167Z", + "updatedAt": "2025-02-27T07:28:52.167Z" + }, + { + "id": "1e2dcc6d-8944-4b5b-abd4-72f7201bf657", + "dataSourceId": "c462a40f-c7fa-4d55-98fe-9bc445271cab", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.167Z", + "updatedAt": "2025-02-27T07:28:52.167Z" + }, + { + "id": "24bfe83d-b2d3-4bf0-8730-1bd14d96cf7a", + "dataSourceId": "c462a40f-c7fa-4d55-98fe-9bc445271cab", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.167Z", + "updatedAt": "2025-02-27T07:28:52.167Z" + }, + { + "id": "44600a77-04bf-4b30-8613-bd7a7bff6508", + "dataSourceId": "cc0b5feb-29ea-47c0-9a9a-62c0e0b89ccb", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.174Z", + "updatedAt": "2025-02-27T07:28:52.174Z" + }, + { + "id": "b70c436e-70e5-48d3-84d2-ca2c784c7425", + "dataSourceId": "cc0b5feb-29ea-47c0-9a9a-62c0e0b89ccb", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.174Z", + "updatedAt": "2025-02-27T07:28:52.174Z" + }, + { + "id": "7bd1351e-61b9-4ba6-9e57-8eca1a3a0df3", + "dataSourceId": "cc0b5feb-29ea-47c0-9a9a-62c0e0b89ccb", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.174Z", + "updatedAt": "2025-02-27T07:28:52.174Z" + }, + { + "id": "f8268c18-8453-48ac-acf7-46c2d0c75c75", + "dataSourceId": "907dde15-1ac2-4f53-ba72-8e4e1d066f0e", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.181Z", + "updatedAt": "2025-02-27T07:28:52.181Z" + }, + { + "id": "cd59c697-6ac2-4594-b8f6-493676e8b3c7", + "dataSourceId": "907dde15-1ac2-4f53-ba72-8e4e1d066f0e", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.181Z", + "updatedAt": "2025-02-27T07:28:52.181Z" + }, + { + "id": "7270ef0a-f6d6-498e-9959-4b646a30d5d1", + "dataSourceId": "907dde15-1ac2-4f53-ba72-8e4e1d066f0e", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.181Z", + "updatedAt": "2025-02-27T07:28:52.181Z" + }, + { + "id": "fe4824d5-57fa-42dc-9812-4af634737898", + "dataSourceId": "66ad4e35-f981-47a6-99cd-31e9e7bbd9b3", + "environmentId": "4efb81aa-756a-4a8f-a017-e167f0720b85", + "options": null, + "createdAt": "2025-02-27T07:28:52.189Z", + "updatedAt": "2025-02-27T07:28:52.189Z" + }, + { + "id": "521a6d91-484b-45bc-af8f-aaceb0dc515c", + "dataSourceId": "66ad4e35-f981-47a6-99cd-31e9e7bbd9b3", + "environmentId": "ed48a4ab-ef5c-47c0-b99c-453fec90b9ec", + "options": null, + "createdAt": "2025-02-27T07:28:52.189Z", + "updatedAt": "2025-02-27T07:28:52.189Z" + }, + { + "id": "f5955bf2-c148-4544-acd9-a9a92a943e5b", + "dataSourceId": "66ad4e35-f981-47a6-99cd-31e9e7bbd9b3", + "environmentId": "eb006618-493c-48ac-8a4d-e80d208d29de", + "options": null, + "createdAt": "2025-02-27T07:28:52.189Z", + "updatedAt": "2025-02-27T07:28:52.189Z" + } + ], + "schemaDetails": { + "multiPages": true, + "multiEnv": true, + "globalDataSources": true + } + } + } + } + ], + "tooljet_version": "3.5.3-ee-lts" +} \ No newline at end of file diff --git a/cypress-tests/cypress/fixtures/templates/invalid_app.json b/cypress-tests/cypress/fixtures/templates/invalid_app.json index 09307af471..edb4c882a3 100644 --- a/cypress-tests/cypress/fixtures/templates/invalid_app.json +++ b/cypress-tests/cypress/fixtures/templates/invalid_app.json @@ -2127,7 +2127,7 @@ "encrypted": false }, "host": { - "value": "35.202.183.199", + "value": "9.234.17.31", "encrypted": false }, "port": { diff --git a/cypress-tests/cypress/fixtures/templates/one_version.json b/cypress-tests/cypress/fixtures/templates/one_version.json index d5dda12b3a..a93a35900e 100644 --- a/cypress-tests/cypress/fixtures/templates/one_version.json +++ b/cypress-tests/cypress/fixtures/templates/one_version.json @@ -585,7 +585,7 @@ "encrypted": false }, "host": { - "value": "35.202.183.199", + "value": "9.234.17.31", "encrypted": false }, "port": { diff --git a/cypress-tests/cypress/fixtures/templates/three-versions.json b/cypress-tests/cypress/fixtures/templates/three-versions.json index e6db26b19b..715f5e68cc 100644 --- a/cypress-tests/cypress/fixtures/templates/three-versions.json +++ b/cypress-tests/cypress/fixtures/templates/three-versions.json @@ -1701,7 +1701,7 @@ ] }, "list_rows": {}, - "runOnPageLoad": true + "runOnPageLoad": false }, "dataSourceId": "f4cf0089-aec2-4713-800e-3560e678220b", "appVersionId": "b74fcff1-8cf1-40f8-a13d-c2d2a0b1ebf1", @@ -1862,7 +1862,7 @@ "encrypted": false }, "host": { - "value": "35.202.183.199", + "value": "9.234.17.31", "encrypted": false }, "port": { diff --git a/cypress-tests/cypress/fixtures/templates/workspace_constants.json b/cypress-tests/cypress/fixtures/templates/workspace_constants.json index c5c015e832..074e6e408b 100644 --- a/cypress-tests/cypress/fixtures/templates/workspace_constants.json +++ b/cypress-tests/cypress/fixtures/templates/workspace_constants.json @@ -2766,7 +2766,7 @@ "name": "restapiStaticUrlG", "options": { "method": "get", - "url": "http://34.66.166.236:4000/{{constants.gconstEndpoint}}", + "url": "http://20.29.40.108:4000/{{constants.gconstEndpoint}}", "url_params": [ [ "", @@ -2814,7 +2814,7 @@ "name": "restapiUrlS", "options": { "method": "get", - "url": "http://34.66.166.236:4000/{{secrets.sconstEndpoint}}", + "url": "http://20.29.40.108:4000/{{secrets.sconstEndpoint}}", "url_params": [ [ "", @@ -2908,7 +2908,7 @@ "name": "restapiUrlGS", "options": { "method": "get", - "url": "http://34.66.166.{{constants.gconst}}{{secrets.sconst}}/production", + "url": "http://20.29.40.{{constants.gconst}}{{secrets.sconst}}/production", "url_params": [ [ "", @@ -3419,7 +3419,7 @@ "environmentId": "dab04b8d-7d1a-468a-b219-b2e1d0169d8c", "options": { "url": { - "value": "http://34.66.166.236:4000/{{constants.gconstEndpoint}}", + "value": "http://20.29.40.108:4000/{{constants.gconstEndpoint}}", "encrypted": false }, "auth_type": { @@ -3540,7 +3540,7 @@ "environmentId": "dab04b8d-7d1a-468a-b219-b2e1d0169d8c", "options": { "url": { - "value": "http://34.66.166.236:4000/{{secrets.sconstEndpoint}}", + "value": "http://20.29.40.108:4000/{{secrets.sconstEndpoint}}", "encrypted": false }, "auth_type": { @@ -3782,7 +3782,7 @@ "environmentId": "dab04b8d-7d1a-468a-b219-b2e1d0169d8c", "options": { "url": { - "value": "http://34.66.166.{{constants.gconst}}{{secrets.sconst}}/production", + "value": "http://20.29.40.{{constants.gconst}}{{secrets.sconst}}/production", "encrypted": false }, "auth_type": { diff --git a/cypress-tests/cypress/support/utils/api.js b/cypress-tests/cypress/support/utils/api.js new file mode 100644 index 0000000000..1ea37104f8 --- /dev/null +++ b/cypress-tests/cypress/support/utils/api.js @@ -0,0 +1,72 @@ +import { groupsSelector } from "Selectors/manageGroups"; +import { navigateToManageGroups } from 'Support/utils/common'; +export const apiRequest = (method, url, body = {}, headers = {}) => { + return cy.request({ + method, + url, + body, + headers: { + Authorization: Cypress.env('AUTH_TOKEN'), + "Content-Type": "application/json", + ...headers, + }, + failOnStatusCode: false + }); +}; + +export const createUser = (userData) => { + return apiRequest("POST", `${Cypress.env('API_URL')}/ext/users`, userData); +}; + +export const getUser = (userId) => { + return apiRequest("GET", `${Cypress.env('API_URL')}/ext/user/${userId}`); +}; + +export const getAllUsers = () => { + return apiRequest("GET", `${Cypress.env('API_URL')}/ext/users`); +}; + +export const updateUser = (userId, userData) => { + return apiRequest("PATCH", `${Cypress.env('API_URL')}/ext/user/${userId}`, userData); +}; +export const updateUserRole = (workspaceId, userData) => { + return apiRequest("PUT", `${Cypress.env('API_URL')}/ext/update-user-role/workspace/${workspaceId}`, userData); +} + +export const replaceUserWorkspace = (userId, workspaceId, userData) => { + return apiRequest("PATCH", `${Cypress.env('API_URL')}/ext/user/${userId}/workspace/${workspaceId}`, userData); +} + +export const replaceUserWorkspacesRelations = (userId, userData) => { + return apiRequest("PUT", `${Cypress.env('API_URL')}/ext/user/${userId}/workspaces`, userData); +} + +export const getAllWorkspaces = () => { + return apiRequest("GET", `${Cypress.env('API_URL')}/ext/workspaces`); +} + +export const importApp = (workspaceId, appData, headers) => { + return apiRequest("POST", `${Cypress.env('API_URL')}/ext/import/workspace/${workspaceId}/apps`, appData, headers); +} + +export const exportApp = (workspaceId, appId, endpoint, headers) => { + return apiRequest("POST", `${Cypress.env('API_URL')}/ext/export/workspace/${workspaceId}/apps/${appId}${endpoint}`, headers); +} + +export const allAppsDetails = (workspaceIds) => { + return apiRequest("GET", `${Cypress.env('API_URL')}/ext/workspace/${workspaceIds}/apps`); +} + +export const createGroup = (groupName) => { + cy.get(groupsSelector.createNewGroupButton).click(); + cy.clearAndType(groupsSelector.groupNameInput, groupName); + cy.get(groupsSelector.createGroupButton).click(); +} +export const validateUserInGroup = (email, workspaceSlug, groupName, shouldExist = true) => { + if (workspaceSlug) cy.visit(workspaceSlug); + navigateToManageGroups(); + cy.get(groupsSelector.groupLink(groupName)).click(); + cy.get(groupsSelector.usersLink).click(); + const userRow = `[data-cy="${email}-user-row"]`; + cy.get(userRow).should(shouldExist ? "exist" : "not.exist"); +}; \ No newline at end of file diff --git a/cypress-tests/cypress/support/utils/apps.js b/cypress-tests/cypress/support/utils/apps.js index 814c12cc25..4041c3ce46 100644 --- a/cypress-tests/cypress/support/utils/apps.js +++ b/cypress-tests/cypress/support/utils/apps.js @@ -1,109 +1,132 @@ import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { appPromote } from "Support/utils/platform/multiEnv"; const slugValidations = [ - { input: "", error: "App slug can't be empty" }, - { input: "_2#", error: "Special characters are not accepted." }, - { input: "t ", error: "Cannot contain spaces" }, - { input: "T", error: "Only lowercase letters are accepted." }, + { input: "", error: "App slug can't be empty" }, + { input: "_2#", error: "Special characters are not accepted." }, + { input: "t ", error: "Cannot contain spaces" }, + { input: "T", error: "Only lowercase letters are accepted." }, ]; export const verifySlugValidations = (inputSelector) => { - slugValidations.forEach(({ input, error }) => { - cy.get(inputSelector).clear(); - if (input) cy.clearAndType(inputSelector, input); - cy.wait(500); - cy.get(commonWidgetSelector.appSlugErrorLabel).verifyVisibleElement( - "have.text", - error - ); - }); + slugValidations.forEach(({ input, error }) => { + cy.get(inputSelector).clear(); + if (input) cy.clearAndType(inputSelector, input); + cy.wait(500); + cy.get(commonWidgetSelector.appSlugErrorLabel).verifyVisibleElement( + "have.text", + error + ); + }); }; export const verifySuccessfulSlugUpdate = (workspaceId, slug) => { - const host = resolveHost(); - cy.get('[data-cy="app-slug-accepted-label"]').verifyVisibleElement( - "have.text", - "Slug accepted!" - ); - cy.get(commonWidgetSelector.appLinkSucessLabel).verifyVisibleElement( - "have.text", - "Link updated successfully!" - ); - cy.get(commonWidgetSelector.appLinkField).verifyVisibleElement( - "have.text", - `${host}/${workspaceId}/apps/${slug}` - ); + const host = resolveHost(); + cy.get('[data-cy="app-slug-accepted-label"]').verifyVisibleElement( + "have.text", + "Slug accepted!" + ); + + cy.wait(500); + // cy.get(commonWidgetSelector.appLinkSucessLabel).should('be.visible'); + cy.get(commonWidgetSelector.appLinkSucessLabel).should( + "have.text", + "Link updated successfully!" + ); + cy.get(commonWidgetSelector.appLinkField).verifyVisibleElement( + "have.text", + `${host}/${workspaceId}/apps/${slug}` + ); }; export const verifyURLs = (workspaceId, slug, page) => { - const baseUrl = Cypress.config("baseUrl"); + const baseUrl = Cypress.config("baseUrl"); - cy.url().should( - "eq", - page - ? `${baseUrl}/${workspaceId}/apps/${slug}/home` - : `${baseUrl}/${workspaceId}/apps/${slug}` - ); + cy.url().should( + "eq", + page + ? `${baseUrl}/${workspaceId}/apps/${slug}/home` + : `${baseUrl}/${workspaceId}/apps/${slug}` + ); - cy.openInCurrentTab(commonWidgetSelector.previewButton); + cy.openInCurrentTab(commonWidgetSelector.previewButton); + cy.ifEnv("Community", () => { cy.url().should("eq", `${baseUrl}/applications/${slug}/home?version=v1`); + }); + cy.ifEnv("Enterprise", () => { + cy.url().should( + "eq", + `${baseUrl}/applications/${slug}/home?env=production&version=v1` + ); + }); - cy.visit("/my-workspace"); - cy.visitSlug({ - actualUrl: `${baseUrl}/applications/${slug}`, - }); - cy.url().should("eq", `${baseUrl}/applications/${slug}`); + cy.visit("/my-workspace"); + cy.visitSlug({ + actualUrl: `${baseUrl}/applications/${slug}`, + }); + cy.url().should("eq", `${baseUrl}/applications/${slug}`); }; export const setUpSlug = (slug) => { - cy.get(commonWidgetSelector.shareAppButton).click(); - cy.clearAndType(commonWidgetSelector.appNameSlugInput, slug); - cy.get('[data-cy="app-slug-accepted-label"]') - .should("be.visible") - .and("have.text", "Slug accepted!"); - cy.get(commonWidgetSelector.modalCloseButton).click(); + cy.get(commonWidgetSelector.shareAppButton).click(); + cy.clearAndType(commonWidgetSelector.appNameSlugInput, slug); + cy.get('[data-cy="app-slug-accepted-label"]') + .should("be.visible") + .and("have.text", "Slug accepted!"); + cy.get(commonWidgetSelector.modalCloseButton).click(); }; export const setupAppWithSlug = (appName, slug) => { - cy.apiCreateApp(appName); - cy.apiAddComponentToApp(appName, "text1"); - cy.apiReleaseApp(appName); - cy.apiAddAppSlug(appName, slug); + cy.apiCreateApp(appName); + cy.apiAddComponentToApp(appName, "text1"); + + cy.ifEnv("Enterprise", () => { + cy.openApp( + "", + Cypress.env("workspaceId"), + Cypress.env("appId"), + commonWidgetSelector.draggableWidget("text1") + ); + appPromote("development", "production"); + }); + + cy.apiReleaseApp(appName); + cy.apiAddAppSlug(appName, slug); }; export const verifyRestrictedAccess = () => { - cy.get('[data-cy="modal-header"]').should("have.text", "Restricted access"); - cy.get('[data-cy="modal-description"]') - .invoke("text") - .then((text) => { - const normalizedText = text.replace(/’/g, "'"); - expect(normalizedText).to.equal( - "You don't have access to this app. Kindly contact admin to know more." - ); - }); - cy.get('[data-cy="back-to-home-button"]').verifyVisibleElement( - "have.text", - "Back to home page" - ); + cy.get('[data-cy="modal-header"]').should("have.text", "Restricted access"); + cy.get('[data-cy="modal-description"]') + .invoke("text") + .then((text) => { + const normalizedText = text.replace(/’/g, "'"); + expect(normalizedText).to.equal( + "You don't have access to this app. Kindly contact admin to know more." + ); + }); + cy.get('[data-cy="back-to-home-button"]').verifyVisibleElement( + "have.text", + "Back to home page" + ); }; export const onboardUserFromAppLink = ( - email, - slug, - workspaceName = "My workspace", - isNonExistingUser = true + email, + slug, + workspaceName = "My workspace", + isNonExistingUser = true ) => { - const dbConfig = Cypress.env("app_db"); + const dbConfig = Cypress.env("app_db"); - const query = isNonExistingUser - ? ` + const query = isNonExistingUser + ? ` SELECT u.invitation_token, o.id AS workspace_id, ou.invitation_token AS organization_token FROM users u JOIN organization_users ou ON u.id = ou.user_id JOIN organizations o ON ou.organization_id = o.id WHERE u.email = '${email}' AND o.name = '${workspaceName}'; ` - : ` + : ` SELECT ou.invitation_token, o.id AS workspace_id FROM users u JOIN organization_users ou ON u.id = ou.user_id @@ -111,36 +134,33 @@ export const onboardUserFromAppLink = ( WHERE u.email = '${email}' AND o.name = '${workspaceName}'; `; - return cy - .task("updateId", { dbconfig: dbConfig, sql: query }) - .then((resp) => { - if (!resp.rows || resp.rows.length === 0) { - throw new Error( - `No records found for email: ${email} and workspace: ${workspaceName}` - ); - } + cy.task("dbConnection", { dbconfig: dbConfig, sql: query }).then((resp) => { + if (!resp.rows || resp.rows.length === 0) { + throw new Error( + `No records found for email: ${email} and workspace: ${workspaceName}` + ); + } - const { invitation_token, workspace_id, organization_token } = - resp.rows[0]; - const token = isNonExistingUser ? organization_token : invitation_token; - const url = isNonExistingUser - ? `${Cypress.config("baseUrl")}/invitations/${invitation_token}/workspaces/${organization_token}?oid=${workspace_id}&redirectTo=%2Fapplications%2F${slug}` - : `${Cypress.config("baseUrl")}/organization-invitations/${token}?oid=${workspace_id}&redirectTo=%2Fapplications%2F${slug}`; + const { invitation_token, workspace_id, organization_token } = resp.rows[0]; + const token = isNonExistingUser ? organization_token : invitation_token; + const url = isNonExistingUser + ? `${Cypress.config("baseUrl")}/invitations/${invitation_token}/workspaces/${organization_token}?oid=${workspace_id}&redirectTo=%2Fapplications%2F${slug}` + : `${Cypress.config("baseUrl")}/organization-invitations/${token}?oid=${workspace_id}&redirectTo=%2Fapplications%2F${slug}`; - cy.visit(url); - }); + cy.visit(url); + }); }; export const resolveHost = () => { - const baseUrl = Cypress.config("baseUrl"); + const baseUrl = Cypress.config("baseUrl"); - const urlMapping = { - "http://localhost:8082": "http://localhost:8082", - "http://localhost:3000/apps": "http://localhost:3000/apps", - "http://localhost:4001": "http://localhost:3000", - "http://localhost:4001/apps": "http://localhost:3000/apps" - }; + const urlMapping = { + "http://localhost:8082": "http://localhost:8082", + "http://localhost:3000": "http://localhost:3000", + "http://localhost:3000/apps": "http://localhost:3000/apps", + "http://localhost:4001": "http://localhost:3000", + "http://localhost:4001/apps": "http://localhost:3000/apps", + }; - return urlMapping[baseUrl]; + return urlMapping[baseUrl]; }; - diff --git a/cypress-tests/cypress/support/utils/basicComponents.js b/cypress-tests/cypress/support/utils/basicComponents.js index e92c37d173..9577dee022 100644 --- a/cypress-tests/cypress/support/utils/basicComponents.js +++ b/cypress-tests/cypress/support/utils/basicComponents.js @@ -14,9 +14,21 @@ export const verifyComponent = (widgetName) => { }; export const verifyComponentinrightpannel = (widgetName) => { - cy.get(commonWidgetSelector.widgetBox(widgetName), { - timeout: 10000, - }).should("be.visible"); + cy.get("body") + .then(($body) => { + const isSearchVisible = $body + .find(commonSelectors.searchField) + .is(":visible"); + + if (!isSearchVisible) { + cy.get('[data-cy="right-sidebar-plus-button"]').click(); + } + }) + .then(() => { + cy.get(commonWidgetSelector.widgetBox(widgetName), { + timeout: 10000, + }).should("be.visible"); + }); }; export const deleteComponentAndVerify = (widgetName) => { @@ -32,10 +44,10 @@ export const deleteComponentAndVerify = (widgetName) => { .last() .realClick(); }); - cy.verifyToastMessage( - `[class=go3958317564]`, - "Component deleted! (Ctrl + Z to undo)" - ); + // cy.verifyToastMessage( + // `[class=go3958317564]`, + // "Component deleted! (Ctrl + Z to undo)" + // ); cy.notVisible(commonWidgetSelector.draggableWidget(widgetName)); }; diff --git a/cypress-tests/cypress/support/utils/button.js b/cypress-tests/cypress/support/utils/button.js index ddace2f305..66e340965c 100644 --- a/cypress-tests/cypress/support/utils/button.js +++ b/cypress-tests/cypress/support/utils/button.js @@ -40,7 +40,7 @@ export const verifyControlComponentAction = (widgetName, value) => { export const addBasicData = (data) => { openEditorSidebar(buttonText.defaultWidgetName); - verifyAndModifyParameter(buttonText.buttonTextLabel, data.widgetName); + verifyAndModifyParameter('Label', data.widgetName); openAccordion(commonWidgetText.accordionEvents); addDefaultEventHandler(data.alertMessage); diff --git a/cypress-tests/cypress/support/utils/common.js b/cypress-tests/cypress/support/utils/common.js index eaf5bdb7bc..63f188db61 100644 --- a/cypress-tests/cypress/support/utils/common.js +++ b/cypress-tests/cypress/support/utils/common.js @@ -6,6 +6,7 @@ import moment from "moment"; import { dashboardSelector } from "Selectors/dashboard"; import { groupsSelector } from "Selectors/manageGroups"; import { groupsText } from "Texts/manageGroups"; +import { appPromote } from "Support/utils/platform/multiEnv"; export const navigateToProfile = () => { cy.get(commonSelectors.settingsIcon).click(); @@ -16,9 +17,7 @@ export const navigateToProfile = () => { export const logout = () => { cy.get(commonSelectors.settingsIcon).click(); cy.get(commonSelectors.logoutLink).click(); - cy.intercept("GET", "/api/metadata").as("publicConfig"); - cy.wait("@publicConfig"); - cy.wait(500); + cy.wait(1000); }; export const navigateToManageUsers = () => { @@ -50,7 +49,7 @@ export const randomDateOrTime = (format = "DD/MM/YYYY") => { let startDate = new Date(2018, 0, 1); startDate = new Date( startDate.getTime() + - Math.random() * (endDate.getTime() - startDate.getTime()) + Math.random() * (endDate.getTime() - startDate.getTime()) ); return moment(startDate).format(format); }; @@ -91,7 +90,7 @@ export const navigateToAppEditor = (appName) => { .find(commonSelectors.editButton) .click({ force: true }); if (Cypress.env("environment") === "Community") { - cy.intercept("GET", "/api/v2/data_sources").as("appDs"); + cy.intercept("GET", "/api/data-sources").as("appDs"); cy.wait("@appDs", { timeout: 15000 }); cy.skipEditorPopover(); } else { @@ -103,11 +102,14 @@ export const navigateToAppEditor = (appName) => { export const viewAppCardOptions = (appName) => { cy.wait(1000); - cy.reloadAppForTheElement(appName); + cy.get(commonSelectors.appCard(appName)) + .realHover() + .find(commonSelectors.appCardOptionsButton) + .realHover(); cy.contains("div", appName) .parent() .within(() => { - cy.get(commonSelectors.appCardOptionsButton).invoke("click"); + cy.get(commonSelectors.appCardOptionsButton).click(); }); }; @@ -183,13 +185,13 @@ export const manageUsersPagination = (email) => { export const searchUser = (email) => { cy.clearAndType(commonSelectors.inputUserSearch, email); - cy.wait(1000) + cy.wait(1000); }; - export const selectAppCardOption = (appName, appCardOption) => { + cy.wait(1000); viewAppCardOptions(appName); - cy.get(appCardOption).should("be.visible").click({ force: true }); + cy.get(appCardOption).should("be.visible").click(); }; export const navigateToDatabase = () => { @@ -221,7 +223,6 @@ export const pinInspector = () => { } }); cy.hideTooltip(); - }; export const navigateToworkspaceConstants = () => { @@ -230,6 +231,9 @@ export const navigateToworkspaceConstants = () => { }; export const releaseApp = () => { + cy.ifEnv("Enterprise", () => { + appPromote("development", "production"); + }); cy.get(commonSelectors.releaseButton).click(); cy.get(commonSelectors.yesButton).click(); cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released"); @@ -243,24 +247,3 @@ export const verifyTooltipDisabled = (selector, message) => { cy.get(".tooltip-inner").last().should("have.text", message); }); }; - -export const deleteAllGroupChips = () => { - cy.get('body').then(($body) => { - if ($body.find('[data-cy="group-chip"]').length > 0) { - cy.get('[data-cy="group-chip"]').then(($groupChip) => { - if ($groupChip.is(':visible')) { - cy.get('[data-cy="group-chip"]').first().click(); - cy.get('[data-cy="delete-button"]').click(); - cy.get('[data-cy="yes-button"]').click(); - - cy.wait(2000); - deleteAllGroupChips(); // Recursive call to delete next chip - } else { - cy.log("Group chip is present but not visible, skipping deletion"); - } - }); - } else { - cy.log("No group chips left to delete"); - } - }); -} \ No newline at end of file diff --git a/cypress-tests/cypress/support/utils/commonWidget.js b/cypress-tests/cypress/support/utils/commonWidget.js index c48cd18917..b34ff0b48e 100644 --- a/cypress-tests/cypress/support/utils/commonWidget.js +++ b/cypress-tests/cypress/support/utils/commonWidget.js @@ -31,8 +31,11 @@ export const verifyAndModifyParameter = (paramName, value) => { export const openEditorSidebar = (widgetName = "") => { cy.hideTooltip(); - cy.get(`${commonWidgetSelector.draggableWidget(widgetName)}:eq(0)`).realHover() - cy.get(commonWidgetSelector.widgetConfigHandle(widgetName)).click(); + + cy.get(`${commonWidgetSelector.draggableWidget(widgetName)}:eq(0)`).realHover().then(() => { + cy.wait(1000); + cy.get(commonWidgetSelector.widgetConfigHandle(widgetName)).click(); + }) }; export const verifyAndModifyToggleFx = ( diff --git a/cypress-tests/cypress/support/utils/dashboard.js b/cypress-tests/cypress/support/utils/dashboard.js index 0809909297..dbc090735b 100644 --- a/cypress-tests/cypress/support/utils/dashboard.js +++ b/cypress-tests/cypress/support/utils/dashboard.js @@ -53,6 +53,8 @@ export const modifyAndVerifyAppCardIcon = (appName) => { }; export const verifyAppDelete = (appName) => { + cy.get("body").should("exist").and("be.visible"); + cy.get('[data-cy="dashboard-section-header"]').should("be.visible"); cy.get("body").then(($title) => { if (!$title.text().includes(commonText.introductionMessage)) { cy.clearAndType(commonSelectors.homePageSearchBar, appName); diff --git a/cypress-tests/cypress/support/utils/dataSource.js b/cypress-tests/cypress/support/utils/dataSource.js index 348f563ee5..e480fce14d 100644 --- a/cypress-tests/cypress/support/utils/dataSource.js +++ b/cypress-tests/cypress/support/utils/dataSource.js @@ -6,6 +6,7 @@ import { commonText } from "Texts/common"; import { dataSourceSelector } from "Selectors/dataSource"; import { dataSourceText } from "Texts/dataSource"; import { navigateToAppEditor } from "Support/utils/common"; +import { verifyAppDelete } from "Support/utils/dashboard"; export const verifyCouldnotConnectWithAlert = (dangerText) => { cy.get(postgreSqlSelector.connectionFailedText, { @@ -24,7 +25,7 @@ export const query = (operation) => { }; export const verifypreview = (type, data) => { - cy.get(`[data-cy="preview-tab-${type}"]`).click(); + cy.get(`[data-cy="preview-tab-${type}"]`, { timeout: 15000 }).click(); cy.get(`[data-cy="preview-${type}-data-container"]`).verifyVisibleElement( "contain.text", data, @@ -60,6 +61,15 @@ export const deleteDatasource = (datasourceName) => { // " Databases" // ); }; +export const deleteAppandDatasourceAfterExecution = ( + appName, + datasourceName +) => { + cy.backToApps(); + cy.deleteApp(appName); + verifyAppDelete(appName); + deleteDatasource(datasourceName); +}; export const closeDSModal = () => { cy.get("body").then(($body) => { @@ -79,7 +89,7 @@ export const addQueryN = (queryName, query, dbName) => { cy.clearAndType('[data-cy="gds-querymanager-search-bar"]', `${dbName}`); } }); - cy.intercept("POST", "/api/data_queries").as("createQuery"); + cy.intercept("POST", "/api/data-queries/**").as("createQuery"); cy.get(`[data-cy="${dbName}-add-query-card"] > .text-truncate`).click(); cy.get('[data-cy="query-rename-input"]').clear().type(queryName); @@ -96,9 +106,7 @@ export const addQueryN = (queryName, query, dbName) => { export const addQuery = (queryName, query, dbName) => { cy.get('[data-cy="show-ds-popover-button"]').click(); cy.get(".css-4e90k9").type(`${dbName}`); - cy.intercept("POST", "/api/data_queries").as( - "createQuery" - ); + cy.intercept("POST", "/api/data-queries/**").as("createQuery"); cy.contains(`[id*="react-select-"]`, dbName).click(); cy.get('[data-cy="query-rename-input"]').clear().type(queryName); @@ -132,7 +140,7 @@ export const addQueryAndOpenEditor = (queryName, query, dbName, appName) => { cy.get('[data-cy="show-ds-popover-button"]').click(); cy.get(".css-4e90k9").type(`${dbName}`); cy.get(".css-4e90k9").type(`${dbName}`); - cy.intercept("POST", "/api/data_queries").as("createQuery"); + cy.intercept("POST", "/api/data-queries").as("createQuery"); cy.contains(`[id*="react-select-"]`, dbName).click(); cy.get('[data-cy="query-rename-input"]').clear().type(queryName); @@ -177,13 +185,13 @@ export const selectDatasource = (datasourceName) => { export const createDataQuery = (appName, url, key, value) => { let appId, versionId; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select id from apps where name='${appName}';`, }).then((resp) => { appId = resp.rows[0].id; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select id from app_versions where app_id='${appId}';`, }).then((resp) => { @@ -197,7 +205,7 @@ export const createDataQuery = (appName, url, key, value) => { cy.request({ method: "POST", - url: `${Cypress.env("server_host")}/api/data_queries`, + url: `${Cypress.env("server_host")}/api/data-queries`, headers: headers, body: { app_id: appId, @@ -225,28 +233,35 @@ export const createDataQuery = (appName, url, key, value) => { }); }; -export const createRestAPIQuery = (queryName, dsName, key = '', value = '', url = "", run = true) => { +export const createRestAPIQuery = ( + queryName, + dsName, + key = "", + value = "", + url = "", + run = true, + kind = "restapi" +) => { cy.getCookie("tj_auth_token").then((cookie) => { const headers = { "Tj-Workspace-Id": Cypress.env("workspaceId"), Cookie: `tj_auth_token=${cookie.value}`, }; - cy.log(Cypress.env("appId")); cy.request({ method: "GET", - url: `${Cypress.env("server_host")}/api/v2/apps/${Cypress.env("appId")}`, + url: `${Cypress.env("server_host")}/api/apps/${Cypress.env("appId")}`, headers: headers, }).then((response) => { const editingVersionId = response.body.editing_version.id; - const data_source_id = Cypress.env(`${dsName}-id`); + const data_source_id = Cypress.env(`${dsName}`); const requestBody = { app_id: Cypress.env("appId"), app_version_id: editingVersionId, name: queryName, - kind: "restapi", + kind: kind, options: { method: "get", url: url, @@ -265,7 +280,7 @@ export const createRestAPIQuery = (queryName, dsName, key = '', value = '', url cy.request({ method: "POST", - url: `${Cypress.env("server_host")}/api/data_queries`, + url: `${Cypress.env("server_host")}/api/data-queries/data-sources/${data_source_id}/versions/${editingVersionId}`, headers: headers, body: requestBody, }).then((response) => { diff --git a/cypress-tests/cypress/support/utils/inspector.js b/cypress-tests/cypress/support/utils/inspector.js index 295787d507..67f9896fa9 100644 --- a/cypress-tests/cypress/support/utils/inspector.js +++ b/cypress-tests/cypress/support/utils/inspector.js @@ -1,60 +1,49 @@ -export const verifyNodeData = (node, type, children, index = 0) => { - cy.get( - `[data-cy="inspector-node-${node.toLowerCase()}"] > .node-length-color` - ) - .eq(index) - .realHover() - .verifyVisibleElement("have.text", `${children}`); - cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .node-key`) - .eq(index) - .verifyVisibleElement("have.text", node); - cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .node-type`) - .eq(index) - .verifyVisibleElement("have.text", type); -}; +import { commonWidgetSelector } from "Selectors/common"; -export const openNode = (node, index = 0, time = 1000) => { - cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .node-key`) - .eq(index) - .click(); - cy.wait(time); -}; - -export const verifyValue = (node, type, children, index = 0) => { - cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .mx-2`) - .eq(index) - .realHover() - .verifyVisibleElement("contain.text", `${children}`); - cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .node-key`) - .eq(index) - .verifyVisibleElement("contain.text", node); - cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .mx-1`) - .eq(index) - .verifyVisibleElement("contain.text", type); -}; -export const deleteComponentFromInspector = (node) => { - cy.get('[data-cy="inspector-node-components"] > .node-key').click(); - cy.get(`[data-cy="inspector-node-${node}"] > .node-key`).realHover().parent().find('[style="height: 13px; width: 13px;"] > img').click(); -}; - -export const verifyfunctions = (node, type, index = 0) => { - cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .fs-10`) - .eq(index) - .realHover() - .verifyVisibleElement("contain.text", `${type}`); - cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .node-key`) - .eq(index) - .verifyVisibleElement("contain.text", node); - // cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .mx-1`) - // .eq(index) - // .verifyVisibleElement("contain.text", type); +export const openAndVerifyNode = (nodeName, nodes, verificationFunction) => { + openStateFromComponent(nodeName); + verifyNodes(nodes, verificationFunction); }; export const verifyNodes = (nodes, verificationFunction) => { nodes.forEach(node => verificationFunction(node.key, node.type, node.value)); }; -export const openAndVerifyNode = (nodeName, nodes, verificationFunction) => { - openNode(nodeName); - verifyNodes(nodes, verificationFunction); +export const openNode = (node, index = 0, time = 1000) => { + cy.get(`[data-cy="inspector-${node.toLowerCase()}-expand-button"]`, { timeout: time }) + .eq(index) + .click(); +}; + +export const openStateFromComponent = (widgetName) => { + cy.get(commonWidgetSelector.draggableWidget(widgetName)) + .realHover() + .realHover(); + + cy.get(commonWidgetSelector.draggableWidget(widgetName)) + .realHover() + .then(() => { + cy.get(`[data-cy="${widgetName}-inspect-button"]`) + .realHover({ position: "topRight" }) + .last() + .realClick(); + }); +} + +export const verifyNodeData = (node, type, value, index = 0) => { + cy.get( + `[data-cy="inspector-${node.toLowerCase()}-label"]` + ) + .eq(index) + .realHover() + .verifyVisibleElement("have.text", `${node}`); + + cy.get(`[data-cy="inspector-${node.toLowerCase()}-value"]`) + .eq(index) + .verifyVisibleElement("have.text", type == 'Function' ? 'function' : value); +}; + +export const deleteComponentFromInspector = (node) => { + cy.get('[data-cy="inspector-menu-icon"]').click(); + cy.get(`[data-cy="inspector-delete-component-action"`).realHover().parent().find('[style="height: 13px; width: 13px;"] > img').last().click(); }; \ No newline at end of file diff --git a/cypress-tests/cypress/support/utils/manageGroups.js b/cypress-tests/cypress/support/utils/manageGroups.js index 2c114dfad8..2ddd80379d 100644 --- a/cypress-tests/cypress/support/utils/manageGroups.js +++ b/cypress-tests/cypress/support/utils/manageGroups.js @@ -5,7 +5,7 @@ import { navigateToManageGroups } from "Support/utils/common"; import { cyParamName } from "Selectors/common"; import { fake } from "Fixtures/fake"; import { onboardingSelectors } from "Selectors/onboarding"; -import { fetchAndVisitInviteLink } from "Support/utils/onboarding"; +import { fetchAndVisitInviteLink } from "Support/utils/manageUsers"; import { usersSelector } from "Selectors/manageUsers"; import { fillUserInviteForm } from "Support/utils/manageUsers"; import { navigateToManageUsers, logout } from "Support/utils/common"; @@ -335,7 +335,7 @@ export const manageGroupsElements = () => { ); cy.verifyElement(groupsSelector.confimButton, groupsText.updateButtonText); - cy.get(groupsSelector.confimButton).should("be.enabled"); + cy.get(groupsSelector.confimButton).should("be.disabled"); cy.verifyElement(groupsSelector.cancelButton, groupsText.cancelButton); cy.get(groupsSelector.cancelButton).click(); @@ -542,7 +542,7 @@ export const manageGroupsElements = () => { ); cy.verifyElement(groupsSelector.confimButton, groupsText.updateButtonText); - cy.get(groupsSelector.confimButton).should("be.enabled"); + cy.get(groupsSelector.confimButton).should("be.disabled"); cy.verifyElement(groupsSelector.cancelButton, groupsText.cancelButton); cy.get(groupsSelector.cancelButton).click(); //Add Modal @@ -646,7 +646,7 @@ export const createGroupAddAppAndUserToGroup = (groupName, email) => { cy.request({ method: "POST", - url: `${Cypress.env("server_host")}/api/v2/group_permissions`, + url: `${Cypress.env("server_host")}/api/v2/group-permissions`, headers: headers, body: { name: groupName, @@ -658,14 +658,14 @@ export const createGroupAddAppAndUserToGroup = (groupName, email) => { cy.request({ method: "POST", - url: `${Cypress.env("server_host")}/api/v2/group_permissions/granular-permissions`, + url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/granular-permissions/app`, headers: headers, body: { name: "Apps", type: "app", groupId: groupId, isAll: false, - createAppsPermissionsObject: { + createResourcePermissionObject: { canEdit: true, canView: false, hideFromDashboard: false, @@ -680,15 +680,17 @@ export const createGroupAddAppAndUserToGroup = (groupName, email) => { expect(response.status).to.equal(201); }); - cy.task("updateId", { + cy.wait(2000); + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select id from users where email='${email}';`, }).then((resp) => { const userId = resp.rows[0].id; + cy.log(userId); cy.request({ method: "POST", - url: `${Cypress.env("server_host")}/api/v2/group_permissions/group-user`, + url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/users`, headers: headers, body: { userIds: [userId], @@ -720,11 +722,11 @@ export const OpenGroupCardOption = (groupName) => { export const duplicateMultipleGroups = (groupNames) => { groupNames.forEach((groupName) => { OpenGroupCardOption(groupName); - cy.wait(3000); + cy.wait(2000); cy.get(commonSelectors.duplicateOption).click(); // Click on the duplicate option cy.get(commonSelectors.confirmDuplicateButton).click(); // Confirm duplication if needed }); -} +}; export const verifyGroupCardOptions = (groupName) => { cy.get(groupsSelector.groupLink(groupName)).click(); @@ -850,6 +852,9 @@ export const createGroupsAndAddUserInGroup = (groupName, email) => { commonSelectors.toastMessage, groupsText.groupCreatedToast ); + addUserInGroup(groupName, email); +}; +export const addUserInGroup = (groupName, email) => { cy.get(groupsSelector.groupLink(groupName)).click(); cy.clearAndType(groupsSelector.multiSelectSearchInput, email); cy.wait(2000); @@ -864,7 +869,7 @@ export const createGroupsAndAddUserInGroup = (groupName, email) => { export const inviteUserBasedOnRole = (firstName, email, role = "end-user") => { fillUserInviteForm(firstName, email); - cy.get(".css-1dyz3mf").type(`${role}{enter}`); + cy.get(".css-1mlj61j").type(`${role}{enter}`); cy.get(usersSelector.buttonInviteUsers).click(); cy.wait(500); @@ -885,7 +890,13 @@ export const verifyBasicPermissions = (canCreate = true) => { ); }; -export const setupWorkspaceAndInviteUser = (workspaceName, workspaceSlug, firstName, email, role = "end-user") => { +export const setupWorkspaceAndInviteUser = ( + workspaceName, + workspaceSlug, + firstName, + email, + role = "end-user" +) => { cy.apiCreateWorkspace(workspaceName, workspaceSlug); cy.visit(workspaceSlug); cy.wait(1000); @@ -901,7 +912,10 @@ export const verifySettingsAccess = (shouldExist = true) => { ); }; -export const verifyUserPrivileges = (expectedButtonState, shouldHaveWorkspaceSettings) => { +export const verifyUserPrivileges = ( + expectedButtonState, + shouldHaveWorkspaceSettings +) => { cy.get(commonSelectors.dashboardAppCreateButton).should(expectedButtonState); cy.get(commonSelectors.settingsIcon).click(); @@ -917,4 +931,4 @@ export const setupAndUpdateRole = (currentRole, endRole, email) => { updateRole(currentRole, endRole, email); cy.wait(1000); cy.apiLogout(); -}; \ No newline at end of file +}; diff --git a/cypress-tests/cypress/support/utils/manageSSO.js b/cypress-tests/cypress/support/utils/manageSSO.js index 4a1a41fecb..b8e64e4f56 100644 --- a/cypress-tests/cypress/support/utils/manageSSO.js +++ b/cypress-tests/cypress/support/utils/manageSSO.js @@ -18,7 +18,7 @@ export const generalSettings = () => { cy.get(ssoSelector.workspaceLoginPage.defaultSSO).click(); cy.get(ssoSelector.defaultGoogle).verifyVisibleElement("have.text", "Google"); - cy.get(ssoSelector.defaultGithub).verifyVisibleElement("have.text", "Github"); + cy.get(ssoSelector.defaultGithub).verifyVisibleElement("have.text", "Git"); cy.clearAndType(ssoSelector.allowedDomainInput, ssoText.allowedDomain); cy.get(ssoSelector.saveButton).click(); @@ -317,12 +317,12 @@ export const invitePageElements = () => { .and("equal", "https://www.tooljet.com/privacy"); }; -export const updateId = () => { - cy.task("updateId", { +export const dbConnection = () => { + cy.task("dbConnection", { dbconfig: Cypress.config("db"), sql: "update sso_configs set id='5edf41b2-ff2b-4932-9e2a-08aef4a303cc' where sso='google';", }); - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.config("db"), sql: "update sso_configs set id='9628dee2-6fa9-4aca-9c98-ef950601c83e' where sso='git';", }); @@ -331,18 +331,18 @@ export const updateId = () => { export const setSSOStatus = (workspaceName, ssoType, enabled) => { let workspaceId; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `SELECT id FROM organizations WHERE name = '${workspaceName}'`, }).then((resp) => { workspaceId = resp.rows[0].id; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `SELECT * FROM sso_configs WHERE organization_id = '${workspaceId}' AND sso = '${ssoType}'`, }).then((ssoConfigResp) => { if (ssoConfigResp.rows.length > 0) { - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `UPDATE sso_configs SET enabled = ${enabled ? "true" : "false" } WHERE organization_id = '${workspaceId}' AND sso = '${ssoType}'`, @@ -371,8 +371,8 @@ export const defaultSSO = (enable) => { }); }; -export const setSignupStatus = (enable, workspaceName = 'My workspace') => { - cy.task("updateId", { +export const setSignupStatus = (enable, workspaceName = "My workspace") => { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `SELECT id FROM organizations WHERE name = '${workspaceName}'`, }).then((resp) => { @@ -381,7 +381,7 @@ export const setSignupStatus = (enable, workspaceName = 'My workspace') => { cy.getCookie("tj_auth_token").then((cookie) => { cy.request({ method: "PATCH", - url: `${Cypress.env("server_host")}/api/organizations`, + url: `${Cypress.env("server_host")}/api/login-configs/organization-general`, headers: { "Tj-Workspace-Id": workspaceId, Cookie: `tj_auth_token=${cookie.value}`, @@ -396,13 +396,13 @@ export const setSignupStatus = (enable, workspaceName = 'My workspace') => { export const deleteOrganisationSSO = (workspaceName, services) => { let workspaceId; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select id from organizations where name='${workspaceName}';`, }).then((resp) => { workspaceId = resp.rows[0].id; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `DELETE FROM sso_configs WHERE organization_id = '${workspaceId}' AND sso IN (${services .map((service) => `'${service}'`) @@ -416,7 +416,7 @@ export const resetDomain = () => { cy.request( { method: "PATCH", - url: `${Cypress.env("server_host")}/api/organizations`, + url: `${Cypress.env("server_host")}/api/login-configs/organization-general`, headers: { "Tj-Workspace-Id": Cypress.env("workspaceId"), Cookie: `tj_auth_token=${cookie.value}`, @@ -429,3 +429,23 @@ export const resetDomain = () => { }); }); }; + +export const enableInstanceSignup = ( + allowPersonalWorkspace = true, + enableLoginConfig = true, + allowedDomains = "" +) => { + const allowValue = allowPersonalWorkspace ? "true" : "false"; + const loginConfigValue = enableLoginConfig ? "true" : "false"; + + const sql = ` + UPDATE instance_settings SET value = '${allowValue}' WHERE key = 'ALLOW_PERSONAL_WORKSPACE'; + UPDATE instance_settings SET value = '${loginConfigValue}' WHERE key = 'ENABLE_SIGNUP'; + UPDATE instance_settings SET value = '${allowedDomains}' WHERE key = 'ALLOWED_DOMAINS'; + `; + + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql, + }); +}; diff --git a/cypress-tests/cypress/support/utils/manageUsers.js b/cypress-tests/cypress/support/utils/manageUsers.js index 6bf47b8868..460b8d3dde 100644 --- a/cypress-tests/cypress/support/utils/manageUsers.js +++ b/cypress-tests/cypress/support/utils/manageUsers.js @@ -6,6 +6,7 @@ import { ssoText } from "Texts/manageSSO"; import * as common from "Support/utils/common"; import { commonText } from "Texts/common"; import { onboardingSelectors } from "Selectors/onboarding"; +const envVar = Cypress.env("environment"); export const manageUsersElements = () => { cy.get(commonSelectors.breadcrumbTitle).should(($el) => { @@ -15,7 +16,7 @@ export const manageUsersElements = () => { }); cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement( "have.text", - " Users" + "Users" ); for (const element in usersSelector.usersElements) { @@ -114,10 +115,17 @@ export const manageUsersElements = () => { ); cy.get(usersSelector.buttonUploadCsvFile).click(); - cy.get(usersSelector.helperTextBulkUpload).verifyVisibleElement( - "have.text", - usersText.helperTextBulkUpload - ); + if (envVar === "Enterprise") { + cy.get(usersSelector.helperTextBulkUpload).verifyVisibleElement( + "have.text", + "Download the template to add user details or format your file in the same way as the template. Files in any other format may not be recognized. " + ); + } else { + cy.get(usersSelector.helperTextBulkUpload).verifyVisibleElement( + "have.text", + usersText.helperTextBulkUpload + ); + } cy.get(usersSelector.buttonDownloadTemplate).verifyVisibleElement( "have.text", usersText.buttonDownloadTemplate @@ -320,44 +328,46 @@ export const inviteUserWithUserGroups = ( }; export const fetchAndVisitInviteLink = (email) => { - let invitationToken, - organizationToken, - workspaceId, - userId, - url = ""; + let invitationToken, organizationToken, workspaceId, userId; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select invitation_token from users where email='${email}';`, - }).then((resp) => { - invitationToken = resp.rows[0].invitation_token; + }) + .then((resp) => { + invitationToken = resp.rows[0]?.invitation_token; - cy.task("updateId", { - dbconfig: Cypress.env("app_db"), - sql: "select id from organizations where name='My workspace';", - }).then((resp) => { - workspaceId = resp.rows[0].id; + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: "select id from organizations where name='My workspace';", + }); + }) + .then((resp) => { + workspaceId = resp.rows[0]?.id; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select id from users where email='${email}';`, - }).then((resp) => { - userId = resp.rows[0].id; - - cy.task("updateId", { - dbconfig: Cypress.env("app_db"), - sql: `select invitation_token from organization_users where user_id='${userId}';`, - }).then((resp) => { - organizationToken = resp.rows[1].invitation_token; - - url = `/invitations/${invitationToken}/workspaces/${organizationToken}?oid=${workspaceId}`; - cy.apiLogout(); - cy.wait(1000); - cy.visit(url); - }); }); + }) + .then((resp) => { + userId = resp.rows[0]?.id; + + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `select invitation_token from organization_users where user_id='${userId}';`, + }); + }) + .then((resp) => { + organizationToken = + resp.rows?.[1]?.invitation_token || resp.rows?.[0]?.invitation_token; + + const url = `/invitations/${invitationToken}/workspaces/${organizationToken}?oid=${workspaceId}`; + + cy.apiLogout(); + cy.wait(1000); + cy.visit(url); }); - }); }; export const inviteUserWithUserRole = (firstName, email, role) => { @@ -389,4 +399,5 @@ export const inviteUserWithUserRole = (firstName, email, role) => { cy.get(commonSelectors.signUpButton).click(); cy.wait(2000); cy.get(commonSelectors.acceptInviteButton).click(); + cy.get(commonSelectors.homePageLogo, { timeout: 10000 }).should("be.visible"); }; diff --git a/cypress-tests/cypress/support/utils/mongoDB.js b/cypress-tests/cypress/support/utils/mongoDB.js index 0ca418db9d..61107f639e 100644 --- a/cypress-tests/cypress/support/utils/mongoDB.js +++ b/cypress-tests/cypress/support/utils/mongoDB.js @@ -10,7 +10,7 @@ export const connectMongo = () => { selectAndAddDataSource(mongoDbText.mongoDb); cy.clearAndType( - '[data-cy="data-source-name-input-filed"]', + '[data-cy="data-source-name-input-field"]', mongoDbText.cypressMongoDb ); diff --git a/cypress-tests/cypress/support/utils/multipage.js b/cypress-tests/cypress/support/utils/multipage.js index 8821054af0..57f236442e 100644 --- a/cypress-tests/cypress/support/utils/multipage.js +++ b/cypress-tests/cypress/support/utils/multipage.js @@ -3,7 +3,7 @@ import { commonSelectors } from "../../constants/selectors/common"; export const searchPage = (pageName) => { cy.get('[title="Search"]').click(); - cy.get('[data-cy="search-input-filed"]').type(pageName); + cy.get('[data-cy="search-input-field"]').type(pageName); }; export const clearSearch = () => { @@ -57,7 +57,7 @@ export const setHomePage = (pageName) => { export const addNewPage = (pageName) => { cy.get(multipageSelector.addPageIcon).click(); - cy.get(".col-12 > .form-control").type(`{selectAll}{backspace}${pageName}`); + cy.get('[role="button"] > div > .form-control').type(`{selectAll}{backspace}${pageName}`); cy.get(multipageSelector.addPageIcon).click(); cy.get(`[data-cy="pages-name-${pageName.toLowerCase()}"]`).click(); }; diff --git a/cypress-tests/cypress/support/utils/onboarding.js b/cypress-tests/cypress/support/utils/onboarding.js index 44ab148934..74d5c77d3d 100644 --- a/cypress-tests/cypress/support/utils/onboarding.js +++ b/cypress-tests/cypress/support/utils/onboarding.js @@ -9,6 +9,7 @@ import { navigateToManageUsers, logout } from "Support/utils/common"; import { ssoSelector } from "Selectors/manageSSO"; import { ssoText } from "Texts/manageSSO"; import { onboardingSelectors } from "Selectors/onboarding"; +import { fetchAndVisitInviteLink } from "Support/utils/manageUsers"; export const verifyConfirmEmailPage = (email) => { cy.get(commonSelectors.pageLogo).should("be.visible"); @@ -39,72 +40,11 @@ export const verifyConfirmEmailPage = (email) => { ); }; -export const verifyOnboardingQuestions = (fullName, workspaceName) => { - cy.wait(5000); - cy.get(commonSelectors.pageLogo).should("be.visible"); - cy.get(commonSelectors.userAccountNameAvatar).should("be.visible"); - cy.get(commonSelectors.createAccountCheckMark).should("be.visible"); - cy.get(commonSelectors.createAccountCheckPoint).verifyVisibleElement( - "have.text", - commonText.createAccountCheckPoint - ); - cy.get(commonSelectors.verifyEmailCheckMark).should("be.visible"); - cy.get(commonSelectors.verifyEmailCheckPoint).verifyVisibleElement( - "have.text", - commonText.verifyEmailCheckPoint - ); - cy.get(commonSelectors.setUpworkspaceCheckPoint).verifyVisibleElement( - "have.text", - commonText.setUpworkspaceCheckPoint - ); - - cy.get(commonSelectors.onboardingPorgressBubble).should("be.visible"); - cy.get(commonSelectors.onboardingPageHeader).verifyVisibleElement( - "have.text", - commonText.companyPageHeader(fullName) - ); - cy.get(commonSelectors.onboardingPageSubHeader).verifyVisibleElement( - "have.text", - commonText.onboardingPageSubHeader - ); - cy.get(commonSelectors.companyNameInputField).should("be.visible"); - cy.get(commonSelectors.continueButton).verifyVisibleElement( - "have.text", - commonText.continueButton - ); - cy.get(commonSelectors.continueButton).should("be.disabled"); - cy.clearAndType(commonSelectors.companyNameInputField, workspaceName); - cy.get(commonSelectors.continueButton).should("be.enabled").click(); - - cy.get(commonSelectors.backArrow).should("be.visible"); - cy.get(commonSelectors.backArrowText).verifyVisibleElement( - "have.text", - commonText.backArrowText - ); - cy.get(commonSelectors.onboardingPageHeader).verifyVisibleElement( - "have.text", - commonText.userRolePageHeader - ); - verifyandModifyUserRole(); - - cy.get(commonSelectors.backArrow).should("be.visible"); - cy.get(commonSelectors.onboardingPageHeader).verifyVisibleElement( - "have.text", - commonText.sizeOftheCompanyHeader - ); - verifyandModifySizeOftheCompany(); - - cy.get(commonSelectors.backArrow).should("be.visible"); - cy.get(commonSelectors.onboardingPageHeader).verifyVisibleElement( - "have.text", - "Enter your phone number" - ); - - cy.get(".form-control").should("be.visible"); - cy.get(".tj-onboarding-phone-input-wrapper") - .find("input") - .type("919876543210"); - cy.get(commonSelectors.continueButton).click(); +export const verifyOnboardingQuestions = (workspaceName) => { + bannerElementsVerification(); + onboardingStepOne(); + onboardingStepTwo(workspaceName); + onboardingStepThree(); }; export const verifyInvalidInvitationLink = () => { @@ -128,13 +68,15 @@ export const verifyInvalidInvitationLink = () => { export const userSignUp = (fullName, email, workspaceName = "test") => { let invitationLink; - cy.intercept("GET", "/api/organizations/public-configs").as("publicConfig"); + cy.intercept("GET", "/api/login-configs/public").as("publicConfig"); cy.visit("/"); cy.wait("@publicConfig"); - cy.wait(2000) + cy.wait(2000); cy.get(commonSelectors.createAnAccountLink, { timout: 10000 }).click(); cy.wait(2000); - cy.get(onboardingSelectors.nameInput, { timeout: 1000 }).should("not.be.disabled"); + cy.get(onboardingSelectors.nameInput, { timeout: 1000 }).should( + "not.be.disabled" + ); cy.get(onboardingSelectors.nameInput).clear(); cy.get(onboardingSelectors.nameInput).type(fullName); cy.clearAndType(onboardingSelectors.loginEmailInput, email); @@ -142,7 +84,7 @@ export const userSignUp = (fullName, email, workspaceName = "test") => { cy.get(commonSelectors.signUpButton).click(); cy.wait(2500); - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select invitation_token from users where email='${email}';`, }).then((resp) => { @@ -150,53 +92,15 @@ export const userSignUp = (fullName, email, workspaceName = "test") => { cy.visit(invitationLink); cy.wait(2500); }); - if (Cypress.env("environment") !== "Community") { - cy.clearAndType('[data-cy="onboarding-workspace-name-input"]', workspaceName); + if (Cypress.env("environment") == "Cloud") { + cy.clearAndType( + '[data-cy="onboarding-workspace-name-input"]', + workspaceName + ); cy.get('[data-cy="onboarding-submit-button"]').click(); } }; -export const fetchAndVisitInviteLink = (email) => { - let invitationToken, - organizationToken, - workspaceId, - userId, - url = ""; - - cy.task("updateId", { - dbconfig: Cypress.env("app_db"), - sql: `select invitation_token from users where email='${email}';`, - }).then((resp) => { - invitationToken = resp.rows[0].invitation_token; - - cy.task("updateId", { - dbconfig: Cypress.env("app_db"), - sql: "select id from organizations where name='My workspace';", - }).then((resp) => { - workspaceId = resp.rows[0].id; - - cy.task("updateId", { - dbconfig: Cypress.env("app_db"), - sql: `select id from users where email='${email}';`, - }).then((resp) => { - userId = resp.rows[0].id; - - cy.task("updateId", { - dbconfig: Cypress.env("app_db"), - sql: `select invitation_token from organization_users where user_id='${userId}';`, - }).then((resp) => { - organizationToken = resp.rows[1].invitation_token; - url = `/invitations/${invitationToken}/workspaces/${organizationToken}?oid=${workspaceId}`; - cy.apiLogout(); - cy.wait(1000); - cy.visit(url); - }); - }); - }); - }); -}; - - export const inviteUser = (firstName, email) => { cy.apiUserInvite(firstName, email); fetchAndVisitInviteLink(email); @@ -227,23 +131,22 @@ export const roleBasedOnboarding = (firstName, email, userRole) => { cy.get(commonSelectors.acceptInviteButton).click(); }; - export const visitWorkspaceInvitation = (email, workspaceName) => { let workspaceId, userId, url, organizationToken; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select id from organizations where name='${workspaceName}';`, }).then((resp) => { workspaceId = resp.rows[0].id; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select id from users where email='${email}';`, }).then((resp) => { userId = resp.rows[0].id; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select invitation_token from organization_users where organization_id= '${workspaceId}' AND user_id='${userId}';`, }).then((resp) => { @@ -324,13 +227,14 @@ export const SignUpPageElements = () => { export const signUpLink = (email) => { let invitationLink; - cy.task("updateId", { + cy.task("dbConnection", { dbconfig: Cypress.env("app_db"), sql: `select invitation_token from users where email='${email}';`, }).then((resp) => { invitationLink = `/invitations/${resp.rows[0].invitation_token}`; cy.visit(invitationLink); cy.wait(3000); + }); }; @@ -343,4 +247,94 @@ export const bannerElementsVerification = () => { bannerElements.forEach((element) => { cy.get(element.selector).should("be.visible"); }); -}; \ No newline at end of file +}; + +export const enableInstanceSignUp = (allow = true) => { + const value = allow ? "true" : "false"; + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `UPDATE instance_settings SET value = '${value}' WHERE key = 'ALLOW_PERSONAL_WORKSPACE'; + UPDATE instance_settings SET value = '${value}' WHERE key = 'ENABLE_SIGNUP';`, + }); +}; + +export const onboardingStepOne = () => { + const companyPageTexts = [ + { + selector: onboardingSelectors.tellUsAbit, + text: "Tell us a bit about yourself", + }, + { + selector: onboardingSelectors.pageDescription, + text: "This information will help us improve ToolJet", + }, + { + selector: '[data-cy="onboarding-company-name-label"]', + text: "Company name *", + }, + { + selector: '[data-cy="onboarding-build-purpose-label"]', + text: "What would you like to build on ToolJet? *", + }, + ]; + + companyPageTexts.forEach((item) => { + cy.get(item.selector).should("be.visible").and("have.text", item.text); + }); + + cy.get(onboardingSelectors.companyNameInput).should("be.visible"); + cy.get(onboardingSelectors.buildPurposeInput).should("be.visible"); + cy.get(onboardingSelectors.onboardingSubmitButton).verifyVisibleElement( + "have.attr", + "disabled" + ); + + cy.get(onboardingSelectors.companyNameInput).type("Tooljet"); + cy.get(onboardingSelectors.onboardingSubmitButton).should( + "have.attr", + "disabled" + ); + cy.get(onboardingSelectors.buildPurposeInput).type("Exploring"); + cy.get(onboardingSelectors.onboardingSubmitButton).verifyVisibleElement( + "have.text", + "Continue" + ); + cy.get(onboardingSelectors.onboardingSubmitButton) + .should("be.enabled") + .click(); +}; + +export const onboardingStepTwo = (workspaceName = "My workspace") => { + cy.get(commonSelectors.setUpworkspaceCheckPoint) + .should("be.visible") + .and("have.text", "Set up your workspace!"); + + cy.get(onboardingSelectors.pageDescription).verifyVisibleElement( + "have.text", + "Set up workspaces to manage users, applications & resources across various teams" + ); + cy.get(commonSelectors.workspaceNameInputLabel) + .should("be.visible") + .and("have.text", commonText.workspaceNameInputLabel); + cy.clearAndType(commonSelectors.workspaceNameInputField, workspaceName); + cy.get(commonSelectors.OnbordingContinue) + .verifyVisibleElement("have.text", "Continue") + .click(); +}; + +export const onboardingStepThree = () => { + cy.get( + `[data-cy="we've-created-a-sample-application-for-you!-header"]` + ).verifyVisibleElement( + "have.text", + "We've created a sample application for you!" + ); + cy.get(onboardingSelectors.pageDescription).verifyVisibleElement( + "have.text", + "The sample application comes with a sample PostgreSQL database for you to play around with. You can also get started quickly with pre-built applications from our template collection!" + ); + + cy.get(onboardingSelectors.onboardingSubmitButton) + .verifyVisibleElement("have.text", "Continue") + .click(); +}; diff --git a/cypress-tests/cypress/support/utils/platform/eeCommon.js b/cypress-tests/cypress/support/utils/platform/eeCommon.js new file mode 100644 index 0000000000..80db62fca9 --- /dev/null +++ b/cypress-tests/cypress/support/utils/platform/eeCommon.js @@ -0,0 +1,591 @@ +import { + commonEeSelectors, + ssoEeSelector, + instanceSettingsSelector, + multiEnvSelector, + workspaceSelector, +} from "Selectors/eeCommon"; +import { ssoEeText } from "Texts/eeCommon"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import * as common from "Support/utils/common"; +import { groupsSelector } from "Selectors/manageGroups"; +import { groupsText } from "Texts/manageGroups"; +import { eeGroupsSelector } from "Selectors/eeCommon"; +import { eeGroupsText } from "Texts/eeCommon"; +import { + // verifyOnboardingQuestions, + // verifyCloudOnboardingQuestions, + fetchAndVisitInviteLink, +} from "Support/utils/manageUsers"; +import { commonText } from "Texts/common"; +import { dashboardText } from "Texts/dashboard"; +import { usersText } from "Texts/manageUsers"; +import { usersSelector } from "Selectors/manageUsers"; +import { ssoSelector } from "Selectors/manageSSO"; +import { ssoText } from "Texts/manageSSO"; +// import { appPromote } from "Support/utils/multiEnv"; + +export const oidcSSOPageElements = () => { + cy.get(ssoEeSelector.oidcToggle).click(); + cy.get(ssoSelector.saveButton).eq(1).click(); + cy.get('[data-cy="modal-title"]').verifyVisibleElement( + "have.text", + "Enable OpenID Connect" + ); + cy.get('[data-cy="modal-close-button"]').should("be.visible"); + cy.get('[data-cy="modal-message"]').verifyVisibleElement( + "have.text", + "Enabling OpenID Connect at the workspace level will override any OpenID Connect configurations set at the instance level." + ); + cy.get('[data-cy="confirmation-text"]').verifyVisibleElement( + "have.text", + "Are you sure you want to continue?" + ); + cy.get('[data-cy="cancel-button"]') + .eq(2) + .verifyVisibleElement("have.text", "Cancel"); + cy.get('[data-cy="enable-button"]').verifyVisibleElement( + "have.text", + "Enable" + ); + + cy.get('[data-cy="cancel-button"]').eq(2).click(); + cy.get('[data-cy="status-label"]').verifyVisibleElement( + "have.text", + ssoText.disabledLabel + ); + + cy.get(ssoEeSelector.oidcToggle).click(); + cy.get(ssoSelector.saveButton).eq(1).click(); + cy.get('[data-cy="enable-button"]').click(); + + cy.verifyToastMessage( + commonSelectors.toastMessage, + ssoText.toggleUpdateToast("OpenID") + ); + + cy.get(ssoEeSelector.statusLabel).verifyVisibleElement( + "have.text", + ssoEeText.enabledLabel + ); + + cy.get('[data-cy="redirect-url-label"]').verifyVisibleElement( + "have.text", + ssoText.redirectUrlLabel + ); + cy.get('[data-cy="redirect-url"]').should("be.visible"); + cy.get('[data-cy="copy-icon"]').should("be.visible"); + + cy.get(ssoEeSelector.oidcToggle).click(); + cy.get(ssoSelector.saveButton).eq(1).click(); + // cy.get('[data-cy="enable-button"]').click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + ssoText.toggleUpdateToast("OpenID") + ); + cy.get(ssoSelector.statusLabel).verifyVisibleElement( + "have.text", + ssoText.disabledLabel + ); + + cy.get(ssoEeSelector.oidcToggle).click(); + cy.clearAndType(ssoEeSelector.nameInput, ssoEeText.testName); + cy.clearAndType(ssoEeSelector.clientIdInput, ssoEeText.testclientId); + cy.clearAndType(ssoEeSelector.clientSecretInput, ssoEeText.testclientSecret); + cy.clearAndType(ssoEeSelector.WellKnownUrlInput, ssoEeText.testWellknownUrl); + cy.get(ssoSelector.saveButton).eq(1).click(); + cy.get('[data-cy="enable-button"]').click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + ssoText.toggleUpdateToast("OpenID") + ); + cy.get(ssoEeSelector.nameInput).should("have.value", ssoEeText.testName); + cy.get(ssoEeSelector.clientIdInput).should( + "have.value", + ssoEeText.testclientId + ); + cy.get(ssoEeSelector.clientSecretInput).should( + "have.value", + ssoEeText.testclientSecret + ); + cy.get(ssoEeSelector.WellKnownUrlInput).should( + "have.value", + ssoEeText.testWellknownUrl + ); +}; + +export const resetDsPermissions = () => { + common.navigateToManageGroups(); + cy.wait(200); + cy.get(groupsSelector.permissionsLink).click(); + + cy.get(groupsSelector.appsCreateCheck).then(($el) => { + if ($el.is(":checked")) { + cy.get(groupsSelector.appsCreateCheck).uncheck(); + } + }); + cy.get(eeGroupsSelector.dsCreateCheck).then(($el) => { + if ($el.is(":checked")) { + cy.get(eeGroupsSelector.dsCreateCheck).uncheck(); + } + }); + cy.get(eeGroupsSelector.dsDeleteCheck).then(($el) => { + if ($el.is(":checked")) { + cy.get(eeGroupsSelector.dsDeleteCheck).uncheck(); + } + }); +}; + +export const deleteAssignedDatasources = () => { + common.navigateToManageGroups(); + cy.get('[data-cy="datasource-link"]').click(); + cy.get("body").then(($body) => { + const removeAllButtons = $body.find('[data-cy="remove-button"]'); + if (removeAllButtons.length > 0) { + cy.get('[data-cy="remove-button"]').click({ multiple: true }); + } + }); +}; + +export const userSignUp = (fullName, email, workspaceName) => { + const verificationFunction = + Cypress.env("environment") === "Enterprise" + ? verifyOnboardingQuestions + : verifyCloudOnboardingQuestions; + + let invitationLink = ""; + cy.visit("/"); + cy.wait(500); + cy.get(commonSelectors.createAnAccountLink).realClick(); + cy.clearAndType(commonSelectors.nameInputField, fullName); + cy.clearAndType(commonSelectors.emailInputField, email); + cy.clearAndType(commonSelectors.passwordInputField, commonText.password); + cy.get(commonSelectors.signUpButton).click(); + + cy.wait(500); + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `select invitation_token from users where email='${email}';`, + }).then((resp) => { + invitationLink = `/invitations/${resp.rows[0].invitation_token}`; + cy.visit(invitationLink); + cy.get(commonSelectors.setUpToolJetButton).click(); + cy.wait(4000); + + verificationFunction(fullName, workspaceName); + }); +}; + +export const allowPersonalWorkspace = (allow = true) => { + const value = allow ? "true" : "false"; + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `UPDATE instance_settings SET value = '${value}' WHERE key = 'ALLOW_PERSONAL_WORKSPACE';`, + }); +}; + +export const addNewUserEE = (firstName, email) => { + common.navigateToManageUsers(); + cy.get(usersSelector.buttonAddUsers).click(); + cy.get(commonSelectors.inputFieldFullName).type(firstName); + cy.get(commonSelectors.inputFieldEmailAddress).type(email); + + cy.get(usersSelector.buttonInviteUsers).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + usersText.userCreatedToast + ); + WorkspaceInvitationLink(email); + cy.clearAndType(commonSelectors.passwordInputField, usersText.password); + cy.get(commonSelectors.signUpButton).click(); + cy.wait(2000); + cy.get(commonSelectors.acceptInviteButton).click(); + cy.get(commonSelectors.workspaceName).verifyVisibleElement( + "have.text", + "My workspace" + ); +}; + +export const inviteUser = (firstName, email) => { + cy.get(usersSelector.buttonAddUsers).click(); + cy.get(commonSelectors.inputFieldFullName).type(firstName); + cy.get(commonSelectors.inputFieldEmailAddress).type(email); + + cy.get(usersSelector.buttonInviteUsers).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + usersText.userCreatedToast + ); + fetchAndVisitInviteLink(email); +}; + +export const defaultWorkspace = () => { + cy.get(".org-select-container").then(($title) => { + if (!$title.text().includes("My workspace")) { + cy.get(commonSelectors.workspaceName).realClick(); + cy.contains("My workspace").realClick(); + cy.wait(2000); + defaultWorkspace(); + } + }); +}; + +export const trunOffAllowPersonalWorkspace = () => { + cy.get(commonSelectors.settingsIcon).click(); + cy.get(commonEeSelectors.instanceSettingIcon).click(); + cy.get(instanceSettingsSelector.manageInstanceSettings).click(); + cy.get(instanceSettingsSelector.allowWorkspaceToggle) + .eq(0) + .then(($el) => { + if ($el.is(":checked")) { + cy.get(instanceSettingsSelector.allowWorkspaceToggle).eq(0).uncheck(); + cy.get(commonEeSelectors.saveButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "Instance settings have been updated" + ); + } + }); +}; + +export const verifySSOSignUpPageElements = () => { + cy.get(commonSelectors.invitePageHeader).verifyVisibleElement( + "have.text", + "Join ToolJet" + ); + cy.get(commonSelectors.invitePageSubHeader).verifyVisibleElement( + "have.text", + "You are invited to ToolJet." + ); + cy.get(commonSelectors.userNameInputLabel).verifyVisibleElement( + "have.text", + commonText.userNameInputLabel + ); + cy.get(commonSelectors.invitedUserName).should("be.visible"); + cy.get(commonSelectors.emailInputLabel).verifyVisibleElement( + "have.text", + commonText.emailInputLabel + ); + cy.get(commonSelectors.invitedUserEmail).should("be.visible"); + cy.get(commonSelectors.acceptInviteButton).verifyVisibleElement( + "have.text", + commonText.acceptInviteButton + ); + + cy.get(commonSelectors.signUpTermsHelperText).should(($el) => { + expect($el.contents().first().text().trim()).to.eq( + commonText.signUpTermsHelperText + ); + }); + cy.get(commonSelectors.termsOfServiceLink) + .verifyVisibleElement("have.text", commonText.termsOfServiceLink) + .and("have.attr", "href") + .and("equal", "https://www.tooljet.com/terms"); + cy.get(commonSelectors.privacyPolicyLink) + .verifyVisibleElement("have.text", commonText.privacyPolicyLink) + .and("have.attr", "href") + .and("equal", "https://www.tooljet.com/privacy"); +}; + +export const VerifyWorkspaceInvitePageElements = () => { + cy.get(commonSelectors.invitePageHeader).verifyVisibleElement( + "have.text", + commonText.invitePageHeader + ); + cy.get(commonSelectors.invitePageSubHeader).verifyVisibleElement( + "have.text", + commonText.invitePageSubHeader + ); + cy.verifyLabel(commonText.userNameInputLabel); + cy.get(commonSelectors.invitedUserName).should("be.visible"); + cy.verifyLabel(commonText.emailInputLabel); + cy.get(commonSelectors.invitedUserEmail).should("be.visible"); + cy.get(commonSelectors.acceptInviteButton).verifyVisibleElement( + "have.text", + commonText.acceptInviteButton + ); + + cy.get(commonSelectors.signUpTermsHelperText).should(($el) => { + expect($el.contents().first().text().trim()).to.eq( + commonText.signUpTermsHelperText + ); + }); + cy.get(commonSelectors.termsOfServiceLink) + .verifyVisibleElement("have.text", commonText.termsOfServiceLink) + .and("have.attr", "href") + .and("equal", "https://www.tooljet.com/terms"); + cy.get(commonSelectors.privacyPolicyLink) + .verifyVisibleElement("have.text", commonText.privacyPolicyLink) + .and("have.attr", "href") + .and("equal", "https://www.tooljet.com/privacy"); + + cy.get("body").then(($el) => { + if ($el.text().includes("Google")) { + cy.get(ssoSelector.googleSSOText).verifyVisibleElement( + "have.text", + ssoText.googleSignUpText + ); + cy.get(ssoSelector.gitSSOText).verifyVisibleElement( + "have.text", + ssoText.gitSignUpText + ); + cy.get(commonSelectors.onboardingSeperator).should("be.visible"); + } + }); +}; + +export const WorkspaceInvitationLink = (email) => { + let invitationToken, + organizationToken, + workspaceId, + userId, + url = ""; + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `select invitation_token from users where email='${email}';`, + }).then((resp) => { + invitationToken = resp.rows[0].invitation_token; + + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: "select id from organizations where name='My workspace';", + }).then((resp) => { + workspaceId = resp.rows[0].id; + + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `select id from users where email='${email}';`, + }).then((resp) => { + userId = resp.rows[0].id; + + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `select invitation_token from organization_users where user_id='${userId}';`, + }).then((resp) => { + organizationToken = resp.rows[0].invitation_token; + + url = `/invitations/${invitationToken}/workspaces/${organizationToken}?oid=${workspaceId}`; + common.logout(); + cy.visit(url); + }); + }); + }); + }); +}; + +export const enableDefaultSSO = () => { + common.navigateToManageSSO(); + cy.get("body").then(($el) => { + if (!$el.text().includes("Allowed domains")) { + cy.get(ssoSelector.generalSettingsElements.generalSettings).click(); + } + }); + cy.get(ssoSelector.allowDefaultSSOToggle).then(($el) => { + if (!$el.is(":checked")) { + cy.get(ssoSelector.allowDefaultSSOToggle).check(); + cy.get(ssoSelector.saveButton).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.ssoToast); + } + }); +}; + +export const disableSSO = (ssoSelector, toggleSelector) => { + cy.wait(1000); + cy.get(ssoSelector).click(); + cy.get(toggleSelector).then(($el) => { + if ($el.is(":checked")) { + cy.get(toggleSelector).uncheck(); + } + }); +}; + +export const AddDataSourceToGroup = (groupName, dsName) => { + common.navigateToManageGroups(); + cy.get(groupsSelector.groupLink(groupName)).click(); + cy.get(eeGroupsSelector.datasourceLink).click(); + cy.wait(500); + cy.get( + '[data-cy="datasource-select-search"] >> .rmsc > .dropdown-container > .dropdown-heading > .dropdown-heading-value > .gray' + ).click(); + cy.contains(dsName).realClick(); + + cy.get(eeGroupsSelector.AddDsButton).click(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + "Datasources added to the group" + ); +}; + +export const enableToggle = (toggleSelector) => { + cy.get(toggleSelector).then(($el) => { + if (!$el.is(":checked")) { + cy.get(toggleSelector).check(); + } + }); +}; + +export const disableToggle = (toggleSelector) => { + cy.get(toggleSelector).then(($el) => { + if ($el.is(":checked")) { + cy.get(toggleSelector).uncheck(); + } + }); +}; + +export const verifyPromoteModalUI = (versionName, currEnv, targetEnv) => { + cy.get(commonEeSelectors.promoteButton) + .verifyVisibleElement("have.text", " Promote ") + .click(); + cy.get(commonEeSelectors.modalTitle).verifyVisibleElement( + "have.text", + `Promote ${versionName}` + ); + cy.get(commonSelectors.closeButton).should("be.visible"); + cy.get(multiEnvSelector.fromLabel).verifyVisibleElement("have.text", "FROM"); + cy.get(multiEnvSelector.toLabel).verifyVisibleElement("have.text", "TO"); + cy.get(multiEnvSelector.currEnvName).verifyVisibleElement( + "have.text", + currEnv + ); + cy.get('[data-cy="target-env-name"]').verifyVisibleElement( + "have.text", + targetEnv + ); + cy.get('[data-cy="cancel-button"]').verifyVisibleElement( + "have.text", + "Cancel" + ); + cy.get(commonEeSelectors.promoteButton) + .eq(1) + .verifyVisibleElement("have.text", "Promote "); +}; + +export const resetPassword = (email) => { + cy.visit("/"); + cy.get(commonSelectors.forgotPasswordLink).click(); + cy.clearAndType(commonSelectors.emailInputField, email); + cy.get(commonSelectors.resetPasswordLinkButton).click(); + + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `select forgot_password_token from users where email='${email}';`, + }).then((resp) => { + const passwordResetLink = `/reset-password/${resp.rows[0].forgot_password_token}`; + cy.visit(passwordResetLink); + }); + cy.wait(500); + + cy.clearAndType(commonSelectors.newPasswordInputField, "Password"); + cy.clearAndType(commonSelectors.confirmPasswordInputField, "Password"); + cy.wait(4000); + cy.get(commonSelectors.resetPasswordButton).click(); + cy.get(commonSelectors.backToLoginButton).click(); +}; + +export const verifyTooltipDisabled = (selector, message) => { + cy.get(selector) + .trigger("mouseover", { force: true }) + .then(() => { + cy.get(".tooltip-inner").last().should("have.text", message); + }); +}; + +export const createAnAppWithSlug = (appName, slug) => { + cy.apiCreateApp(appName); + cy.openApp(); + cy.dragAndDropWidget("Table", 250, 250); + appPromote("development", "release"); + cy.get(commonWidgetSelector.shareAppButton).click(); + cy.clearAndType(commonWidgetSelector.appNameSlugInput, `${slug}`); + cy.wait(2000); + cy.get(commonWidgetSelector.modalCloseButton).click(); +}; + +export const updateLicense = (key) => { + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `update instance_settings set value='${key}', updated_at= NOW() where key='LICENSE_KEY';`, + }); +}; + +export const openInstanceSettings = () => { + cy.get(commonSelectors.settingsIcon).click(); + cy.get(commonEeSelectors.instanceSettingIcon).click(); +}; + +export const openUserActionMenu = (email) => { + cy.clearAndType(commonSelectors.inputUserSearch, email); + cy.wait(1000); + cy.get('[data-cy="user-actions-button"]').eq(0).click(); + cy.wait(2000); +}; + +export const archiveWorkspace = (workspaceName) => { + cy.get(instanceSettingsSelector.allWorkspaceTab).click(); + cy.clearAndType(commonEeSelectors.searchBar, workspaceName); + cy.get(workspaceSelector.workspaceStatusChange).eq(0).click(); + cy.get(commonEeSelectors.confirmButton).click(); +}; + +export const passwordToggle = (enable) => { + cy.getCookie("tj_auth_token").then((cookie) => { + cy.request( + { + method: "PATCH", + url: "http://localhost:3000/api/organizations/configs", + headers: { + "Tj-Workspace-Id": Cypress.env("workspaceId"), + Cookie: `tj_auth_token=${cookie.value}`, + }, + body: { type: "form", enabled: enable }, + }, + { log: false } + ).then((response) => { + expect(response.status).to.equal(200); + }); + }); +}; + +export const InstanceSSO = (personalWorkspace, enableSignup, workspaceSSO) => { + allowPersonalWorkspace(personalWorkspace); + + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `UPDATE instance_settings SET value = '${enableSignup}' WHERE key = 'ENABLE_SIGNUP';UPDATE instance_settings SET value = '${workspaceSSO}' WHERE key = 'ENABLE_WORKSPACE_LOGIN_CONFIGURATION';`, + }); +}; + +export const resetInstanceDomain = () => { + cy.getCookie("tj_auth_token").then((cookie) => { + cy.request( + { + method: "PATCH", + url: "http://localhost:3000/api/instance-login-configs", + headers: { + "Tj-Workspace-Id": Cypress.env("workspaceId"), + Cookie: `tj_auth_token=${cookie.value}`, + }, + body: { allowedDomains: "" }, + }, + { log: false } + ).then((response) => { + expect(response.status).to.equal(200); + }); + }); +}; + +export const instanceSSOConfig = (allow = true) => { + const value = allow ? "true" : "false"; + + cy.task("dbConnection", { + dbconfig: Cypress.env("app_db"), + sql: `UPDATE sso_configs SET enabled = ${allow} WHERE sso IN ('google', 'git', 'openid')AND organization_id IS NULL;`, + }); +}; + +export const updateInstanceSettings = (key, value) => { + cy.task("updateSetting", { + dbconfig: Cypress.env("app_db"), + sql: `UPDATE instance_settings SET value = ${value} WHERE key = ${key};`, + }); +}; diff --git a/cypress-tests/cypress/support/utils/platform/multiEnv.js b/cypress-tests/cypress/support/utils/platform/multiEnv.js new file mode 100644 index 0000000000..be72c3ae53 --- /dev/null +++ b/cypress-tests/cypress/support/utils/platform/multiEnv.js @@ -0,0 +1,120 @@ +import { multiEnvSelector, commonEeSelectors } from "Selectors/eeCommon"; +import { commonSelectors, commonWidgetSelector } from "Selectors/common"; +import { appVersionSelectors } from "Selectors/exportImport"; +import { appVersionText } from "Texts/exportImport"; + +export const promoteApp = () => { + cy.get(commonEeSelectors.promoteButton).click(); + cy.get(commonEeSelectors.promoteButton).eq(1).click(); + cy.waitForAppLoad(); + cy.wait(3000); +}; + +export const releaseApp = () => { + cy.get(commonSelectors.releaseButton).click(); + cy.get(commonSelectors.yesButton).click(); + cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released"); + cy.wait(500); +}; + +export const launchApp = () => { + cy.url().then((url) => { + const parts = url.split("/"); + const value = parts[parts.length - 1]; + cy.log(`Extracted value: ${value}`); + cy.visit(`/applications/${value}`); + cy.wait(3000); + }); +}; + +export const appPromote = (fromEnv, toEnv) => { + const commonActions = () => { + cy.get(commonEeSelectors.promoteButton).click(); + cy.get(commonEeSelectors.promoteButton).eq(1).click(); + cy.waitForAppLoad(); + cy.wait(2000); + }; + + const transitions = { + development: { + staging: commonActions, + production: () => { + commonActions(); + appPromote("staging", "production"); + }, + release: () => { + commonActions(); + commonActions(); + cy.get(commonSelectors.releaseButton).click(); + cy.get(commonSelectors.yesButton).click(); + cy.wait(500); + }, + }, + staging: { + production: commonActions, + release: () => { + commonActions(); + cy.get(commonSelectors.releaseButton).click(); + cy.get(commonSelectors.yesButton).click(); + cy.wait(500); + }, + }, + }; + + const transition = transitions[fromEnv]?.[toEnv]; + + transition(); +}; + +export const createNewVersion = (value, newVersion = [], version) => { + cy.get('[data-cy="list-current-env-name"]').click(); + cy.get(multiEnvSelector.envNameList).eq(0).click(); + cy.get(appVersionSelectors.currentVersionField(value)).click(); + cy.get(appVersionSelectors.createNewVersionButton).click(); + cy.get(appVersionSelectors.createVersionInputField).click(); + cy.contains(`[id*="react-select-"]`, version).click(); + cy.get(appVersionSelectors.versionNameInputField).click().type(newVersion[0]); + cy.get(appVersionSelectors.createNewVersionButton).click(); + cy.waitForAppLoad(); + cy.verifyToastMessage( + commonSelectors.toastMessage, + appVersionText.createdToastMessage + ); + cy.get(appVersionSelectors.currentVersionField(newVersion[0])).should( + "be.visible" + ); +}; + +export const selectVersion = (value, newVersion = []) => { + cy.get(appVersionSelectors.currentVersionField(value)).click(); + cy.get(".react-select__menu-list .app-version-name") + .contains(newVersion[0]) + .click(); + cy.waitForAppLoad(); +}; + +export const selectEnv = (envName) => { + const envIndex = { + development: 0, + staging: 1, + production: 2, + }[envName]; + + const isValidEnvName = (envName) => { + return ( + envName === "development" || + envName === "staging" || + envName === "production" + ); + }; + + if (isValidEnvName(envName)) { + cy.get('[data-cy="list-current-env-name"]').click(); + cy.wait(500) + const envSelector = `${multiEnvSelector.envNameList}:eq(${envIndex})`; + cy.get(envSelector).click(); + cy.waitForAppLoad(); + } +}; + + diff --git a/cypress-tests/cypress/support/utils/postgreSql.js b/cypress-tests/cypress/support/utils/postgreSql.js index fc3f257627..f925e6e276 100644 --- a/cypress-tests/cypress/support/utils/postgreSql.js +++ b/cypress-tests/cypress/support/utils/postgreSql.js @@ -65,10 +65,9 @@ export const selectAndAddDataSource = ( ).click(); }); - cy.wait(1000) cy.get(postgreSqlSelector.buttonSave).should("be.disabled") cy.clearAndType( - '[data-cy="data-source-name-input-filed"]', + '[data-cy="data-source-name-input-field"]', cyParamName(`cypress-${dataSourceName}-${dataSource}`) ); cy.get(postgreSqlSelector.buttonSave).click(); @@ -88,7 +87,7 @@ export const selectAndAddDataSource = ( export const fillConnectionForm = (data, toggle = "") => { cy.get("body").then(($body) => { - const editButton = $body.find(".tj-btn"); + const editButton = $body.find('.datasource-edit-btn'); if (editButton.length > 0) { editButton.click(); } diff --git a/cypress-tests/cypress/support/utils/queries.js b/cypress-tests/cypress/support/utils/queries.js index 59fbdb6250..83e8df0e2e 100644 --- a/cypress-tests/cypress/support/utils/queries.js +++ b/cypress-tests/cypress/support/utils/queries.js @@ -11,8 +11,9 @@ export const selectQueryFromLandingPage = (dbName, label) => { }; export const deleteQuery = (queryName) => { - cy.get(`[data-cy="list-query-${queryName}"]`).realHover(); + cy.get(`[data-cy="list-query-${queryName}"]`).click(); cy.get(`[data-cy="delete-query-${queryName}"]`).click(); + cy.get('[data-cy="component-inspector-delete-button"]').click() }; export const query = (action) => { @@ -47,6 +48,8 @@ export const waitForQueryAction = (action) => { export const chainQuery = (currentQuery, trigger) => { cy.get(`[data-cy="list-query-${currentQuery}"]`).click(); + cy.wait(1000); + cy.get('[data-cy="query-tab-settings"]').click(); selectEvent("Query Success", "Run Query"); cy.get('[data-cy="query-selection-field"]') .click() @@ -55,8 +58,16 @@ export const chainQuery = (currentQuery, trigger) => { }; export const addSuccessNotification = (notification) => { - changeQueryToggles("notification-on-success"); - cy.get('[data-cy="success-message-input-field"]').clearAndTypeOnCodeMirror( - notification - ); + cy.get('[data-cy="query-tab-settings"]').click(); + cy.get('body').then(($body) => { + if (!$body.find('[data-cy="success-message-input-field"]').is(':visible')) { + changeQueryToggles("notification-on-success"); + // cy.get('[data-cy="success-message-input-field"]').then(($input) => { + // cy.wrap($input).clearAndTypeOnCodeMirror(notification); + // }); + } + }); + cy.get('[data-cy="success-message-input-field"]').clearAndTypeOnCodeMirror(notification); + cy.get('[data-cy="query-tab-setup"]').click(); + cy.wait(300); }; diff --git a/cypress-tests/cypress/support/utils/restAPI.js b/cypress-tests/cypress/support/utils/restAPI.js new file mode 100644 index 0000000000..dd205d326d --- /dev/null +++ b/cypress-tests/cypress/support/utils/restAPI.js @@ -0,0 +1,152 @@ +export const createAndRunRestAPIQuery = ({ + queryName, + dsName, + method = "GET", + url = "", + urlSuffix = "", + headersList = [], + bodyList = [], + jsonBody = null, + rawBody = null, + cookiesList = [], + paramsList = [], + run = true, + expectedResponseShape = {}, + authType = "", + authToken = "", +}) => { + cy.getCookie("tj_auth_token").then((cookie) => { + const headers = { + "Tj-Workspace-Id": Cypress.env("workspaceId"), + Cookie: `tj_auth_token=${cookie.value}`, + }; + // if (authType === "bearer" || authType === "oauth2") { + // headers["Authorization"] = `Bearer ${authToken}`; + // } else if (authType === "basic") { + // headers["Authorization"] = `Basic ${btoa(authToken)}`; + // } + + cy.request({ + method: "GET", + url: `${Cypress.env("server_host")}/api/apps/${Cypress.env("appId")}`, + headers, + }).then((appResponse) => { + const currentEnvironmentId = appResponse.body.editorEnvironment.id; + const editingVersionId = appResponse.body.editing_version.id; + + cy.request({ + method: "GET", + url: `${Cypress.env("server_host")}/api/data-sources/${Cypress.env("workspaceId")}/environments/${currentEnvironmentId}/versions/${editingVersionId}`, + headers, + }).then((dsResponse) => { + const dataSource = dsResponse.body.data_sources.find( + (ds) => ds.name === dsName + ); + const useJson = jsonBody !== null; + const useRaw = rawBody !== null; + const useForm = bodyList?.length && !useJson && !useRaw; + + const queryOptions = { + method: method.toLowerCase(), + url: url + urlSuffix, + url_params: paramsList.length ? paramsList : [["", ""]], + headers: headersList.length ? headersList : [["", ""]], + cookies: cookiesList.length ? cookiesList : [["", ""]], + body: useForm ? bodyList : [["", ""]], + json_body: useJson ? jsonBody : null, + raw_body: useRaw ? rawBody : "", + body_toggle: useJson || useRaw, + runOnPageLoad: run, + transformationLanguage: "javascript", + enableTransformation: false, + }; + const requestBody = { + app_id: Cypress.env("appId"), + app_version_id: editingVersionId, + name: queryName, + kind: "restapi", + options: queryOptions, + data_source_id: dataSource.id, + plugin_id: null, + }; + + cy.request({ + method: "POST", + url: `${Cypress.env("server_host")}/api/data-queries/data-sources/${dataSource.id}/versions/${editingVersionId}`, + headers, + body: requestBody, + }).then((createResponse) => { + expect(createResponse.status).to.eq(201); + const queryId = createResponse.body.id; + + if (run) { + cy.request({ + method: "POST", + url: `${Cypress.env("server_host")}/api/data-queries/${queryId}/run`, + headers, + failOnStatusCode: false, + }).then((runResponse) => { + const responseData = runResponse.body?.data; + const requestHeaders = + runResponse.body?.metadata?.request?.headers || {}; + + if (runResponse.body.status === "ok") { + expect([200, 201]).to.include(runResponse.status); + cy.log("Response:", responseData); + if ( + expectedResponseShape && + typeof expectedResponseShape === "object" + ) { + Object.entries(expectedResponseShape).forEach( + ([path, expected]) => { + const value = path + .split(".") + .reduce((obj, key) => obj?.[key], responseData); + + if (expected === true) { + expect(value).to.not.be.undefined; + } else { + expect(value).to.eq(expected); + } + } + ); + } + + const expectedContentType = headersList.find( + ([key]) => key.toLowerCase() === "content-type" + )?.[1]; + if (expectedContentType && requestHeaders["content-type"]) { + expect(requestHeaders["content-type"]).to.include( + expectedContentType + ); + } + if (Array.isArray(responseData)) { + responseData.forEach((item) => { + expect(item).to.have.any.keys("id", "name", "username", "email", "address"); + }); + } + if (responseData?.id) { + cy.log(responseData.id) + cy.writeFile("cypress/fixtures/restAPI/storedId.json", { + id: responseData.id, + }); + } + } else if (runResponse.body.status === "failed") { + expect(runResponse.body.message).to.eq( + "Query could not be completed" + ); + const statusCode = + runResponse.body?.metadata?.response?.statusCode; + expect([400, 401, 403, 404, 500]).to.include(statusCode); + cy.log( + "Failure validated as expected with status:", + statusCode + ); + } + }); + } + }); + }); + }); + }); +}; diff --git a/cypress-tests/cypress/support/utils/version.js b/cypress-tests/cypress/support/utils/version.js index 9d3bdb5043..ffb1ad32a0 100644 --- a/cypress-tests/cypress/support/utils/version.js +++ b/cypress-tests/cypress/support/utils/version.js @@ -7,11 +7,9 @@ import { confirmVersionModalSelectors, editVersionSelectors, } from "Selectors/version"; -import { - deleteVersionText, - releasedVersionText, -} from "Texts/version"; +import { deleteVersionText, releasedVersionText } from "Texts/version"; import { verifyComponent } from "Support/utils/basicComponents"; +import { appPromote } from "./platform/multiEnv"; export const navigateToCreateNewVersionModal = (value) => { cy.get(appVersionSelectors.appVersionLabel).click(); @@ -98,9 +96,16 @@ export const deleteVersionAndVerify = (value, toastMessageText) => { .click({ force: true }); }); - cy.get(commonSelectors.modalMessage).verifyVisibleElement("have.text", deleteVersionText.deleteModalText(value)) + cy.get(commonSelectors.modalMessage).verifyVisibleElement( + "have.text", + deleteVersionText.deleteModalText(value) + ); cy.get(confirmVersionModalSelectors.yesButton).click(); - cy.verifyToastMessage(commonSelectors.toastMessage, deleteVersionText.deleteToastMessage(value), false); + cy.verifyToastMessage( + commonSelectors.toastMessage, + deleteVersionText.deleteToastMessage(value), + false + ); }; export const verifyDuplicateVersion = (newVersion = [], version) => { @@ -112,10 +117,14 @@ export const verifyDuplicateVersion = (newVersion = [], version) => { cy.verifyToastMessage( commonSelectors.toastMessage, appVersionText.versionNameAlreadyExists + // "Already exists!" ); }; export const releasedVersionAndVerify = (currentVersion) => { + cy.ifEnv("Enterprise", () => { + appPromote("development", "production"); + }); cy.contains("Release").click(); cy.get(confirmVersionModalSelectors.yesButton).click(); @@ -139,9 +148,10 @@ export const verifyVersionAfterPreview = (currentVersion) => { .click(); cy.url().should("include", "/home"); cy.wait(2000); - cy.get('[data-cy^="draggable-widget-table"]').should('be.visible') + cy.get('[data-cy^="draggable-widget-table"]').should("be.visible"); cy.url().should("include", `version=${currentVersion}`); - cy.get('[data-cy="viewer-page-logo"]').click(); + // cy.get('[data-cy="viewer-page-logo"]').click(); + cy.go("back"); cy.wait(8000); }; @@ -149,5 +159,5 @@ export const switchVersionAndVerify = (currentVersion, newVersion) => { cy.get(appVersionSelectors.currentVersionField(currentVersion)) .should("be.visible") .click(); - cy.get('.app-version-name').contains(newVersion).click(); -} + cy.get(".app-version-name").contains(newVersion).click(); +}; diff --git a/deploy/ec2/ee/setup_app b/deploy/ec2/ee/setup_app index 3dad6ebeef..deec307c1d 100755 --- a/deploy/ec2/ee/setup_app +++ b/deploy/ec2/ee/setup_app @@ -161,6 +161,21 @@ else exit 1 fi +if [[ "$WORKFLOW_WORKER" == "true" ]]; then + echo "WORKER is true. Running the worker..." + npm run worker:prod & +else + echo "WORKER is not true. Skipping the worker execution." +fi + +if sudo systemctl start neo4j && sudo systemctl enable neo4j +then + echo "Successfully started Neo4j!" +else + echo "Failed to start and enable Neo4j" + exit 1 +fi + TOOLJET_EDTION=ee npm --prefix server run db:setup:prod if sudo -E systemctl start nest @@ -172,4 +187,4 @@ else fi sudo systemctl restart nest -sudo -E systemctl restart postgrest \ No newline at end of file +sudo -E systemctl restart postgrest diff --git a/deploy/ec2/ee/setup_machine.sh b/deploy/ec2/ee/setup_machine.sh index 23aa5bf911..5dd6c635fd 100644 --- a/deploy/ec2/ee/setup_machine.sh +++ b/deploy/ec2/ee/setup_machine.sh @@ -7,11 +7,11 @@ sudo apt-get -y install --no-install-recommends wget gnupg ca-certificates apt-u curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" -nvm install 18.18.2 +nvm install 22.15.1 sudo ln -s "$(which node)" /usr/bin/node sudo ln -s "$(which npm)" /usr/bin/npm -sudo npm i -g npm@9.8.1 +sudo npm i -g npm@10.9.2 # Setup openresty wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add - @@ -58,10 +58,10 @@ envsubst "${VARS_TO_SUBSTITUTE}" < /tmp/nginx.conf > /tmp/nginx-substituted.conf sudo cp /tmp/nginx-substituted.conf /usr/local/openresty/nginx/conf/nginx.conf # Download and setup postgrest binary -curl -OL https://github.com/PostgREST/postgrest/releases/download/v12.0.2/postgrest-v12.0.2-linux-static-x64.tar.xz -tar xJf postgrest-v12.0.2-linux-static-x64.tar.xz +curl -OL https://github.com/PostgREST/postgrest/releases/download/v12.2.0/postgrest-v12.2.0-linux-static-x64.tar.xz +tar xJf postgrest-v12.2.0-linux-static-x64.tar.xz sudo mv ./postgrest /bin/postgrest -sudo rm postgrest-v12.0.2-linux-static-x64.tar.xz +sudo rm postgrest-v12.2.0-linux-static-x64.tar.xz # Add the Redis APT repository sudo add-apt-repository ppa:redislabs/redis -y @@ -78,6 +78,28 @@ sudo cp /tmp/redis-server.service /lib/systemd/system/redis-server.service # Start and enable Redis service sudo systemctl daemon-reload + +# Setup Neo4j with APOC plugin +wget -O - https://debian.neo4j.com/neotechnology.gpg.key | sudo apt-key add - +echo "deb https://debian.neo4j.com stable 5" | sudo tee /etc/apt/sources.list.d/neo4j.list +sudo apt-get update +sudo apt-get install -y neo4j=1:5.26.6 +sudo apt-mark hold neo4j + +# Setup APOC plugin +sudo mkdir -p /var/lib/neo4j/plugins +sudo wget -P /var/lib/neo4j/plugins https://github.com/neo4j/apoc/releases/download/5.26.6/apoc-5.26.6-core.jar + +# Update Neo4j config +echo "dbms.security.procedures.unrestricted=apoc.*" | sudo tee -a /etc/neo4j/neo4j.conf +echo "dbms.security.procedures.allowlist=apoc.*,algo.*,gds.*" | sudo tee -a /etc/neo4j/neo4j.conf +echo "dbms.directories.plugins=/var/lib/neo4j/plugins" | sudo tee -a /etc/neo4j/neo4j.conf +echo "dbms.security.auth_enabled=true" | sudo tee -a /etc/neo4j/neo4j.conf + +# Clean up APT cache +sudo apt-get clean +sudo rm -rf /var/lib/apt/lists/* + # Setup app directory mkdir -p ~/app @@ -92,8 +114,9 @@ mv /tmp/.env ~/app/.env mv /tmp/setup_app ~/app/setup_app sudo chmod +x ~/app/setup_app -npm install -g npm@9.8.1 +npm install -g npm@10.9.2 # Building ToolJet app npm install -g @nestjs/cli -TOOLJET_EDTION=ee npm run build \ No newline at end of file +export NODE_OPTIONS='--max-old-space-size=8000' +TOOLJET_EDTION=ee npm run build diff --git a/deploy/ec2/ee/tooljet_ubuntu_focal.pkr.hcl b/deploy/ec2/ee/tooljet_ubuntu_focal.pkr.hcl index 144803d55c..675c67f4b5 100644 --- a/deploy/ec2/ee/tooljet_ubuntu_focal.pkr.hcl +++ b/deploy/ec2/ee/tooljet_ubuntu_focal.pkr.hcl @@ -16,7 +16,7 @@ source "amazon-ebs" "ubuntu" { source_ami_filter { filters = { - name = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*" + name = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*" root-device-type = "ebs" virtualization-type = "hvm" } @@ -30,7 +30,7 @@ source "amazon-ebs" "ubuntu" { launch_block_device_mappings { device_name = "/dev/sda1" - volume_size = 10 + volume_size = 30 delete_on_termination = true } @@ -47,7 +47,7 @@ build { } provisioner "file" { - source = "../../frontend/config/nginx.conf.template" + source = "../../../frontend/config/nginx.conf.template" destination = "/tmp/nginx.conf" } diff --git a/deploy/ec2/ee/variables.pkr.hcl b/deploy/ec2/ee/variables.pkr.hcl index 39dcdfd3cd..f18073b73b 100644 --- a/deploy/ec2/ee/variables.pkr.hcl +++ b/deploy/ec2/ee/variables.pkr.hcl @@ -4,7 +4,7 @@ variable "ami_name" { variable "instance_type" { type = string - default = "t2.medium" + default = "t2.large" } variable "ami_region" { diff --git a/docker/ce-entrypoint.sh b/docker/ce-entrypoint.sh new file mode 100755 index 0000000000..b18ab59ffd --- /dev/null +++ b/docker/ce-entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +if [ -f "./.env" ]; then + export $(grep -v '^#' ./.env | xargs -d '\n') || true +fi + +if [ -d "./server/dist" ]; then + SETUP_CMD='npm run db:setup:prod' +else + SETUP_CMD='npm run db:setup' +fi + +if [ -f "./.env" ]; then + declare $(grep -v '^#' ./.env | xargs) +fi + +if [ -z "$DATABASE_URL" ]; then + ./server/scripts/wait-for-it.sh $PG_HOST:${PG_PORT:-5432} --strict --timeout=300 -- $SETUP_CMD +else + PG_HOST=$(echo "$DATABASE_URL" | awk -F'[/:@?]' '{print $6}') + PG_PORT=$(echo "$DATABASE_URL" | awk -F'[/:@?]' '{print $7}') + + if [ -z "$DATABASE_PORT" ]; then + DATABASE_PORT="5432" + fi + + ./server/scripts/wait-for-it.sh "$PG_HOST:$PG_PORT" --strict --timeout=300 -- $SETUP_CMD +fi + +exec "$@" diff --git a/docker/ce-preview.Dockerfile b/docker/ce-preview.Dockerfile index 1d463b6fd2..22190bc273 100644 --- a/docker/ce-preview.Dockerfile +++ b/docker/ce-preview.Dockerfile @@ -1,4 +1,4 @@ -FROM node:18.18.2-buster AS builder +FROM node:22.15.1 AS builder # Fix for JS heap limit allocation issue ENV NODE_OPTIONS="--max-old-space-size=4096" @@ -30,15 +30,16 @@ COPY ./server/package.json ./server/package-lock.json ./server/ RUN npm --prefix server install COPY ./server/ ./server/ RUN npm install -g @nestjs/cli +RUN npm install -g copyfiles RUN npm --prefix server run build -FROM node:18.18.2-bullseye +FROM node:22.15.1-bullseye # copy postgrest executable COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin ENV NODE_ENV=production ENV NODE_OPTIONS="--max-old-space-size=4096" -RUN apt-get update && apt-get install -y postgresql-client freetds-dev libaio1 wget supervisor +RUN apt-get update && apt-get install -y freetds-dev libaio1 wget supervisor # Install Instantclient Basic Light Oracle and Dependencies WORKDIR /opt/oracle @@ -54,9 +55,6 @@ ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21 WORKDIR / -RUN mkdir -p /app /var/log/supervisor -COPY /deploy/docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf - # copy npm scripts COPY --from=builder /app/package.json ./app/package.json # copy plugins dependencies @@ -70,7 +68,6 @@ COPY --from=builder /app/frontend/build ./app/frontend/build # copy server build COPY --from=builder /app/server/package.json ./app/server/package.json COPY --from=builder /app/server/.version ./app/server/.version -COPY --from=builder /app/server/entrypoint.sh ./app/server/entrypoint.sh COPY --from=builder /app/server/node_modules ./app/server/node_modules COPY --from=builder /app/server/templates ./app/server/templates COPY --from=builder /app/server/scripts ./app/server/scripts @@ -78,16 +75,72 @@ COPY --from=builder /app/server/dist ./app/server/dist WORKDIR /app +# Install PostgreSQL +USER root +RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - +RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list +RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor + +# Explicitly create PG main directory with correct ownership +RUN mkdir -p /var/lib/postgresql/13/main && \ + chown -R postgres:postgres /var/lib/postgresql + +RUN mkdir -p /var/log/supervisor /var/run/postgresql && \ + chown -R postgres:postgres /var/run/postgresql /var/log/supervisor + +# Remove existing data and create directory with proper ownership +RUN rm -rf /var/lib/postgresql/13/main && \ + mkdir -p /var/lib/postgresql/13/main && \ + chown -R postgres:postgres /var/lib/postgresql + +# Initialize PostgreSQL +RUN su - postgres -c "/usr/lib/postgresql/13/bin/initdb -D /var/lib/postgresql/13/main" + +# Configure Supervisor to manage PostgREST, ToolJet, and Redis +RUN echo "[supervisord] \n" \ + "nodaemon=true \n" \ + "user=root \n" \ + "\n" \ + "[program:postgrest] \n" \ + "command=/bin/postgrest \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "\n" \ + "[program:tooljet] \n" \ + "user=root \n" \ + "command=/bin/bash -c '/app/server/scripts/boot.sh' \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/dev/stdout \n" \ + "stderr_logfile_maxbytes=0 \n" \ + "stdout_logfile=/dev/stdout \n" \ + "stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf + # ENV defaults -ENV TOOLJET_HOST=http://localhost:80 \ - PGRST_HOST=http://localhost:3000 \ - PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \ - TOOLJET_DB=tooljet_db \ - ENABLE_TOOLJET_DB=true \ +ENV TOOLJET_HOST=http://localhost \ PORT=80 \ + NODE_ENV=production \ LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \ SECRET_KEY_BASE=replace_with_secret_key_base \ - ORM_LOGGING=all \ + PG_DB=tooljet_production \ + PG_USER=postgres \ + PG_PASS=postgres \ + PG_HOST=localhost \ + ENABLE_TOOLJET_DB=true \ + TOOLJET_DB_HOST=localhost \ + TOOLJET_DB_USER=postgres \ + TOOLJET_DB_PASS=postgres \ + TOOLJET_DB=tooljet_db \ + PGRST_HOST=http://localhost:3000 \ + PGRST_DB_URI=postgres://postgres:postgres@localhost/tooljet_db \ + PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \ + PGRST_DB_PRE_CONFIG=postgrest.pre_config \ + ORM_LOGGING=true \ + DEPLOYMENT_PLATFORM=docker:local \ + HOME=/home/appuser \ TERM=xterm -CMD ["/usr/bin/supervisord"] + +RUN chmod +x ./server/scripts/preview.sh +# Set the entrypoint +ENTRYPOINT ["./server/scripts/preview.sh"] diff --git a/docker/ce-production.Dockerfile b/docker/ce-production.Dockerfile index 4e70ecb882..bbd0130210 100644 --- a/docker/ce-production.Dockerfile +++ b/docker/ce-production.Dockerfile @@ -1,9 +1,9 @@ -FROM node:18.18.2-buster AS builder +FROM node:22.15.1 AS builder # Fix for JS heap limit allocation issue -ENV NODE_OPTIONS="--max-old-space-size=4096" +ENV NODE_OPTIONS="--max-old-space-size=8096" -RUN npm i -g npm@9.8.1 +RUN npm i -g npm@10.9.2 RUN mkdir -p /app WORKDIR /app @@ -31,10 +31,11 @@ ENV NODE_ENV=production COPY ./server/package.json ./server/package-lock.json ./server/ RUN npm --prefix server install COPY ./server/ ./server/ -RUN npm install -g @nestjs/cli +RUN npm install -g @nestjs/cli +RUN npm install -g copyfiles RUN npm --prefix server run build -FROM debian:11 +FROM debian:12 RUN apt-get update -yq \ && apt-get install curl gnupg zip -yq \ @@ -42,12 +43,12 @@ RUN apt-get update -yq \ && apt-get clean -y -RUN curl -O https://nodejs.org/dist/v18.18.2/node-v18.18.2-linux-x64.tar.xz \ - && tar -xf node-v18.18.2-linux-x64.tar.xz \ - && mv node-v18.18.2-linux-x64 /usr/local/lib/nodejs \ +RUN curl -O https://nodejs.org/dist/v22.15.1/node-v22.15.1-linux-x64.tar.xz \ + && tar -xf node-v22.15.1-linux-x64.tar.xz \ + && mv node-v22.15.1-linux-x64 /usr/local/lib/nodejs \ && echo 'export PATH="/usr/local/lib/nodejs/bin:$PATH"' >> /etc/profile.d/nodejs.sh \ && /bin/bash -c "source /etc/profile.d/nodejs.sh" \ - && rm node-v18.18.2-linux-x64.tar.xz + && rm node-v22.15.1-linux-x64.tar.xz ENV PATH=/usr/local/lib/nodejs/bin:$PATH ENV NODE_ENV=production @@ -88,12 +89,13 @@ COPY --from=builder /app/frontend/build ./app/frontend/build # copy server build COPY --from=builder /app/server/package.json ./app/server/package.json COPY --from=builder /app/server/.version ./app/server/.version -COPY --from=builder /app/server/entrypoint.sh ./app/server/entrypoint.sh COPY --from=builder /app/server/node_modules ./app/server/node_modules COPY --from=builder /app/server/templates ./app/server/templates COPY --from=builder /app/server/scripts ./app/server/scripts COPY --from=builder /app/server/dist ./app/server/dist +COPY ./docker/ce-entrypoint.sh ./app/server/entrypoint.sh + # Define non-sudo user RUN useradd --create-home --home-dir /home/appuser appuser \ && chown -R appuser:0 /app \ @@ -111,5 +113,4 @@ WORKDIR /app # Dependencies for scripts outside nestjs RUN npm install dotenv@10.0.0 joi@17.4.1 - ENTRYPOINT ["./server/entrypoint.sh"] diff --git a/docker/cloud/cloud-entrypoint.sh b/docker/cloud/cloud-entrypoint.sh new file mode 100755 index 0000000000..26df38cb5c --- /dev/null +++ b/docker/cloud/cloud-entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +npm cache clean --force + +if [ -d "./server/dist" ]; then + SETUP_CMD='npm run cloud:setup:prod' +else + SETUP_CMD='npm run cloud:setup' +fi + +npm cache clean --force + +if [ -f "./.env" ]; then + declare $(grep -v '^#' ./.env | xargs) +fi + +if [ -z "$DATABASE_URL" ]; then + ./server/scripts/wait-for-it.sh $PG_HOST:${PG_PORT:-5432} --strict --timeout=300 -- $SETUP_CMD +else + PG_HOST=$(echo "$DATABASE_URL" | awk -F'[/:@?]' '{print $6}') + PG_PORT=$(echo "$DATABASE_URL" | awk -F'[/:@?]' '{print $7}') + + if [ -z "$DATABASE_PORT" ]; then + DATABASE_PORT="5432" + fi + + ./server/scripts/wait-for-it.sh "$PG_HOST:$PG_PORT" --strict --timeout=300 -- $SETUP_CMD +fi + +exec "$@" diff --git a/docker/cloud/cloud-production.Dockerfile b/docker/cloud/cloud-production.Dockerfile new file mode 100644 index 0000000000..7590566fc7 --- /dev/null +++ b/docker/cloud/cloud-production.Dockerfile @@ -0,0 +1,156 @@ +FROM node:22.15.1 AS builder + +# Fix for JS heap limit allocation issue +ENV NODE_OPTIONS="--max-old-space-size=4096" + +RUN npm i -g npm@10.9.2 +RUN mkdir -p /app +RUN npm cache clean --force + +WORKDIR /app + +# Set GitHub token and branch as build arguments +ARG CUSTOM_GITHUB_TOKEN +ARG BRANCH_NAME + +# Clone and checkout the frontend repository +RUN git config --global url."https://x-access-token:${CUSTOM_GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" + +RUN git config --global http.version HTTP/1.1 +RUN git config --global http.postBuffer 524288000 +RUN git clone https://github.com/ToolJet/ToolJet.git . + +# The branch name needs to be changed the branch with modularisation in CE repo +RUN git checkout ${BRANCH_NAME} + +RUN git submodule update --init --recursive + +# Checkout the same branch in submodules if it exists, otherwise stay on default branch +RUN git submodule foreach " \ + if git show-ref --verify --quiet refs/heads/${BRANCH_NAME} || \ + git ls-remote --exit-code --heads origin ${BRANCH_NAME}; then \ + git checkout ${BRANCH_NAME}; \ + else \ + echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to main'; \ + git checkout main; \ + fi" + +# Scripts for building +COPY ./package.json ./package.json + +# Build plugins +COPY ./plugins/package.json ./plugins/package-lock.json ./plugins/ +RUN npm --prefix plugins install +COPY ./plugins/ ./plugins/ +RUN NODE_ENV=production npm --prefix plugins run build +RUN npm --prefix plugins prune --production + +ENV TOOLJET_EDITION=cloud + +# Build frontend +COPY ./frontend/package.json ./frontend/package-lock.json ./frontend/ +RUN npm --prefix frontend install +COPY ./frontend/ ./frontend/ +RUN npm --prefix frontend run build --production +RUN npm --prefix frontend prune --production + +ENV TOOLJET_EDITION=cloud +ENV NODE_ENV=production + +# Build server +COPY ./server/package.json ./server/package-lock.json ./server/ +RUN npm --prefix server install +COPY ./server/ ./server/ +RUN npm install -g @nestjs/cli +RUN npm install -g copyfiles +RUN npm --prefix server run build + +FROM debian:12 + +RUN apt-get update -yq \ + && apt-get install curl gnupg zip -yq \ + && apt-get install -yq build-essential \ + && apt-get clean -y + +RUN curl -O https://nodejs.org/dist/v22.15.1/node-v22.15.1-linux-x64.tar.xz \ + && tar -xf node-v22.15.1-linux-x64.tar.xz \ + && mv node-v22.15.1-linux-x64 /usr/local/lib/nodejs \ + && echo 'export PATH="/usr/local/lib/nodejs/bin:$PATH"' >> /etc/profile.d/nodejs.sh \ + && /bin/bash -c "source /etc/profile.d/nodejs.sh" \ + && rm node-v22.15.1-linux-x64.tar.xz +ENV PATH=/usr/local/lib/nodejs/bin:$PATH + +ENV NODE_ENV=production +ENV TOOLJET_EDITION=cloud +ENV NODE_OPTIONS="--max-old-space-size=4096" +RUN apt-get update && \ + apt-get install -y postgresql-client freetds-dev libaio1 wget && \ + apt-get -o Dpkg::Options::="--force-confold" upgrade -q -y --force-yes && \ + apt-get -y autoremove && \ + apt-get -y autoclean + +# Install Instantclient Basic Light Oracle and Dependencies +WORKDIR /opt/oracle + +RUN wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linuxx64.zip && \ + wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \ + unzip instantclient-basiclite-linuxx64.zip && rm -f instantclient-basiclite-linuxx64.zip && \ + unzip instantclient-basiclite-linux.x64-11.2.0.4.0.zip && rm -f instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \ + cd /opt/oracle/instantclient_21_10 && rm -f *jdbc* *occi* *mysql* *mql1* *ipc1* *jar uidrvci genezi adrci && \ + cd /opt/oracle/instantclient_11_2 && rm -f *jdbc* *occi* *mysql* *mql1* *ipc1* *jar uidrvci genezi adrci && \ + echo /opt/oracle/instantclient* > /etc/ld.so.conf.d/oracle-instantclient.conf && ldconfig +# Set the Instant Client library paths +ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21_10:${LD_LIBRARY_PATH}" + + +WORKDIR / + +RUN mkdir -p /app + +# copy npm scripts +COPY --from=builder /app/package.json ./app/package.json +# copy plugins dependencies +COPY --from=builder /app/plugins/dist ./app/plugins/dist +COPY --from=builder /app/plugins/client.js ./app/plugins/client.js +COPY --from=builder /app/plugins/node_modules ./app/plugins/node_modules +COPY --from=builder /app/plugins/packages/common ./app/plugins/packages/common +COPY --from=builder /app/plugins/package.json ./app/plugins/package.json +# copy frontend build +COPY --from=builder /app/frontend/build ./app/frontend/build +# copy server build +COPY --from=builder /app/server/package.json ./app/server/package.json +COPY --from=builder /app/server/.version ./app/server/.version +COPY --from=builder /app/server/ee/keys ./app/server/ee/keys +COPY --from=builder /app/server/node_modules ./app/server/node_modules +COPY --from=builder /app/server/templates ./app/server/templates +COPY --from=builder /app/server/scripts ./app/server/scripts +COPY --from=builder /app/server/dist ./app/server/dist +COPY --from=builder --chown=appuser:0 /app/server/ee/ai/assets ./app/server/ee/ai/assets + +COPY ./docker/cloud/cloud-entrypoint.sh ./app/server/cloud-entrypoint.sh + +# Define non-sudo user +RUN useradd --create-home --home-dir /home/appuser appuser \ + && chown -R appuser:0 /app \ + && chown -R appuser:0 /home/appuser \ + && chmod u+x /app \ + && chmod -R g=u /app + +# Set npm cache directory +ENV npm_config_cache /home/appuser/.npm + +ENV HOME=/home/appuser + +# Installing git for simple git commands +RUN apt-get update && apt-get install -y git && apt-get clean + +#Installing open ssh client for ssh +RUN apt-get update && apt-get install -y openssh-client + +USER appuser + +WORKDIR /app +# Dependencies for scripts outside nestjs +RUN npm install dotenv@10.0.0 joi@17.4.1 + +ENTRYPOINT ["./server/cloud-entrypoint.sh"] \ No newline at end of file diff --git a/docker/cloud/cloud-server.Dockerfile b/docker/cloud/cloud-server.Dockerfile index cc9fd4fce3..c2eb40d8d0 100644 --- a/docker/cloud/cloud-server.Dockerfile +++ b/docker/cloud/cloud-server.Dockerfile @@ -1,16 +1,18 @@ -FROM node:18.18.2-buster as builder +FROM node:22.15.1 AS builder # Fix for JS heap limit allocation issue ENV NODE_OPTIONS="--max-old-space-size=4096" -RUN npm i -g npm@9.8.1 +RUN npm i -g npm@10.9.2 +RUN npm cache clean --force RUN npm install -g @nestjs/cli RUN mkdir -p /app WORKDIR /app +# Set GitHub token and branch as build arguments ARG CUSTOM_GITHUB_TOKEN -ARG BRANCH_NAME=main +ARG BRANCH_NAME # Clone and checkout the frontend repository RUN git config --global url."https://x-access-token:${CUSTOM_GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" @@ -25,8 +27,17 @@ RUN git checkout ${BRANCH_NAME} RUN git submodule update --init --recursive # Checkout the same branch in submodules if it exists, otherwise stay on default branch -RUN git submodule foreach 'git checkout ${BRANCH_NAME} || true' +RUN git submodule foreach " \ + if git show-ref --verify --quiet refs/heads/${BRANCH_NAME} || \ + git ls-remote --exit-code --heads origin ${BRANCH_NAME}; then \ + git checkout ${BRANCH_NAME}; \ + else \ + echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to main'; \ + git checkout main; \ + fi" + +# Scripts for building COPY ./package.json ./package.json # Building ToolJet plugins @@ -37,28 +48,34 @@ ENV NODE_ENV=production RUN npm --prefix plugins run build RUN npm --prefix plugins prune --production +ENV TOOLJET_EDITION=cloud +ENV NODE_ENV=production + # Building ToolJet server COPY ./server/package.json ./server/package-lock.json ./server/ -RUN npm --prefix server install --only=production +RUN npm --prefix server install COPY ./server/ ./server/ +RUN npm install -g @nestjs/cli +RUN npm install -g copyfiles RUN npm --prefix server run build -FROM debian:11 +FROM debian:12 RUN apt-get update -yq \ && apt-get install curl gnupg zip -yq \ && apt-get install -yq build-essential \ && apt-get clean -y -RUN curl -O https://nodejs.org/dist/v18.18.2/node-v18.18.2-linux-x64.tar.xz \ - && tar -xf node-v18.18.2-linux-x64.tar.xz \ - && mv node-v18.18.2-linux-x64 /usr/local/lib/nodejs \ +RUN curl -O https://nodejs.org/dist/v22.15.1/node-v22.15.1-linux-x64.tar.xz \ + && tar -xf node-v22.15.1-linux-x64.tar.xz \ + && mv node-v22.15.1-linux-x64 /usr/local/lib/nodejs \ && echo 'export PATH="/usr/local/lib/nodejs/bin:$PATH"' >> /etc/profile.d/nodejs.sh \ && /bin/bash -c "source /etc/profile.d/nodejs.sh" \ - && rm node-v18.18.2-linux-x64.tar.xz + && rm node-v22.15.1-linux-x64.tar.xz ENV PATH=/usr/local/lib/nodejs/bin:$PATH ENV NODE_ENV=production +ENV TOOLJET_EDITION=cloud ENV NODE_OPTIONS="--max-old-space-size=4096" RUN apt-get update && apt-get install -y postgresql-client freetds-dev libaio1 wget @@ -91,11 +108,18 @@ COPY --from=builder /app/plugins/package.json ./app/plugins/package.json # copy server build COPY --from=builder /app/server/package.json ./app/server/package.json COPY --from=builder /app/server/.version ./app/server/.version -COPY --from=builder /app/server/entrypoint.sh ./app/server/entrypoint.sh +COPY --from=builder /app/server/ee/keys ./app/server/ee/keys COPY --from=builder /app/server/node_modules ./app/server/node_modules COPY --from=builder /app/server/templates ./app/server/templates COPY --from=builder /app/server/scripts ./app/server/scripts COPY --from=builder /app/server/dist ./app/server/dist +COPY --from=builder --chown=appuser:0 /app/server/ee/ai/assets ./app/server/ee/ai/assets + +COPY ./docker/cloud/cloud-entrypoint.sh ./app/server/cloud-entrypoint.sh + + +# Installing git for simple git commands +RUN apt-get update && apt-get install -y git openssh-client && apt-get clean # Define non-sudo user RUN useradd --create-home --home-dir /home/appuser appuser \ @@ -104,14 +128,26 @@ RUN useradd --create-home --home-dir /home/appuser appuser \ && chmod u+x /app \ && chmod -R g=u /app +RUN mkdir -p /home/appuser/.npm/_cacache \ + mkdir -p /home/appuser/.npm_cache_tmp \ + mkdir -p /home/appuser/.npm/_logs \ + && chown -R appuser:0 /home/appuser/.npm \ + && chmod g+s /home/appuser/.npm_cache_tmp + # Set npm cache directory -ENV npm_config_cache /home/appuser/.npm +RUN npm config set cache /tmp/npm-cache --global +ENV npm_config_cache /tmp/npm-cache ENV HOME=/home/appuser + +# Switch back to appuser USER appuser WORKDIR /app + # Dependencies for scripts outside nestjs RUN npm install dotenv@10.0.0 joi@17.4.1 -ENTRYPOINT ["./server/entrypoint.sh"] +RUN npm cache clean --force + +ENTRYPOINT ["./server/cloud-entrypoint.sh"] diff --git a/docker/ee/ee-entrypoint.sh b/docker/ee/ee-entrypoint.sh new file mode 100755 index 0000000000..23cd04ab0e --- /dev/null +++ b/docker/ee/ee-entrypoint.sh @@ -0,0 +1,183 @@ +#!/bin/bash +set -e + +npm cache clean --force + +# Load environment variables from .env if the file exists +if [ -f "./.env" ]; then + export $(grep -v '^#' ./.env | xargs -d '\n') || true +fi + +# Start Redis server only if REDIS_HOST is localhost or not set +if [ -z "$REDIS_HOST" ] || [ "$REDIS_HOST" = "localhost" ]; then + echo "Starting Redis server locally..." + redis-server /etc/redis/redis.conf & +elif [ -n "$REDIS_URL" ]; then + echo "REDIS_URL connection is set: $REDIS_URL" +else + echo "Using external Redis at $REDIS_HOST:$REDIS_PORT." + + # Validate external Redis connection + if ! ./server/scripts/wait-for-it.sh "$REDIS_HOST:${REDIS_PORT:-6379}" --strict --timeout=300 -- echo "Redis is up"; then + echo "Error: Unable to connect to Redis at $REDIS_HOST:$REDIS_PORT." + exit 1 + fi +fi + +# Check if PGRST_HOST starts with "localhost" +if [[ "$PGRST_HOST" == localhost:* ]]; then + echo "Starting PostgREST server locally..." + + # Generate PostgREST configuration in a writable directory + POSTGREST_CONFIG_PATH="/tmp/postgrest.conf" + + echo "db-uri = \"${PGRST_DB_URI}\"" > "$POSTGREST_CONFIG_PATH" + echo "db-pre-config = \"postgrest.pre_config\"" >> "$POSTGREST_CONFIG_PATH" + echo "server-port = \"${PGRST_SERVER_PORT}\"" >> "$POSTGREST_CONFIG_PATH" + + # Starting PostgREST + echo "Starting PostgREST..." + postgrest "$POSTGREST_CONFIG_PATH" & +else + echo "Using external PostgREST at $PGRST_HOST." +fi + + +# Check WORKLOW_WORKER and skip SETUP_CMD if true +if [ "${WORKFLOW_WORKER}" == "true" ]; then + echo "WORKFLOW_WORKER is set to true. Running worker process." + npm run worker:prod +else + # Determine setup command based on the presence of ./server/dist + if [ -d "./server/dist" ]; then + SETUP_CMD='npm run db:setup:prod' + else + SETUP_CMD='npm run db:setup' + fi +fi + +# Neo4j configuration +# ---------------------------------- +# Default Neo4j environment values +# ---------------------------------- +export NEO4J_USER=${NEO4J_USER:-"neo4j"} +export NEO4J_PASSWORD=${NEO4J_PASSWORD:-"appaqvyvRLbeukhFE"} +export NEO4J_AUTH=${NEO4J_AUTH:-"neo4j/appaqvyvRLbeukhFE"} +export NEO4J_URI=${NEO4J_URI:-"bolt://localhost:7687"} +export NEO4J_PLUGINS=${NEO4J_PLUGINS:-'["apoc"]'} +export NEO4J_AUTH + +# Extract username and password from NEO4J_AUTH if set +if [ -n "$NEO4J_AUTH" ]; then + # Extract username and password from NEO4J_AUTH (format: username/password) + NEO4J_USERNAME=$(echo "$NEO4J_AUTH" | cut -d'/' -f1) + NEO4J_PASSWORD=$(echo "$NEO4J_AUTH" | cut -d'/' -f2) + + # Export these for application use + export NEO4J_USERNAME + export NEO4J_PASSWORD + + echo "Neo4j authentication configured with username: $NEO4J_USERNAME" >/dev/null 2>&1 +else + echo "NEO4J_AUTH not set, using default authentication" >/dev/null 2>&1 +fi + +# Check if Neo4j is already initialized and set password if necessary +if [ "$NEO4J_AUTH" != "none" ] && [ -n "$NEO4J_PASSWORD" ]; then + echo "Setting Neo4j initial password..." >/dev/null 2>&1 + + # Ensure Neo4j is not running before setting the initial password + neo4j stop || true + + # Set the initial password using the correct command format for Neo4j 5.x + NEO4J_ADMIN_CMD=$(which neo4j-admin) + NEO4J_VERSION=$(neo4j --version | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+" | head -n 1) + echo "Detected Neo4j version: $NEO4J_VERSION" >/dev/null 2>&1 + + # Use version-specific command format + MAJOR_VERSION=$(echo $NEO4J_VERSION | cut -d. -f1) + if [ "$MAJOR_VERSION" -ge "5" ]; then + # For Neo4j 5.x and higher + echo "Using Neo4j 5.x+ password command format" >/dev/null 2>&1 + $NEO4J_ADMIN_CMD dbms set-initial-password "$NEO4J_PASSWORD" --require-password-change=false >/dev/null 2>&1 || { + echo "Warning: Could not set Neo4j password, it may already be set" >/dev/null 2>&1 + } + else + # For Neo4j 4.x and lower + echo "Using Neo4j 4.x password command format" >/dev/null 2>&1 + $NEO4J_ADMIN_CMD set-initial-password "$NEO4J_PASSWORD" >/dev/null 2>&1 || { + echo "Warning: Could not set Neo4j password, it may already be set" >/dev/null 2>&1 + } + fi +fi + +# Update Neo4j configuration +echo "Configuring Neo4j..." >/dev/null 2>&1 +cat > /etc/neo4j/neo4j.conf << EOF +# Neo4j configuration +dbms.security.auth_enabled=true +server.bolt.enabled=true +server.bolt.listen_address=0.0.0.0:7687 +server.directories.data=/var/lib/neo4j/data +server.directories.logs=/var/log/neo4j +initial.dbms.default_database=neo4j +server.directories.plugins=/var/lib/neo4j/plugins +server.directories.import=/var/lib/neo4j/import + +# APOC Settings +dbms.security.procedures.unrestricted=apoc.* +dbms.security.procedures.allowlist=apoc.*,algo.*,gds.* +EOF + +if [ -w "$NEO4J_LOG_DIR" ]; then + chmod -R 770 "$NEO4J_LOG_DIR" || echo "Warning: Could not set log directory permissions" >/dev/null 2>&1 +fi + +# Start Neo4j +echo "Starting Neo4j service..." +neo4j console >/dev/null 2>&1 & + +# Add a wait for Neo4j to be ready with more robust checking +echo "Waiting for Neo4j to be ready..." >/dev/null 2>&1 +NEO4J_READY=false +for i in {1..60}; do + # First try standard status check + if neo4j status >/dev/null 2>&1; then + echo "Neo4j is ready 🚀" + NEO4J_READY=true + break + fi + + # Also try connecting to the bolt port as a fallback + if command -v nc >/dev/null 2>&1; then + if nc -z localhost 7687 >/dev/null 2>&1; then + echo "Neo4j is ready (port 7687 is open)" + NEO4J_READY=true + break + fi + fi + + echo "Waiting for Neo4j to start... ($i/60)" >/dev/null 2>&1 + sleep 2 +done + +if [ "$NEO4J_READY" = false ]; then + echo "WARNING: Neo4j may not be fully started yet, but continuing..." +fi + +# Wait for PostgreSQL connection +if [ -z "$DATABASE_URL" ]; then + ./server/scripts/wait-for-it.sh $PG_HOST:${PG_PORT:-5432} --strict --timeout=300 -- echo "PostgreSQL is up" +else + PG_HOST=$(echo "$DATABASE_URL" | awk -F'[/:@?]' '{print $6}') + PG_PORT=$(echo "$DATABASE_URL" | awk -F'[/:@?]' '{print $7}') + + ./server/scripts/wait-for-it.sh "$PG_HOST:$PG_PORT" --strict --timeout=300 -- echo "PostgreSQL is up" +fi + +# Run setup command if defined +if [ -n "$SETUP_CMD" ]; then + $SETUP_CMD +fi + +exec "$@" diff --git a/docker/ee/ee-preview.Dockerfile b/docker/ee/ee-preview.Dockerfile index 57b258b648..ed2dbcff26 100644 --- a/docker/ee/ee-preview.Dockerfile +++ b/docker/ee/ee-preview.Dockerfile @@ -1,4 +1,4 @@ -FROM node:18.18.2-buster AS builder +FROM node:22.15.1 AS builder # Fix for JS heap limit allocation issue ENV NODE_OPTIONS="--max-old-space-size=4096" @@ -6,6 +6,7 @@ RUN mkdir -p /app WORKDIR /app +# Set GitHub token and branch as build arguments ARG CUSTOM_GITHUB_TOKEN ARG BRANCH_NAME @@ -21,8 +22,15 @@ RUN git checkout ${BRANCH_NAME} RUN git submodule update --init --recursive -# Checkout the same branch in submodules if it exists, otherwise stay on default branch -RUN git submodule foreach 'git checkout ${BRANCH_NAME} || true' +# Checkout the same branch in submodules if it exists, otherwise fallback to main +RUN git submodule foreach " \ + if git show-ref --verify --quiet refs/heads/${BRANCH_NAME} || \ + git ls-remote --exit-code --heads origin ${BRANCH_NAME}; then \ + git checkout ${BRANCH_NAME}; \ + else \ + echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to main'; \ + git checkout main; \ + fi" # Scripts for building COPY ./package.json ./package.json @@ -50,10 +58,11 @@ ENV TOOLJET_EDITION=ee COPY ./server/package.json ./server/package-lock.json ./server/ RUN npm --prefix server install COPY ./server/ ./server/ -RUN npm install -g @nestjs/cli +RUN npm install -g @nestjs/cli +RUN npm install -g copyfiles RUN npm --prefix server run build -FROM node:18.18.2-bullseye +FROM node:22.15.1-bullseye RUN apt-get update -yq \ && apt-get install curl gnupg zip -yq \ @@ -66,7 +75,7 @@ COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin ENV NODE_ENV=production ENV TOOLJET_EDITION=ee ENV NODE_OPTIONS="--max-old-space-size=4096" -RUN apt-get update && apt-get install -y postgresql-client freetds-dev libaio1 wget supervisor +RUN apt-get update && apt-get install -y freetds-dev libaio1 wget supervisor # Install Instantclient Basic Light Oracle and Dependencies WORKDIR /opt/oracle @@ -82,9 +91,6 @@ ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21 WORKDIR / -RUN mkdir -p /app /var/log/supervisor -COPY /deploy/docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf - # copy npm scripts COPY --from=builder /app/package.json ./app/package.json # copy plugins dependencies @@ -99,7 +105,6 @@ COPY --from=builder /app/frontend/build ./app/frontend/build COPY --from=builder /app/server/package.json ./app/server/package.json COPY --from=builder /app/server/.version ./app/server/.version COPY --from=builder /app/server/ee/keys ./app/server/ee/keys -COPY --from=builder /app/server/entrypoint.sh ./app/server/entrypoint.sh COPY --from=builder /app/server/node_modules ./app/server/node_modules COPY --from=builder /app/server/templates ./app/server/templates COPY --from=builder /app/server/scripts ./app/server/scripts @@ -107,16 +112,73 @@ COPY --from=builder /app/server/dist ./app/server/dist WORKDIR /app +# Install PostgreSQL +USER root +RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - +RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list +RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor --fix-missing + + +# Explicitly create PG main directory with correct ownership +RUN mkdir -p /var/lib/postgresql/13/main && \ + chown -R postgres:postgres /var/lib/postgresql + +RUN mkdir -p /var/log/supervisor /var/run/postgresql && \ + chown -R postgres:postgres /var/run/postgresql /var/log/supervisor + +# Remove existing data and create directory with proper ownership +RUN rm -rf /var/lib/postgresql/13/main && \ + mkdir -p /var/lib/postgresql/13/main && \ + chown -R postgres:postgres /var/lib/postgresql + +# Initialize PostgreSQL +RUN su - postgres -c "/usr/lib/postgresql/13/bin/initdb -D /var/lib/postgresql/13/main" + +# Configure Supervisor to manage PostgREST, ToolJet, and Redis +RUN echo "[supervisord] \n" \ + "nodaemon=true \n" \ + "user=root \n" \ + "\n" \ + "[program:postgrest] \n" \ + "command=/bin/postgrest \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "\n" \ + "[program:tooljet] \n" \ + "user=root \n" \ + "command=/bin/bash -c '/app/server/scripts/boot.sh' \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/dev/stdout \n" \ + "stderr_logfile_maxbytes=0 \n" \ + "stdout_logfile=/dev/stdout \n" \ + "stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf + # ENV defaults -ENV TOOLJET_HOST=http://localhost:80 \ - PGRST_HOST=http://localhost:3000 \ - PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \ - TOOLJET_DB=tooljet_db \ - ENABLE_TOOLJET_DB=true \ +ENV TOOLJET_HOST=http://localhost \ PORT=80 \ + NODE_ENV=production \ LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \ SECRET_KEY_BASE=replace_with_secret_key_base \ - ORM_LOGGING=all \ + PG_DB=tooljet_production \ + PG_USER=postgres \ + PG_PASS=postgres \ + PG_HOST=localhost \ + ENABLE_TOOLJET_DB=true \ + TOOLJET_DB_HOST=localhost \ + TOOLJET_DB_USER=postgres \ + TOOLJET_DB_PASS=postgres \ + TOOLJET_DB=tooljet_db \ + PGRST_HOST=http://localhost:3000 \ + PGRST_DB_URI=postgres://postgres:postgres@localhost/tooljet_db \ + PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \ + PGRST_DB_PRE_CONFIG=postgrest.pre_config \ + ORM_LOGGING=true \ + DEPLOYMENT_PLATFORM=docker:local \ + HOME=/home/appuser \ TERM=xterm -CMD ["/usr/bin/supervisord"] + +RUN chmod +x ./server/scripts/preview.sh +# Set the entrypoint +ENTRYPOINT ["./server/scripts/preview.sh"] diff --git a/docker/ee/ee-production.Dockerfile b/docker/ee/ee-production.Dockerfile index 230a2f8ebb..ffe2353ac1 100644 --- a/docker/ee/ee-production.Dockerfile +++ b/docker/ee/ee-production.Dockerfile @@ -1,17 +1,16 @@ -FROM node:18.18.2-buster AS builder +FROM node:22.15.1 AS builder # Fix for JS heap limit allocation issue ENV NODE_OPTIONS="--max-old-space-size=4096" -RUN npm i -g npm@9.8.1 -RUN mkdir -p /app -# RUN npm cache clean --force +RUN npm i -g npm@10.9.2 && npm cache clean --force +RUN mkdir -p /app WORKDIR /app # Set GitHub token and branch as build arguments ARG CUSTOM_GITHUB_TOKEN -ARG BRANCH_NAME=main +ARG BRANCH_NAME # Clone and checkout the frontend repository RUN git config --global url."https://x-access-token:${CUSTOM_GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" @@ -21,22 +20,28 @@ RUN git config --global http.postBuffer 524288000 RUN git clone https://github.com/ToolJet/ToolJet.git . # The branch name needs to be changed the branch with modularisation in CE repo -RUN git checkout main +RUN git checkout ${BRANCH_NAME} RUN git submodule update --init --recursive -# Checkout the same branch in submodules if it exists, otherwise stay on default branch -RUN git submodule foreach 'git checkout ${BRANCH_NAME} || true' +# Checkout the same branch in submodules if it exists, otherwise fallback to main +RUN git submodule foreach " \ + if git show-ref --verify --quiet refs/heads/${BRANCH_NAME} || \ + git ls-remote --exit-code --heads origin ${BRANCH_NAME}; then \ + git checkout ${BRANCH_NAME}; \ + else \ + echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to main'; \ + git checkout main; \ + fi" # Scripts for building COPY ./package.json ./package.json # Build plugins COPY ./plugins/package.json ./plugins/package-lock.json ./plugins/ -RUN npm --prefix plugins install +RUN npm --prefix plugins ci --omit=dev COPY ./plugins/ ./plugins/ -RUN NODE_ENV=production npm --prefix plugins run build -RUN npm --prefix plugins prune --production +RUN NODE_ENV=production npm --prefix plugins run build && npm --prefix plugins prune --omit=dev ENV TOOLJET_EDITION=ee @@ -44,43 +49,76 @@ ENV TOOLJET_EDITION=ee COPY ./frontend/package.json ./frontend/package-lock.json ./frontend/ RUN npm --prefix frontend install COPY ./frontend/ ./frontend/ -RUN npm --prefix frontend run build --production -RUN npm --prefix frontend prune --production +RUN npm --prefix frontend run build --production && npm --prefix frontend prune --production ENV NODE_ENV=production ENV TOOLJET_EDITION=ee # Build server COPY ./server/package.json ./server/package-lock.json ./server/ -RUN npm --prefix server install +RUN npm --prefix server ci --omit=dev COPY ./server/ ./server/ -RUN npm install -g @nestjs/cli -RUN npm --prefix server run build +RUN npm install -g @nestjs/cli && npm install -g copyfiles +RUN npm --prefix server run build && npm prune --production --prefix server -FROM debian:11 +# Install dependencies for PostgREST, curl, tar, etc. +RUN apt-get update && apt-get install -y \ + curl ca-certificates tar \ + && rm -rf /var/lib/apt/lists/* -RUN apt-get update -yq \ - && apt-get install curl gnupg zip -yq \ - && apt-get install -yq build-essential \ - && apt-get clean -y +ENV POSTGREST_VERSION=v12.2.0 + +RUN curl -Lo postgrest.tar.xz https://github.com/PostgREST/postgrest/releases/download/${POSTGREST_VERSION}/postgrest-v12.2.0-linux-static-x64.tar.xz && \ + tar -xf postgrest.tar.xz && \ + mv postgrest /postgrest && \ + rm postgrest.tar.xz && \ + chmod +x /postgrest + +FROM debian:12-slim + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + curl \ + wget \ + gnupg \ + unzip \ + ca-certificates \ + xz-utils \ + tar \ + postgresql-client \ + redis \ + libaio1 \ + git \ + openssh-client \ + freetds-dev \ + && apt-get upgrade -y -o Dpkg::Options::="--force-confold" \ + && apt-get autoremove -y \ + && apt-get clean && rm -rf /var/lib/apt/lists/* -RUN curl -O https://nodejs.org/dist/v18.18.2/node-v18.18.2-linux-x64.tar.xz \ - && tar -xf node-v18.18.2-linux-x64.tar.xz \ - && mv node-v18.18.2-linux-x64 /usr/local/lib/nodejs \ +RUN curl -O https://nodejs.org/dist/v22.15.1/node-v22.15.1-linux-x64.tar.xz \ + && tar -xf node-v22.15.1-linux-x64.tar.xz \ + && mv node-v22.15.1-linux-x64 /usr/local/lib/nodejs \ && echo 'export PATH="/usr/local/lib/nodejs/bin:$PATH"' >> /etc/profile.d/nodejs.sh \ && /bin/bash -c "source /etc/profile.d/nodejs.sh" \ - && rm node-v18.18.2-linux-x64.tar.xz + && rm node-v22.15.1-linux-x64.tar.xz ENV PATH=/usr/local/lib/nodejs/bin:$PATH ENV NODE_ENV=production ENV TOOLJET_EDITION=ee ENV NODE_OPTIONS="--max-old-space-size=4096" -RUN apt-get update && \ - apt-get install -y postgresql-client freetds-dev libaio1 wget && \ - apt-get -o Dpkg::Options::="--force-confold" upgrade -q -y --force-yes && \ - apt-get -y autoremove && \ - apt-get -y autoclean + +# Install Neo4j + APOC +RUN wget -O - https://debian.neo4j.com/neotechnology.gpg.key | apt-key add - && \ + echo "deb https://debian.neo4j.com stable 5" > /etc/apt/sources.list.d/neo4j.list && \ + apt-get update && apt-get install -y neo4j=1:5.26.6 && apt-mark hold neo4j && \ + mkdir -p /var/lib/neo4j/plugins && \ + wget -P /var/lib/neo4j/plugins https://github.com/neo4j/apoc/releases/download/5.26.6/apoc-5.26.6-core.jar && \ + echo "dbms.security.procedures.unrestricted=apoc.*" >> /etc/neo4j/neo4j.conf && \ + echo "dbms.security.procedures.allowlist=apoc.*,algo.*,gds.*" >> /etc/neo4j/neo4j.conf && \ + echo "dbms.directories.plugins=/var/lib/neo4j/plugins" >> /etc/neo4j/neo4j.conf && \ + echo "dbms.security.auth_enabled=true" >> /etc/neo4j/neo4j.conf && \ + apt-get clean && rm -rf /var/lib/apt/lists/* # Install Instantclient Basic Light Oracle and Dependencies WORKDIR /opt/oracle @@ -95,47 +133,55 @@ RUN wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketpla # Set the Instant Client library paths ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21_10:${LD_LIBRARY_PATH}" +RUN rm -f *.zip *.key && apt-get clean && rm -rf /var/lib/apt/lists/* WORKDIR / RUN mkdir -p /app -# copy npm scripts -COPY --from=builder /app/package.json ./app/package.json -# copy plugins dependencies -COPY --from=builder /app/plugins/dist ./app/plugins/dist -COPY --from=builder /app/plugins/client.js ./app/plugins/client.js -COPY --from=builder /app/plugins/node_modules ./app/plugins/node_modules -COPY --from=builder /app/plugins/packages/common ./app/plugins/packages/common -COPY --from=builder /app/plugins/package.json ./app/plugins/package.json -# copy frontend build -COPY --from=builder /app/frontend/build ./app/frontend/build -# copy server build -COPY --from=builder /app/server/package.json ./app/server/package.json -COPY --from=builder /app/server/.version ./app/server/.version -COPY --from=builder /app/server/ee/keys ./app/server/ee/keys -COPY --from=builder /app/server/entrypoint.sh ./app/server/entrypoint.sh -COPY --from=builder /app/server/node_modules ./app/server/node_modules -COPY --from=builder /app/server/templates ./app/server/templates -COPY --from=builder /app/server/scripts ./app/server/scripts -COPY --from=builder /app/server/dist ./app/server/dist -# Define non-sudo user -RUN useradd --create-home --home-dir /home/appuser appuser \ - && chown -R appuser:0 /app \ - && chown -R appuser:0 /home \ - && chmod u+x /app \ - && chmod u+x /home \ - && chmod -R g=u /app \ - && chmod -R g=u /home +RUN useradd --create-home --home-dir /home/appuser appuser -# Create directory /home/appuser and set ownership to appuser (Refer doc for understanding the changes https://app.clickup.com/37484951/v/dc/13qycq-4081) +# Use the PostgREST binary from the builder stage +COPY --from=builder --chown=appuser:0 /postgrest /usr/local/bin/postgrest + +RUN mv /usr/local/bin/postgrest /usr/local/bin/postgrest-original && \ + echo '#!/bin/bash\nexec /usr/local/bin/postgrest-original "$@" 2>&1 | sed "s/^/[PostgREST] /"' > /usr/local/bin/postgrest && \ + chmod +x /usr/local/bin/postgrest + + +# Copy application with ownership set directly to avoid chown -R +COPY --from=builder --chown=appuser:0 /app/package.json ./app/package.json +COPY --from=builder --chown=appuser:0 /app/plugins/dist ./app/plugins/dist +COPY --from=builder --chown=appuser:0 /app/plugins/client.js ./app/plugins/client.js +COPY --from=builder --chown=appuser:0 /app/plugins/node_modules ./app/plugins/node_modules +COPY --from=builder --chown=appuser:0 /app/plugins/packages/common ./app/plugins/packages/common +COPY --from=builder --chown=appuser:0 /app/plugins/package.json ./app/plugins/package.json +COPY --from=builder --chown=appuser:0 /app/frontend/build ./app/frontend/build +COPY --from=builder --chown=appuser:0 /app/server/package.json ./app/server/package.json +COPY --from=builder --chown=appuser:0 /app/server/.version ./app/server/.version +COPY --from=builder --chown=appuser:0 /app/server/ee/keys ./app/server/ee/keys +COPY --from=builder --chown=appuser:0 /app/server/node_modules ./app/server/node_modules +COPY --from=builder --chown=appuser:0 /app/server/templates ./app/server/templates +COPY --from=builder --chown=appuser:0 /app/server/scripts ./app/server/scripts +COPY --from=builder --chown=appuser:0 /app/server/dist ./app/server/dist +COPY --from=builder --chown=appuser:0 /app/server/ee/ai/assets ./app/server/ee/ai/assets +COPY ./docker/ee/ee-entrypoint.sh ./app/server/ee-entrypoint.sh + +RUN mkdir -p /var/lib/neo4j/data/databases /var/lib/neo4j/data/transactions /var/log/neo4j /opt/neo4j/run && \ + chown -R appuser:0 /var/lib/neo4j /var/log/neo4j /etc/neo4j /opt/neo4j/run && \ + chmod -R 770 /var/lib/neo4j /var/log/neo4j /etc/neo4j /opt/neo4j/run && \ + chmod -R 644 /var/lib/neo4j/plugins/*.jar && \ + chown -R appuser:0 /var/lib/neo4j/plugins && \ + chmod 755 /var/lib/neo4j/plugins + +# Create directory /home/appuser and set ownership to appuser RUN mkdir -p /home/appuser \ && chown -R appuser:0 /home/appuser \ && chmod g+s /home/appuser \ && chmod -R g=u /home/appuser \ && npm cache clean --force -# Create directory /tmp/.npm/npm-cache/ and set ownership to appuser (Refer doc for understanding the changes https://app.clickup.com/37484951/v/dc/13qycq-4081) +# Create directory /tmp/.npm/npm-cache/ and set ownership to appuser RUN mkdir -p /tmp/.npm/npm-cache/ \ && chown -R appuser:0 /tmp/.npm/npm-cache/ \ && chmod g+s /tmp/.npm/npm-cache/ \ @@ -152,15 +198,17 @@ RUN mkdir -p /tmp/.npm/npm-cache/_logs \ && chmod g+s /tmp/.npm/npm-cache/_logs \ && chmod -R g=u /tmp/.npm/npm-cache/_logs -ENV HOME=/home/appuser +# Create Redis data, log, and configuration directories +RUN mkdir -p /var/lib/redis /var/log/redis /etc/redis \ + && chown -R appuser:0 /var/lib/redis /var/log/redis /etc/redis \ + && chmod g+s /var/lib/redis /var/log/redis /etc/redis \ + && chmod -R g=u /var/lib/redis /var/log/redis /etc/redis +ENV HOME=/home/appuser # Switch back to appuser USER appuser - WORKDIR /app -# Dependencies for scripts outside nestjs -RUN npm install dotenv@10.0.0 joi@17.4.1 -RUN npm cache clean --force +RUN npm install --prefix server --no-save dotenv@10.0.0 joi@17.4.1 && npm cache clean --force -ENTRYPOINT ["./server/entrypoint.sh"] +ENTRYPOINT ["./server/ee-entrypoint.sh"] diff --git a/docker/ee/ee-try-entrypoint-lts.sh b/docker/ee/ee-try-entrypoint-lts.sh new file mode 100755 index 0000000000..c46d799a5b --- /dev/null +++ b/docker/ee/ee-try-entrypoint-lts.sh @@ -0,0 +1,227 @@ +#!/bin/bash +set -e + +echo "🚀 Starting Try ToolJet container initialization..." + +# Neo4j configuration +# ---------------------------------- +# Default Neo4j environment values +# ---------------------------------- +export NEO4J_USER=${NEO4J_USER:-"neo4j"} +export NEO4J_PASSWORD=${NEO4J_PASSWORD:-"appaqvyvRLbeukhFE"} +export NEO4J_AUTH=${NEO4J_AUTH:-"neo4j/appaqvyvRLbeukhFE"} +export NEO4J_URI=${NEO4J_URI:-"bolt://localhost:7687"} +export NEO4J_PLUGINS=${NEO4J_PLUGINS:-'["apoc"]'} +export NEO4J_AUTH + +# Extract username and password from NEO4J_AUTH if set +if [ -n "$NEO4J_AUTH" ]; then + # Extract username and password from NEO4J_AUTH (format: username/password) + NEO4J_USERNAME=$(echo "$NEO4J_AUTH" | cut -d'/' -f1) + NEO4J_PASSWORD=$(echo "$NEO4J_AUTH" | cut -d'/' -f2) + + # Export these for application use + export NEO4J_USERNAME + export NEO4J_PASSWORD + + echo "Neo4j authentication configured with username: $NEO4J_USERNAME" >/dev/null 2>&1 +else + echo "NEO4J_AUTH not set, using default authentication" >/dev/null 2>&1 +fi + +# Check if Neo4j is already initialized and set password if necessary +if [ "$NEO4J_AUTH" != "none" ] && [ -n "$NEO4J_PASSWORD" ]; then + echo "Setting Neo4j initial password..." >/dev/null 2>&1 + + # Ensure Neo4j is not running before setting the initial password + neo4j stop || true + + # Set the initial password using the correct command format for Neo4j 5.x + NEO4J_ADMIN_CMD=$(which neo4j-admin) + NEO4J_VERSION=$(neo4j --version | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+" | head -n 1) + echo "Detected Neo4j version: $NEO4J_VERSION" >/dev/null 2>&1 + + # Use version-specific command format + MAJOR_VERSION=$(echo $NEO4J_VERSION | cut -d. -f1) + if [ "$MAJOR_VERSION" -ge "5" ]; then + # For Neo4j 5.x and higher + echo "Using Neo4j 5.x+ password command format" >/dev/null 2>&1 + $NEO4J_ADMIN_CMD dbms set-initial-password "$NEO4J_PASSWORD" --require-password-change=false >/dev/null 2>&1 || { + echo "Warning: Could not set Neo4j password, it may already be set" >/dev/null 2>&1 + } + else + # For Neo4j 4.x and lower + echo "Using Neo4j 4.x password command format" >/dev/null 2>&1 + $NEO4J_ADMIN_CMD set-initial-password "$NEO4J_PASSWORD" >/dev/null 2>&1 || { + echo "Warning: Could not set Neo4j password, it may already be set" >/dev/null 2>&1 + } + fi +fi + +# Update Neo4j configuration +echo "Configuring Neo4j..." >/dev/null 2>&1 +cat > /etc/neo4j/neo4j.conf << EOF +# Neo4j configuration +dbms.security.auth_enabled=true +server.bolt.enabled=true +server.bolt.listen_address=0.0.0.0:7687 +server.directories.data=/var/lib/neo4j/data +server.directories.logs=/var/log/neo4j +initial.dbms.default_database=neo4j +server.directories.plugins=/var/lib/neo4j/plugins +server.directories.import=/var/lib/neo4j/import + +# APOC Settings +dbms.security.procedures.unrestricted=apoc.* +dbms.security.procedures.allowlist=apoc.*,algo.*,gds.* +EOF + +if [ -w "$NEO4J_LOG_DIR" ]; then + chmod -R 770 "$NEO4J_LOG_DIR" || echo "Warning: Could not set log directory permissions" >/dev/null 2>&1 +fi + +# Start Neo4j +echo "Starting Neo4j service..." +neo4j console >/dev/null 2>&1 & + +# Add a wait for Neo4j to be ready with more robust checking +echo "Waiting for Neo4j to be ready..." >/dev/null 2>&1 +NEO4J_READY=false +for i in {1..60}; do + # First try standard status check + if neo4j status >/dev/null 2>&1; then + echo "Neo4j is ready 🚀" + NEO4J_READY=true + break + fi + + # Also try connecting to the bolt port as a fallback + if command -v nc >/dev/null 2>&1; then + if nc -z localhost 7687 >/dev/null 2>&1; then + echo "Neo4j is ready (port 7687 is open)" + NEO4J_READY=true + break + fi + fi + + echo "Waiting for Neo4j to start... ($i/60)" >/dev/null 2>&1 + sleep 2 +done + +if [ "$NEO4J_READY" = false ]; then + echo "WARNING: Neo4j may not be fully started yet, but continuing..." +fi + + +# Configure PostgreSQL authentication +echo "🔧 Configuring PostgreSQL authentication..." +sed -i 's/^local\s\+all\s\+postgres\s\+\(peer\|md5\)/local all postgres trust/' /etc/postgresql/13/main/pg_hba.conf >/dev/null 2>&1 +sed -i 's/^local\s\+all\s\+all\s\+\(peer\|md5\)/local all all trust/' /etc/postgresql/13/main/pg_hba.conf >/dev/null 2>&1 + +# Start PostgreSQL +echo "📈 Starting PostgreSQL..." +service postgresql start + +# Wait until PostgreSQL is ready +echo "⏳ Waiting for PostgreSQL..." +until pg_isready -h localhost -p 5432; do + echo "PostgreSQL not ready yet, retrying..." + sleep 2 +done + +# Create user and databases for Temporal +echo "🔧 Creating Temporal DBs and user if needed..." +psql -U postgres -tc "SELECT 1 FROM pg_roles WHERE rolname='tooljet'" | grep -q 1 || \ +psql -U postgres -c "CREATE USER tooljet WITH PASSWORD 'postgres' SUPERUSER;" >/dev/null 2>&1 + +psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'temporal'" | grep -q 1 || \ +psql -U postgres -c "CREATE DATABASE temporal OWNER tooljet;" >/dev/null 2>&1 + +psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'temporal_visibility'" | grep -q 1 || \ +psql -U postgres -c "CREATE DATABASE temporal_visibility OWNER tooljet;" >/dev/null 2>&1 + +# Generate Temporal config +echo "🔧 Generating Temporal config..." +mkdir -p /etc/temporal/config +if [ -f /etc/temporal/temporal-server.template.yaml ]; then + envsubst < /etc/temporal/temporal-server.template.yaml > /etc/temporal/config/temporal-server.yaml >/dev/null 2>&1 +else + echo "❌ Missing template: /etc/temporal/temporal-server.template.yaml" + exit 1 +fi + +# Download schema files if not present +if [ ! -d "/etc/temporal/schema/postgresql" ]; then + echo "📥 Downloading Temporal schema files..." + mkdir -p /etc/temporal/schema + cd /tmp + curl -sOL https://github.com/temporalio/temporal/archive/refs/tags/v1.28.0.tar.gz + tar -xzf v1.28.0.tar.gz + cp -r temporal-1.28.0/schema/postgresql /etc/temporal/schema/ + rm -rf temporal-1.28.0 v1.28.0.tar.gz + cd / +fi + +rm -f /etc/temporal/temporal-sql-tool.yaml ~/.temporal/config.yaml +mkdir -p /tmp/temporal + +# Set up schemas +echo "🔧 Setting up Temporal schemas..." +for db in temporal temporal_visibility; do + PGPASSWORD=postgres /usr/bin/temporal-sql-tool --plugin postgres12 \ + --ep "localhost" --port 5432 --user tooljet --password postgres \ + --database $db setup-schema -v 0.0 >/dev/null 2>&1 + + schema_dir="/etc/temporal/schema/postgresql/v12" + schema_type=$([ "$db" = "temporal" ] && echo "temporal" || echo "visibility") + + PGPASSWORD=postgres /usr/bin/temporal-sql-tool --plugin postgres12 \ + --ep "localhost" --port 5432 --user tooljet --password postgres \ + --database $db update-schema -d "$schema_dir/$schema_type/versioned" >/dev/null 2>&1 +done + +echo "✅ Schema setup complete" + +# Export default port if not set +export PORT=${PORT:-80} + +# Start Temporal Server +echo "🚀 Starting Temporal Server..." +/usr/bin/temporal-server start >/dev/null 2>&1 & +TEMPORAL_PID=$! + +# Start Supervisor +echo "🚀 Starting Supervisor..." +supervisord -c /etc/supervisor/conf.d/supervisord.conf & +SUPERVISOR_PID=$! + +# Wait for Temporal to become ready +echo "⏳ Waiting for Temporal..." +for i in {1..30}; do + if grpcurl -plaintext localhost:7233 grpc.health.v1.Health/Check >/dev/null 2>&1; then + echo "✅ Temporal is ready" + break + fi + sleep 2 +done + +# Check if namespace already exists +echo "Checking if Temporal namespace exists..." +if grpcurl -plaintext localhost:7233 temporal.api.workflowservice.v1.WorkflowService/ListNamespaces | grep -q '"name": "default"'; then + echo "Namespace 'default' already exists." +else + # Register the namespace if it doesn't exist + echo "Registering Temporal namespace..." + grpcurl -plaintext -d '{ + "namespace": "default", + "description": "Default namespace", + "workflowExecutionRetentionPeriod": "259200s" + }' localhost:7233 temporal.api.workflowservice.v1.WorkflowService/RegisterNamespace +fi + +# Wait on background processes +wait $TEMPORAL_PID $SUPERVISOR_PID + +# Start worker (last step) +echo "🚀 Starting ToolJet worker..." +npm run worker:prod diff --git a/docker/ee/ee-try-entrypoint.sh b/docker/ee/ee-try-entrypoint.sh new file mode 100755 index 0000000000..df6128f9da --- /dev/null +++ b/docker/ee/ee-try-entrypoint.sh @@ -0,0 +1,226 @@ +#!/bin/bash +set -e + +echo "🚀 Starting Try ToolJet container initialization..." + +# Neo4j configuration +# ---------------------------------- +# Default Neo4j environment values +# ---------------------------------- +export NEO4J_USER=${NEO4J_USER:-"neo4j"} +export NEO4J_PASSWORD=${NEO4J_PASSWORD:-"appaqvyvRLbeukhFE"} +export NEO4J_AUTH=${NEO4J_AUTH:-"neo4j/appaqvyvRLbeukhFE"} +export NEO4J_URI=${NEO4J_URI:-"bolt://localhost:7687"} +export NEO4J_PLUGINS=${NEO4J_PLUGINS:-'["apoc"]'} +export NEO4J_AUTH + +# Extract username and password from NEO4J_AUTH if set +if [ -n "$NEO4J_AUTH" ]; then + # Extract username and password from NEO4J_AUTH (format: username/password) + NEO4J_USERNAME=$(echo "$NEO4J_AUTH" | cut -d'/' -f1) + NEO4J_PASSWORD=$(echo "$NEO4J_AUTH" | cut -d'/' -f2) + + # Export these for application use + export NEO4J_USERNAME + export NEO4J_PASSWORD + + echo "Neo4j authentication configured with username: $NEO4J_USERNAME" >/dev/null 2>&1 +else + echo "NEO4J_AUTH not set, using default authentication" >/dev/null 2>&1 +fi + +# Check if Neo4j is already initialized and set password if necessary +if [ "$NEO4J_AUTH" != "none" ] && [ -n "$NEO4J_PASSWORD" ]; then + echo "Setting Neo4j initial password..." >/dev/null 2>&1 + + # Ensure Neo4j is not running before setting the initial password + neo4j stop || true + + # Set the initial password using the correct command format for Neo4j 5.x + NEO4J_ADMIN_CMD=$(which neo4j-admin) + NEO4J_VERSION=$(neo4j --version | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+" | head -n 1) + echo "Detected Neo4j version: $NEO4J_VERSION" >/dev/null 2>&1 + + # Use version-specific command format + MAJOR_VERSION=$(echo $NEO4J_VERSION | cut -d. -f1) + if [ "$MAJOR_VERSION" -ge "5" ]; then + # For Neo4j 5.x and higher + echo "Using Neo4j 5.x+ password command format" >/dev/null 2>&1 + $NEO4J_ADMIN_CMD dbms set-initial-password "$NEO4J_PASSWORD" --require-password-change=false >/dev/null 2>&1 || { + echo "Warning: Could not set Neo4j password, it may already be set" >/dev/null 2>&1 + } + else + # For Neo4j 4.x and lower + echo "Using Neo4j 4.x password command format" >/dev/null 2>&1 + $NEO4J_ADMIN_CMD set-initial-password "$NEO4J_PASSWORD" >/dev/null 2>&1 || { + echo "Warning: Could not set Neo4j password, it may already be set" >/dev/null 2>&1 + } + fi +fi + +# Update Neo4j configuration +echo "Configuring Neo4j..." >/dev/null 2>&1 +cat > /etc/neo4j/neo4j.conf << EOF +# Neo4j configuration +dbms.security.auth_enabled=true +server.bolt.enabled=true +server.bolt.listen_address=0.0.0.0:7687 +server.directories.data=/var/lib/neo4j/data +server.directories.logs=/var/log/neo4j +initial.dbms.default_database=neo4j +server.directories.plugins=/var/lib/neo4j/plugins +server.directories.import=/var/lib/neo4j/import + +# APOC Settings +dbms.security.procedures.unrestricted=apoc.* +dbms.security.procedures.allowlist=apoc.*,algo.*,gds.* +EOF + +if [ -w "$NEO4J_LOG_DIR" ]; then + chmod -R 770 "$NEO4J_LOG_DIR" || echo "Warning: Could not set log directory permissions" >/dev/null 2>&1 +fi + +# Start Neo4j +echo "Starting Neo4j service..." +neo4j console >/dev/null 2>&1 & + +# Add a wait for Neo4j to be ready with more robust checking +echo "Waiting for Neo4j to be ready..." >/dev/null 2>&1 +NEO4J_READY=false +for i in {1..60}; do + # First try standard status check + if neo4j status >/dev/null 2>&1; then + echo "Neo4j is ready 🚀" + NEO4J_READY=true + break + fi + + # Also try connecting to the bolt port as a fallback + if command -v nc >/dev/null 2>&1; then + if nc -z localhost 7687 >/dev/null 2>&1; then + echo "Neo4j is ready (port 7687 is open)" + NEO4J_READY=true + break + fi + fi + + echo "Waiting for Neo4j to start... ($i/60)" >/dev/null 2>&1 + sleep 2 +done + +if [ "$NEO4J_READY" = false ]; then + echo "WARNING: Neo4j may not be fully started yet, but continuing..." +fi + +# Configure PostgreSQL authentication +echo "🔧 Configuring PostgreSQL authentication..." +sed -i 's/^local\s\+all\s\+postgres\s\+\(peer\|md5\)/local all postgres trust/' /etc/postgresql/13/main/pg_hba.conf >/dev/null 2>&1 +sed -i 's/^local\s\+all\s\+all\s\+\(peer\|md5\)/local all all trust/' /etc/postgresql/13/main/pg_hba.conf >/dev/null 2>&1 + +# Start PostgreSQL +echo "📈 Starting PostgreSQL..." +service postgresql start + +# Wait until PostgreSQL is ready +echo "⏳ Waiting for PostgreSQL..." +until pg_isready -h localhost -p 5432; do + echo "PostgreSQL not ready yet, retrying..." + sleep 2 +done + +# Create user and databases for Temporal +echo "🔧 Creating Temporal DBs and user if needed..." +psql -U postgres -tc "SELECT 1 FROM pg_roles WHERE rolname='tooljet'" | grep -q 1 || \ +psql -U postgres -c "CREATE USER tooljet WITH PASSWORD 'postgres' SUPERUSER;" >/dev/null 2>&1 + +psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'temporal'" | grep -q 1 || \ +psql -U postgres -c "CREATE DATABASE temporal OWNER tooljet;" >/dev/null 2>&1 + +psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'temporal_visibility'" | grep -q 1 || \ +psql -U postgres -c "CREATE DATABASE temporal_visibility OWNER tooljet;" >/dev/null 2>&1 + +# Generate Temporal config +echo "🔧 Generating Temporal config..." +mkdir -p /etc/temporal/config +if [ -f /etc/temporal/temporal-server.template.yaml ]; then + envsubst < /etc/temporal/temporal-server.template.yaml > /etc/temporal/config/temporal-server.yaml >/dev/null 2>&1 +else + echo "❌ Missing template: /etc/temporal/temporal-server.template.yaml" + exit 1 +fi + +# Download schema files if not present +if [ ! -d "/etc/temporal/schema/postgresql" ]; then + echo "📥 Downloading Temporal schema files..." + mkdir -p /etc/temporal/schema + cd /tmp + curl -sOL https://github.com/temporalio/temporal/archive/refs/tags/v1.28.0.tar.gz + tar -xzf v1.28.0.tar.gz + cp -r temporal-1.28.0/schema/postgresql /etc/temporal/schema/ + rm -rf temporal-1.28.0 v1.28.0.tar.gz + cd / +fi + +rm -f /etc/temporal/temporal-sql-tool.yaml ~/.temporal/config.yaml +mkdir -p /tmp/temporal + +# Set up schemas +echo "🔧 Setting up Temporal schemas..." +for db in temporal temporal_visibility; do + PGPASSWORD=postgres /usr/bin/temporal-sql-tool --plugin postgres12 \ + --ep "localhost" --port 5432 --user tooljet --password postgres \ + --database $db setup-schema -v 0.0 >/dev/null 2>&1 + + schema_dir="/etc/temporal/schema/postgresql/v12" + schema_type=$([ "$db" = "temporal" ] && echo "temporal" || echo "visibility") + + PGPASSWORD=postgres /usr/bin/temporal-sql-tool --plugin postgres12 \ + --ep "localhost" --port 5432 --user tooljet --password postgres \ + --database $db update-schema -d "$schema_dir/$schema_type/versioned" >/dev/null 2>&1 +done + +echo "✅ Schema setup complete" + +# Export default port if not set +export PORT=${PORT:-80} + +# Start Temporal Server +echo "🚀 Starting Temporal Server..." +/usr/bin/temporal-server start >/dev/null 2>&1 & +TEMPORAL_PID=$! + +# Start Supervisor +echo "🚀 Starting Supervisor..." +supervisord -c /etc/supervisor/conf.d/supervisord.conf & +SUPERVISOR_PID=$! + +# Wait for Temporal to become ready +echo "⏳ Waiting for Temporal..." +for i in {1..30}; do + if grpcurl -plaintext localhost:7233 grpc.health.v1.Health/Check >/dev/null 2>&1; then + echo "✅ Temporal is ready" + break + fi + sleep 2 +done + +# Check if namespace already exists +echo "Checking if Temporal namespace exists..." +if grpcurl -plaintext localhost:7233 temporal.api.workflowservice.v1.WorkflowService/ListNamespaces | grep -q '"name": "default"'; then + echo "Namespace 'default' already exists." +else + # Register the namespace if it doesn't exist + echo "Registering Temporal namespace..." + grpcurl -plaintext -d '{ + "namespace": "default", + "description": "Default namespace", + "workflowExecutionRetentionPeriod": "259200s" + }' localhost:7233 temporal.api.workflowservice.v1.WorkflowService/RegisterNamespace +fi + +# Wait on background processes +wait $TEMPORAL_PID $SUPERVISOR_PID + +# Start worker (last step) +echo "🚀 Starting ToolJet worker..." +npm --prefix server run worker:prod diff --git a/docker/ee/ee-try-tooljet-lts.Dockerfile b/docker/ee/ee-try-tooljet-lts.Dockerfile new file mode 100644 index 0000000000..c9fa440db2 --- /dev/null +++ b/docker/ee/ee-try-tooljet-lts.Dockerfile @@ -0,0 +1,141 @@ +FROM tooljet/tooljet:ee-lts-latest + +# Copy postgrest executable +COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin + +# Install Postgres +USER root +RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - +RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list +RUN echo "deb http://deb.debian.org/debian" +RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor +USER postgres +RUN service postgresql start && \ + psql -c "create role tooljet with login superuser password 'postgres';" +USER root + +RUN apt update && apt -y install redis + +# Create appuser home & ensure permission for supervisord and services +RUN mkdir -p /var/log/supervisor /var/run/postgresql /var/lib/postgresql /var/lib/redis && \ + chown -R appuser:appuser /etc/supervisor /var/log/supervisor /var/lib/redis && \ + chown -R postgres:postgres /var/run/postgresql /var/lib/postgresql + +# Install Temporal Server Binaries +RUN curl -OL https://github.com/temporalio/temporal/releases/download/v1.28.0/temporal_1.28.0_linux_amd64.tar.gz \ + && tar -xzf temporal_1.28.0_linux_amd64.tar.gz \ + && mv temporal-server /usr/bin/temporal-server \ + && mv temporal-sql-tool /usr/bin/temporal-sql-tool \ + && chmod +x /usr/bin/temporal-server /usr/bin/temporal-sql-tool \ + && rm temporal_1.28.0_linux_amd64.tar.gz + +# Install Temporal UI Server Binaries +RUN curl -OL https://github.com/temporalio/ui-server/releases/download/v2.28.0/ui-server_2.28.0_linux_amd64.tar.gz && \ + tar -xzf ui-server_2.28.0_linux_amd64.tar.gz && \ + mv ui-server /usr/bin/temporal-ui-server && \ + chmod +x /usr/bin/temporal-ui-server && \ + rm ui-server_2.28.0_linux_amd64.tar.gz + + +# Install Git for schema extraction +RUN apt update && apt install -y git && \ + git clone --depth 1 --branch v1.28.0 https://github.com/temporalio/temporal.git /tmp/temporal && \ + mkdir -p /etc/temporal/schema/postgresql && \ + cp -r /tmp/temporal/schema/postgresql/v12 /etc/temporal/schema/postgresql/ && \ + rm -rf /tmp/temporal + +# Install envsubst and grpcurl +RUN apt update && apt install -y gettext-base curl \ + && curl -sSL https://github.com/fullstorydev/grpcurl/releases/download/v1.8.0/grpcurl_1.8.0_linux_x86_64.tar.gz | tar -xzv -C /usr/local/bin grpcurl + +# Copy Temporal configuration files +COPY ./docker/ee/temporal-server.yaml /etc/temporal/temporal-server.template.yaml +COPY ./docker/ee/temporal-ui-server.yaml /etc/temporal/temporal-ui-server.yaml + +# Install Neo4j + APOC +RUN wget -O - https://debian.neo4j.com/neotechnology.gpg.key | apt-key add - && \ + echo "deb https://debian.neo4j.com stable 5" > /etc/apt/sources.list.d/neo4j.list && \ + apt-get update && apt-get install -y neo4j=1:5.26.6 && apt-mark hold neo4j && \ + mkdir -p /var/lib/neo4j/plugins && \ + wget -P /var/lib/neo4j/plugins https://github.com/neo4j/apoc/releases/download/5.26.6/apoc-5.26.6-core.jar && \ + echo "dbms.security.procedures.unrestricted=apoc.*" >> /etc/neo4j/neo4j.conf && \ + echo "dbms.security.procedures.allowlist=apoc.*,algo.*,gds.*" >> /etc/neo4j/neo4j.conf && \ + echo "dbms.directories.plugins=/var/lib/neo4j/plugins" >> /etc/neo4j/neo4j.conf && \ + echo "dbms.security.auth_enabled=true" >> /etc/neo4j/neo4j.conf && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Configure Supervisor to manage PostgREST, ToolJet, and Redis +RUN echo "[supervisord] \n" \ + "nodaemon=true \n" \ + "user=root \n" \ + "\n" \ + "[program:postgrest] \n" \ + "command=/bin/postgrest \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "\n" \ + "[program:tooljet] \n" \ + "user=appuser \n" \ + "command=/bin/bash -c '/app/server/scripts/init-db-boot.sh' \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/dev/stdout \n" \ + "stderr_logfile_maxbytes=0 \n" \ + "stdout_logfile=/dev/stdout \n" \ + "stdout_logfile_maxbytes=0 \n" \ + "\n" \ + "[program:redis] \n" \ + "user=appuser \n" \ + "command=/usr/bin/redis-server \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/dev/stdout \n" \ + "stderr_logfile_maxbytes=0 \n" \ + "stdout_logfile=/dev/stdout \n" \ + "stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf + +# ENV defaults +ENV TOOLJET_HOST=http://localhost \ + TOOLJET_SERVER_URL=http://localhost \ + PORT=80 \ + NODE_ENV=production \ + LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \ + SECRET_KEY_BASE=replace_with_secret_key_base \ + PG_DB=tooljet_production \ + PG_USER=tooljet \ + PG_PASS=postgres \ + PG_HOST=localhost \ + PG_PORT=5432 \ + ENABLE_TOOLJET_DB=true \ + TOOLJET_DB_HOST=localhost \ + TOOLJET_DB_USER=tooljet \ + TOOLJET_DB_PASS=postgres \ + TOOLJET_DB=tooljet_db \ + PGRST_HOST=http://localhost:3000 \ + PGRST_DB_URI=postgres://tooljet:postgres@localhost/tooljet_db \ + PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \ + PGRST_DB_PRE_CONFIG=postgrest.pre_config \ + ORM_LOGGING=true \ + DEPLOYMENT_PLATFORM=docker:local \ + HOME=/home/appuser \ + REDIS_HOST=localhost \ + REDIS_PORT=6379 \ + REDIS_USER=default \ + REDIS_PASS= \ + ENABLE_MARKETPLACE_FEATURE=true \ + TERM=xterm \ + ENABLE_WORKFLOW_SCHEDULING=true \ + TEMPORAL_SERVER_ADDRESS=localhost:7233 \ + TEMPORAL_TASK_QUEUE_NAME_FOR_WORKFLOWS=tooljet-workflows \ + TOOLJET_WORKFLOWS_TEMPORAL_NAMESPACE=default \ + TEMPORAL_ADDRESS=localhost:7233 \ + TEMPORAL_DB_HOST=localhost \ + TEMPORAL_DB_PORT=5432 \ + TEMPORAL_DB_USER=tooljet \ + TEMPORAL_DB_PASS=postgres \ + TEMPORAL_CORS_ORIGINS=http://localhost:8080 + +# Set the entrypoint +COPY ./docker/ee/ee-try-entrypoint-lts.sh /ee-try-entrypoint-lts.sh +RUN chmod +x /ee-try-entrypoint-lts +ENTRYPOINT ["/ee-try-entrypoint-lts.sh"] diff --git a/docker/ee/ee-try-tooljet.Dockerfile b/docker/ee/ee-try-tooljet.Dockerfile new file mode 100644 index 0000000000..a108f30691 --- /dev/null +++ b/docker/ee/ee-try-tooljet.Dockerfile @@ -0,0 +1,141 @@ +FROM tooljet/tooljet:ee-latest + +# Copy postgrest executable +COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin + +# Install Postgres +USER root +RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - +RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list +RUN echo "deb http://deb.debian.org/debian" +RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor +USER postgres +RUN service postgresql start && \ + psql -c "create role tooljet with login superuser password 'postgres';" +USER root + +RUN apt update && apt -y install redis + +# Create appuser home & ensure permission for supervisord and services +RUN mkdir -p /var/log/supervisor /var/run/postgresql /var/lib/postgresql /var/lib/redis && \ + chown -R appuser:appuser /etc/supervisor /var/log/supervisor /var/lib/redis && \ + chown -R postgres:postgres /var/run/postgresql /var/lib/postgresql + +# Install Temporal Server Binaries +RUN curl -OL https://github.com/temporalio/temporal/releases/download/v1.28.0/temporal_1.28.0_linux_amd64.tar.gz \ + && tar -xzf temporal_1.28.0_linux_amd64.tar.gz \ + && mv temporal-server /usr/bin/temporal-server \ + && mv temporal-sql-tool /usr/bin/temporal-sql-tool \ + && chmod +x /usr/bin/temporal-server /usr/bin/temporal-sql-tool \ + && rm temporal_1.28.0_linux_amd64.tar.gz + +# Install Temporal UI Server Binaries +RUN curl -OL https://github.com/temporalio/ui-server/releases/download/v2.28.0/ui-server_2.28.0_linux_amd64.tar.gz && \ + tar -xzf ui-server_2.28.0_linux_amd64.tar.gz && \ + mv ui-server /usr/bin/temporal-ui-server && \ + chmod +x /usr/bin/temporal-ui-server && \ + rm ui-server_2.28.0_linux_amd64.tar.gz + + +# Install Git for schema extraction +RUN apt update && apt install -y git && \ + git clone --depth 1 --branch v1.28.0 https://github.com/temporalio/temporal.git /tmp/temporal && \ + mkdir -p /etc/temporal/schema/postgresql && \ + cp -r /tmp/temporal/schema/postgresql/v12 /etc/temporal/schema/postgresql/ && \ + rm -rf /tmp/temporal + +# Install envsubst and grpcurl +RUN apt update && apt install -y gettext-base curl \ + && curl -sSL https://github.com/fullstorydev/grpcurl/releases/download/v1.8.0/grpcurl_1.8.0_linux_x86_64.tar.gz | tar -xzv -C /usr/local/bin grpcurl + +# Copy Temporal configuration files +COPY ./docker/ee/temporal-server.yaml /etc/temporal/temporal-server.template.yaml +COPY ./docker/ee/temporal-ui-server.yaml /etc/temporal/temporal-ui-server.yaml + +# Install Neo4j + APOC +RUN wget -O - https://debian.neo4j.com/neotechnology.gpg.key | apt-key add - && \ + echo "deb https://debian.neo4j.com stable 5" > /etc/apt/sources.list.d/neo4j.list && \ + apt-get update && apt-get install -y neo4j=1:5.26.6 && apt-mark hold neo4j && \ + mkdir -p /var/lib/neo4j/plugins && \ + wget -P /var/lib/neo4j/plugins https://github.com/neo4j/apoc/releases/download/5.26.6/apoc-5.26.6-core.jar && \ + echo "dbms.security.procedures.unrestricted=apoc.*" >> /etc/neo4j/neo4j.conf && \ + echo "dbms.security.procedures.allowlist=apoc.*,algo.*,gds.*" >> /etc/neo4j/neo4j.conf && \ + echo "dbms.directories.plugins=/var/lib/neo4j/plugins" >> /etc/neo4j/neo4j.conf && \ + echo "dbms.security.auth_enabled=true" >> /etc/neo4j/neo4j.conf && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Configure Supervisor to manage PostgREST, ToolJet, and Redis +RUN echo "[supervisord] \n" \ + "nodaemon=true \n" \ + "user=root \n" \ + "\n" \ + "[program:postgrest] \n" \ + "command=/bin/postgrest \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "\n" \ + "[program:tooljet] \n" \ + "user=appuser \n" \ + "command=/bin/bash -c '/app/server/scripts/init-db-boot.sh' \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/dev/stdout \n" \ + "stderr_logfile_maxbytes=0 \n" \ + "stdout_logfile=/dev/stdout \n" \ + "stdout_logfile_maxbytes=0 \n" \ + "\n" \ + "[program:redis] \n" \ + "user=appuser \n" \ + "command=/usr/bin/redis-server \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/dev/stdout \n" \ + "stderr_logfile_maxbytes=0 \n" \ + "stdout_logfile=/dev/stdout \n" \ + "stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf + +# ENV defaults +ENV TOOLJET_HOST=http://localhost \ + TOOLJET_SERVER_URL=http://localhost \ + PORT=80 \ + NODE_ENV=production \ + LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \ + SECRET_KEY_BASE=replace_with_secret_key_base \ + PG_DB=tooljet_production \ + PG_USER=tooljet \ + PG_PASS=postgres \ + PG_HOST=localhost \ + PG_PORT=5432 \ + ENABLE_TOOLJET_DB=true \ + TOOLJET_DB_HOST=localhost \ + TOOLJET_DB_USER=tooljet \ + TOOLJET_DB_PASS=postgres \ + TOOLJET_DB=tooljet_db \ + PGRST_HOST=http://localhost:3000 \ + PGRST_DB_URI=postgres://tooljet:postgres@localhost/tooljet_db \ + PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \ + PGRST_DB_PRE_CONFIG=postgrest.pre_config \ + ORM_LOGGING=true \ + DEPLOYMENT_PLATFORM=docker:local \ + HOME=/home/appuser \ + REDIS_HOST=localhost \ + REDIS_PORT=6379 \ + REDIS_USER=default \ + REDIS_PASS= \ + ENABLE_MARKETPLACE_FEATURE=true \ + TERM=xterm \ + ENABLE_WORKFLOW_SCHEDULING=true \ + TEMPORAL_SERVER_ADDRESS=localhost:7233 \ + TEMPORAL_TASK_QUEUE_NAME_FOR_WORKFLOWS=tooljet-workflows \ + TOOLJET_WORKFLOWS_TEMPORAL_NAMESPACE=default \ + TEMPORAL_ADDRESS=localhost:7233 \ + TEMPORAL_DB_HOST=localhost \ + TEMPORAL_DB_PORT=5432 \ + TEMPORAL_DB_USER=tooljet \ + TEMPORAL_DB_PASS=postgres \ + TEMPORAL_CORS_ORIGINS=http://localhost:8080 + +# Set the entrypoint +COPY ./docker/ee/ee-try-entrypoint.sh /ee-try-entrypoint.sh +RUN chmod +x /ee-try-entrypoint.sh +ENTRYPOINT ["/ee-try-entrypoint.sh"] \ No newline at end of file diff --git a/docker/ee/temporal-server.yaml b/docker/ee/temporal-server.yaml new file mode 100644 index 0000000000..45324165a2 --- /dev/null +++ b/docker/ee/temporal-server.yaml @@ -0,0 +1,70 @@ +log: + stdout: true + level: info + +persistence: + defaultStore: postgres-default + visibilityStore: postgres-visibility + numHistoryShards: 4 + dataStores: + postgres-default: + sql: + pluginName: "postgres12" + databaseName: "temporal" + connectAddr: "localhost:5432" + user: "tooljet" + password: "postgres" + postgres-visibility: + sql: + pluginName: "postgres12" + databaseName: "temporal_visibility" + connectAddr: "localhost:5432" + user: "tooljet" + password: "postgres" + +global: + membership: + maxJoinDuration: 30s + broadcastAddress: "127.0.0.1" + pprof: + port: 7936 + +services: + frontend: + rpc: + grpcPort: 7233 + membershipPort: 6933 + bindOnLocalHost: true + httpPort: 7243 + + matching: + rpc: + grpcPort: 7235 + membershipPort: 6935 + bindOnLocalHost: true + + history: + rpc: + grpcPort: 7234 + membershipPort: 6934 + bindOnLocalHost: true + + worker: + rpc: + membershipPort: 6939 + +clusterMetadata: + enableGlobalNamespace: false + failoverVersionIncrement: 10 + masterClusterName: "active" + currentClusterName: "active" + clusterInformation: + active: + enabled: true + initialFailoverVersion: 1 + rpcName: "frontend" + rpcAddress: "localhost:7233" + httpAddress: "localhost:7243" + +dcRedirectionPolicy: + policy: "noop" \ No newline at end of file diff --git a/docker/ee/temporal-ui-server.yaml b/docker/ee/temporal-ui-server.yaml new file mode 100644 index 0000000000..4daf530ae2 --- /dev/null +++ b/docker/ee/temporal-ui-server.yaml @@ -0,0 +1,8 @@ +temporalGrpcAddress: 127.0.0.1:7233 # Use the correct Temporal server address +host: 0.0.0.0 +port: 8080 +enableUi: true +cors: + allowOrigins: + - http://localhost:8080 +defaultNamespace: default diff --git a/frontend/.version b/frontend/.version index 7c69a55dbb..1eeac129c5 100644 --- a/frontend/.version +++ b/frontend/.version @@ -1 +1 @@ -3.7.0 +3.16.0 diff --git a/frontend/assets/csv/sample_upload_ce.csv b/frontend/assets/csv/sample_upload_ce.csv new file mode 100644 index 0000000000..2feade21bc --- /dev/null +++ b/frontend/assets/csv/sample_upload_ce.csv @@ -0,0 +1,2 @@ +First Name,Last Name,Email,User Role,Group +test,user,test@gmail.com,"Assign each user a role: Admin, Builder or End User. User role value should be exact same","For multiple groups separate using pipe (|) operator e.g. Groups1|Group2 or leave blank if no group assign" diff --git a/frontend/assets/images/GitSSH.png b/frontend/assets/images/GitSSH.png new file mode 100644 index 0000000000..2a1f9835a5 Binary files /dev/null and b/frontend/assets/images/GitSSH.png differ diff --git a/frontend/assets/images/GithubDarkMode.png b/frontend/assets/images/GithubDarkMode.png new file mode 100644 index 0000000000..7b73b64d06 Binary files /dev/null and b/frontend/assets/images/GithubDarkMode.png differ diff --git a/frontend/assets/images/GithubLightMode.png b/frontend/assets/images/GithubLightMode.png new file mode 100644 index 0000000000..c292463944 Binary files /dev/null and b/frontend/assets/images/GithubLightMode.png differ diff --git a/frontend/assets/images/Gitlab.png b/frontend/assets/images/Gitlab.png new file mode 100644 index 0000000000..5f537a9844 Binary files /dev/null and b/frontend/assets/images/Gitlab.png differ diff --git a/frontend/assets/images/SSHDarkMode.png b/frontend/assets/images/SSHDarkMode.png new file mode 100644 index 0000000000..3f16030f9c Binary files /dev/null and b/frontend/assets/images/SSHDarkMode.png differ diff --git a/frontend/assets/images/SSHLightMode.png b/frontend/assets/images/SSHLightMode.png new file mode 100644 index 0000000000..197bed5708 Binary files /dev/null and b/frontend/assets/images/SSHLightMode.png differ diff --git a/frontend/assets/images/icons/editor/file-code.svg b/frontend/assets/images/icons/editor/file-code.svg new file mode 100644 index 0000000000..4dc470055c --- /dev/null +++ b/frontend/assets/images/icons/editor/file-code.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/assets/images/icons/editor/left-sidebar/authorization.svg b/frontend/assets/images/icons/editor/left-sidebar/authorization.svg new file mode 100644 index 0000000000..609f7a5910 --- /dev/null +++ b/frontend/assets/images/icons/editor/left-sidebar/authorization.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/assets/images/icons/empty-modules.svg b/frontend/assets/images/icons/empty-modules.svg new file mode 100644 index 0000000000..4a93de1815 --- /dev/null +++ b/frontend/assets/images/icons/empty-modules.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/assets/images/icons/module-editor.svg b/frontend/assets/images/icons/module-editor.svg new file mode 100644 index 0000000000..e0b55223c3 --- /dev/null +++ b/frontend/assets/images/icons/module-editor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/assets/images/icons/widgets/chat.jsx b/frontend/assets/images/icons/widgets/chat.jsx new file mode 100644 index 0000000000..d820b217dc --- /dev/null +++ b/frontend/assets/images/icons/widgets/chat.jsx @@ -0,0 +1,25 @@ +import React from 'react'; + +const Chat = ({ fill = '#D7DBDF', width = 24, className = '', viewBox = '0 0 49 48' }) => ( + + + + +); + +export default Chat; diff --git a/frontend/assets/images/icons/widgets/emailinput.jsx b/frontend/assets/images/icons/widgets/emailinput.jsx new file mode 100644 index 0000000000..1394959b50 --- /dev/null +++ b/frontend/assets/images/icons/widgets/emailinput.jsx @@ -0,0 +1,39 @@ +import React from 'react'; + +const EmailInput = ({ fill = '#D7DBDF', width = 24, className = '', viewBox = '0 0 49 48' }) => ( + + + + + + +); + +export default EmailInput; diff --git a/frontend/assets/images/icons/widgets/horizontalDivider.jsx b/frontend/assets/images/icons/widgets/horizontalDivider.jsx new file mode 100644 index 0000000000..6f843ae57a --- /dev/null +++ b/frontend/assets/images/icons/widgets/horizontalDivider.jsx @@ -0,0 +1,22 @@ +import React from 'react'; + +const HorizontalDivider = ({ fill = '#D7DBDF', width = 24, className = '', viewBox = '0 0 49 48' }) => ( + + + + + + + + + + + +); + +export default HorizontalDivider; diff --git a/frontend/assets/images/icons/widgets/index.jsx b/frontend/assets/images/icons/widgets/index.jsx index 24705fdc6f..8752aa5ae6 100644 --- a/frontend/assets/images/icons/widgets/index.jsx +++ b/frontend/assets/images/icons/widgets/index.jsx @@ -13,8 +13,6 @@ import Customcomponent from './customcomponent.jsx'; import Datepicker from './datepicker.jsx'; import DateTimePickerV2 from './datetimepickerV2.jsx'; import Daterangepicker from './daterangepicker.jsx'; -import Divider from './divider.jsx'; -import DividerHorizondal from './dividerhorizontal.jsx'; import Downstatistics from './downstatistics.jsx'; import Dropdown from './dropdown.jsx'; import Filepicker from './filepicker.jsx'; @@ -59,8 +57,13 @@ import Upstatistics from './upstatistics.jsx'; import Verticaldivider from './verticaldivider.jsx'; import TimePicker from './timepicker.jsx'; import DatepickerV2 from './datepickerv2.jsx'; +import HorizontalDivider from './horizontalDivider.jsx'; +import PhoneInput from './phoneinput.jsx'; +import EmailInput from './emailinput.jsx'; +import Chat from './chat.jsx'; const WidgetIcon = (props) => { + // TO_DO -> Use widget type instead of widget name switch (props.name) { case 'boundedbox': return ; @@ -99,16 +102,19 @@ const WidgetIcon = (props) => { return ; case 'datetimepickerv2': return ; + case 'emailinput': + return ; + case 'phoneinput': + return ; case 'daterangepicker': return ; case 'horizontaldivider': - return ; - case 'divider-horizondal': - return ; + return ; case 'downstatistics': return ; case 'dropdown': case 'dropdownv2': + case 'dropdownlegacy': return ; case 'filepicker': return ; @@ -141,6 +147,7 @@ const WidgetIcon = (props) => { return ; case 'multiselect': case 'multiselectv2': + case 'multiselectlegacy': return ; case 'numberinput': return ; @@ -153,9 +160,11 @@ const WidgetIcon = (props) => { case 'qrscanner': return ; case 'radiobutton': + case 'radiobuttonlegacy': case 'radiobuttonv2': return ; case 'rangeslider': + case 'rangesliderv2': return ; case 'rating': return ; @@ -180,6 +189,7 @@ const WidgetIcon = (props) => { case 'text': return ; case 'textarea': + case 'textarealegacy': return