Merge pull request #25866 from appsmithorg/release_v1.9.31

This commit is contained in:
Nidhi 2023-08-03 21:31:19 +05:30 committed by GitHub
commit 23fa634474
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
420 changed files with 14674 additions and 9105 deletions

2
.github/config.json vendored

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,243 @@
name: Ad-hoc DP | Build Push Deploy from branch ( To be used by ops.appsmith.com API layer only)
on:
# This workflow is only triggered by the `/build-deploy-preview` command dispatch
workflow_dispatch:
inputs:
branch:
description: "Github Branch to be deployed"
required: true
default: "release"
skip-tests:
description: "Flag to skip Cypress tests"
required: true
default: "true"
uid:
description: "Unique ID to store the run data in the db"
required: false
sub-domain-name:
description: "Sub-domain for dp.appsmith.com to by used by this deploy preview (This will also be the image name and the k8s namespace identifier)"
required: true
jobs:
# write-job-details-to-db:
# runs-on: ubuntu-latest
# steps:
# - name: Install mongosh
# run: |
# sudo apt-get update
# sudo apt-get install -y wget gnupg
# wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
# echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
# sudo apt-get update
# sudo apt-get install -y mongodb-mongosh
# - name: Update job data to mongoDB
# run: |
# # mongosh '' --eval 'db.appsmiht-ce-github-dp-runs.insert( { run_id: ${{ github.run_id }}, uid: ${{ github.event.inputs.uid }}, status: "Started" } )'
# mongosh '' --eval 'db.appsmiht-ce-github-dp-runs.update(
# { _id: ${{ github.event.inputs.uid }} },
# {
# $set: {
# run_id: ${{ github.run_id }},
# status: "BUILD IN PROGRESS"
# }
# }
# )'
# #TODO Add mongo URI as secret
server-build:
name: server-build
uses: ./.github/workflows/server-build.yml
secrets: inherit
with:
branch: ${{ github.event.inputs.branch }}
skip-tests: ${{ github.event.inputs.skip-tests }}
client-build:
name: client-build
uses: ./.github/workflows/client-build.yml
secrets: inherit
with:
branch: ${{ github.event.inputs.branch }}
skip-tests: ${{ github.event.inputs.skip-tests }}
rts-build:
name: rts-build
uses: ./.github/workflows/rts-build.yml
secrets: inherit
with:
branch: ${{ github.event.inputs.branch }}
push-image:
needs: [client-build, rts-build, server-build]
runs-on: ubuntu-latest
if: success()
steps:
# - name: Install mongosh
# run: |
# sudo apt-get update
# sudo apt-get install -y wget gnupg
# wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
# echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
# sudo apt-get update
# sudo apt-get install -y mongodb-mongosh
# - name: Update job data to mongoDB
# run: |
# # mongosh '' --eval 'db.appsmiht-ce-github-dp-runs.insert( { run_id: ${{ github.run_id }}, uid: ${{ github.event.inputs.uid }}, status: "Started" } )'
# mongosh '' --eval 'db.appsmiht-ce-github-dp-runs.update(
# { _id: ${{ github.event.inputs.uid }} },
# {
# $set: {
# status: "BUILD SUCCESSFUL"
# }
# }
# )'
- name: Set up Depot CLI
uses: depot/setup-action@v1
# Check out merge commit
- name: Checkout PR
uses: actions/checkout@v3
with:
ref: "refs/pull/${{ github.event.client_payload.pull_request.number }}/merge"
# Timestamp will be used to create cache key
- id: timestamp
run: echo "timestamp=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_OUTPUT
# get Git-hash will be used to create cache key
- id: git_hash
run: echo "git_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Download the client build artifact
uses: actions/download-artifact@v3
with:
name: client-build
path: app/client
- name: Unpack the client build artifact
if: steps.run_result.outputs.run_result != 'success'
run: |
mkdir -p app/client/build
tar -xvf app/client/build.tar -C app/client/build
- name: Download the server build artifact
uses: actions/download-artifact@v3
with:
name: server-build
path: app/server/dist
- name: Download the rts build artifact
uses: actions/download-artifact@v3
with:
name: rts-dist
path: app/client/packages/rts/dist
- name: Untar the rts folder
run: |
tar -xvf app/client/packages/rts/dist/rts-dist.tar -C app/client/packages/rts/
echo "Cleaning up the tar files"
rm app/client/packages/rts/dist/rts-dist.tar
- name: Push to Docker Hub
uses: docker/build-push-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
repository: ${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-dp
tags: ${{ github.event.inputs.sub-domain-name }}
outputs:
imageHash: ${{ github.event.inputs.sub-domain-name }}
build-deploy-preview:
needs: [push-image]
runs-on: ubuntu-latest
defaults:
run:
working-directory: "."
if: success()
steps:
- name: Install mongosh
run: |
sudo apt-get update
sudo apt-get install -y wget gnupg
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
sudo apt-get update
sudo apt-get install -y mongodb-mongosh
- name: Update job data to mongoDB
run: |
# mongosh '' --eval 'db.appsmiht-ce-github-dp-runs.insert( { run_id: ${{ github.run_id }}, uid: ${{ github.event.inputs.uid }}, status: "Started" } )'
# mongosh '' --eval 'db.appsmiht-ce-github-dp-runs.update(
# { _id: ${{ github.event.inputs.uid }} },
# {
# $set: {
# status: "DEPLOYMENT IN PROGRESS"
# }
# }
# )'
- name: Set up Depot CLI
uses: depot/setup-action@v1
- name: Checkout PR
uses: actions/checkout@v3
with:
ref: "refs/pull/${{ github.event.client_payload.pull_request.number }}/merge"
fetch-depth: 0
- name: Install relevant packages
run: |
which aws
sudo apt update -q && sudo apt install -y curl unzip less jq
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.23.6/bin/linux/amd64/kubectl && \
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl && \
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 && \
chmod 700 get_helm.sh; ./get_helm.sh
- name: Deploy Helm chart
env:
AWS_ROLE_ARN: ${{ secrets.APPSMITH_EKS_AWS_ROLE_ARN }}
AWS_ACCESS_KEY_ID: ${{ secrets.APPSMITH_CI_AWS_SECRET_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.APPSMITH_CI_AWS_SECRET_ACCESS_KEY }}
IMAGE_HASH: ${{ needs.push-image.outputs.imageHash }}
AWS_RELEASE_CERT: ${{ secrets.APPSMITH_AWS_RELEASE_CERT_RELEASE }}
DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }}
DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
DOCKER_HUB_ACCESS_TOKEN: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
SUB_DOMAIN_NAME: ${{ github.event.inputs.sub-domain-name }}
# RECREATE: ${{ github.event.client_payload.slash_command.args.named.recreate }}
DB_USERNAME: ${{ secrets.DB_USERNAME }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
DB_URL: ${{ secrets.DB_URL }}
run: |
echo "environment variables set to deploy the image" $IMAGE_HASH
/bin/bash ./scripts/build_dp_form_branch.sh
update-db-on-completion:
needs: [build-deploy-preview]
runs-on: ubuntu-latest
if: success()
steps:
- run: echo "Workflow completed successfully!"
# This step creates a comment on the PR with a link to this workflow run.
# - name: Install mongosh
# run: |
# sudo apt-get update
# sudo apt-get install -y wget gnupg
# wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
# echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
# sudo apt-get update
# sudo apt-get install -y mongodb-mongosh
# - name: Update job data to mongoDB
# run: |
# # mongosh '' --eval 'db.appsmiht-ce-github-dp-runs.insert( { run_id: ${{ github.run_id }}, uid: ${{ github.event.inputs.uid }}, status: "Started" } )'
# mongosh '' --eval 'db.appsmiht-ce-github-dp-runs.update(
# { _id: ${{ github.event.inputs.uid }} },
# {
# $set: {
# status: "DEPLOYMENT COMPLETE"
# url: https://${{ github.event.inputs.sub-domain-name }}.dp.appsmith.com
# }
# }
# )'

View File

@ -46,3 +46,4 @@ jobs:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
workingDir: ./app/client/packages/storybook
exitOnceUploaded: true
buildScriptName: "build"

View File

@ -179,8 +179,8 @@ jobs:
ci-test-limited-result:
needs: [file-check, ci-test-limited]
# Only run if the ci-test-limited with matrices step is successful
if: needs.ci-test-limited.result != 'skipped'
# Only run if the file-check.runId == 0
if: always() && needs.file-check.outputs.runId == '0'
runs-on: ubuntu-latest
defaults:
run:
@ -278,8 +278,8 @@ jobs:
ci-test-limited-result-existing:
needs: [file-check, ci-test-limited-existing-docker-image]
# Only run if the ci-test-limited with matrices step is successful
if: needs.ci-test-limited-existing-docker-image.result != 'skipped'
# Only run if the file-check.runId !=0
if: always() && needs.file-check.outputs.runId != '0'
runs-on: ubuntu-latest
defaults:
run:

View File

@ -77,7 +77,11 @@ jobs:
if: steps.run_result.outputs.run_result != 'success'
working-directory: "."
run: |
docker build -t cicontainer .
declare -a args
if [[ "${{ inputs.pr }}" != 0 ]]; then
args+=(--cache-from "${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-${{ vars.EDITION }}:release")
fi
docker build -t cicontainer "${args[@]}" .
# Saving the docker image to tar file
- name: Save Docker image to tar file

449
.github/workflows/ci-test-hosted.yml vendored Normal file
View File

@ -0,0 +1,449 @@
name: Appsmith CI Test Workflow For Hosted Instance
on:
# Schedule to run the workflow everyday at 7 AM or UTC (1.30 AM) only on weekday
schedule:
- cron: "30 1 * * 1-5"
# This line enables manual triggering of this workflow.
workflow_dispatch:
inputs:
pr:
description: "This is the PR number in case the workflow is being called in a pull request"
required: false
type: number
default: 0
workflow_call:
inputs:
pr:
description: "This is the PR number in case the workflow is being called in a pull request"
required: false
type: number
jobs:
ci-test:
runs-on: ubuntu-latest
if: |
github.event.pull_request.head.repo.full_name == github.repository ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
github.event_name == 'repository_dispatch' ||
github.event_name == 'schedule'
defaults:
run:
shell: bash
# Service containers to run with this job. Required for running tests
services:
# Label used to access the service container
redis:
# Docker Hub image for Redis
image: redis
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
mongo:
image: mongo
ports:
- 27017:27017
steps:
- name: Set up Depot CLI
uses: depot/setup-action@v1
# Check out merge commit
- name: Fork based /ok-to-test checkout
if: inputs.pr != 0
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: "refs/pull/${{ inputs.pr }}/merge"
# Checkout the code in the current branch in case the workflow is called because of a branch push event
- name: Checkout the head commit of the branch
if: inputs.pr == 0 || github.event_name == 'schedule'
uses: actions/checkout@v3
with:
fetch-depth: 0
# Timestamp will be used to create cache key
- id: timestamp
run: echo "::set-output name=timestamp::$(date +'%Y-%m-%dT%H:%M:%S')"
# In case this is second attempt try restoring status of the prior attempt from cache
- name: Restore the previous run result
id: cache-appsmith
uses: actions/cache@v3
with:
path: |
~/run_result
key: ${{ github.run_id }}-${{ github.job }}
restore-keys: |
${{ github.run_id }}-${{ github.job }}
- name: Get the previous run result
if: steps.cache-appsmith.outputs.cache-hit == 'true'
id: run_result
run: |
run_result_env=$(cat ~/run_result)
echo "run_result=$run_result_env" >> $GITHUB_OUTPUT
- name: Dump steps context
env:
STEPS_CONTEXT: ${{ toJson(steps) }}
run: echo "$STEPS_CONTEXT"
# In case this is second attempt try restoring failed tests
- name: Restore the previous failed combine result
if: steps.run_result.outputs.run_result == 'failedtest'
uses: actions/download-artifact@v3
with:
name: failed_spec_ci
path: ~/failed_spec_ci
# failed_spec_env will contain list of all failed specs
# We are using environment variable instead of regular to support multiline
- name: Get failed_spec
id: failed_spec
if: steps.run_result.outputs.run_result == 'failedtest'
working-directory: app/client
run: |
echo "failed_spec_env<<EOF" >> $GITHUB_ENV
while IFS= read -r line
do
spec_name=$(echo $line | awk -F'/' '{print $NF}')
failed_spec_env=$(find . -name $spec_name | sed 's|./||')
echo "$failed_spec_env" >> $GITHUB_ENV
done < ~/failed_spec_ci/failed_spec_ci
echo "EOF" >> $GITHUB_ENV
- if: steps.run_result.outputs.run_result != 'success' && steps.run_result.outputs.run_result != 'failedtest'
run: echo "Starting full run" && exit 0
- if: steps.run_result.outputs.run_result == 'failedtest'
run: echo "Rerunning failed tests" && exit 0
- name: cat run_result
run: echo ${{ steps.run_result.outputs.run_result }}
- name: Use Node.js 16.14.0
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-node@v3
with:
node-version: "16.14.0"
# actions/setup-node@v3 doesnt work properly with Yarn 3
# when the project lives in a subdirectory: https://github.com/actions/setup-node/issues/488
# Restoring the cache manually instead
- name: Restore Yarn cache
if: steps.run_result.outputs.run_result != 'success'
uses: actions/cache@v3
with:
path: app/client/.yarn/cache
key: v1-yarn3-${{ hashFiles('app/client/yarn.lock') }}
restore-keys: |
v1-yarn3-
# Install all the dependencies
- name: Install dependencies
if: steps.run_result.outputs.run_result != 'success'
working-directory: app/client
run: |
yarn install --immutable
- name: Setting up the cypress tests
if: steps.run_result.outputs.run_result != 'success'
shell: bash
env:
APPSMITH_SSL_CERTIFICATE: ${{ secrets.APPSMITH_SSL_CERTIFICATE }}
APPSMITH_SSL_KEY: ${{ secrets.APPSMITH_SSL_KEY }}
CYPRESS_URL: ${{ secrets.CYPRESS_URL }}
CYPRESS_USERNAME: ${{ secrets.CYPRESS_USERNAME }}
CYPRESS_PASSWORD: ${{ secrets.CYPRESS_PASSWORD }}
CYPRESS_TESTUSERNAME1: ${{ secrets.CYPRESS_TESTUSERNAME1 }}
CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }}
CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }}
CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }}
CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }}
CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }}
CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }}
CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }}
CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }}
CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }}
CYPRESS_GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
CYPRESS_AIRTABLE_BEARER: ${{ secrets.AIRTABLE_BEARER }}
CYPRESS_FIRESTORE_PRIVATE_KEY: ${{ secrets.FIRESTORE_PRIVATE_KEY }}
CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }}
CYPRESS_TEST_GITHUB_USER_NAME: ${{ secrets.CYPRESS_TEST_GITHUB_USER_NAME }}
CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET }}
CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET }}
CYPRESS_OAUTH_SAML_EMAIL: ${{ secrets.CYPRESS_OAUTH_SAML_EMAIL }}
CYPRESS_OAUTH_SAML_ENTITY_ID: ${{ secrets.CYPRESS_OAUTH_SAML_ENTITY_ID }}
CYPRESS_OAUTH_SAML_METADATA_URL: ${{ secrets.CYPRESS_OAUTH_SAML_METADATA_URL }}
CYPRESS_OAUTH_SAML_METADATA_XML: ${{ secrets.CYPRESS_OAUTH_SAML_METADATA_XML }}
CYPRESS_OAUTH_SAML_PUB_CERT: ${{ secrets.CYPRESS_OAUTH_SAML_PUB_CERT }}
CYPRESS_OAUTH_SAML_SSO_URL: ${{ secrets.CYPRESS_OAUTH_SAML_SSO_URL }}
CYPRESS_OAUTH_SAML_REDIRECT_URL: ${{ secrets.CYPRESS_OAUTH_SAML_REDIRECT_URL }}
CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_SECRET }}
CYPRESS_APPSMITH_OAUTH2_OIDC_AUTH_URL: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_AUTH_URL }}
CYPRESS_APPSMITH_OAUTH2_OIDC_TOKEN_URL: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_TOKEN_URL }}
CYPRESS_APPSMITH_OAUTH2_OIDC_USER_INFO: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_USER_INFO }}
CYPRESS_APPSMITH_OAUTH2_OIDC_JWKS_URL: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_JWKS_URL }}
CYPRESS_EXCLUDE_TAGS: "airgap"
CYPRESS_AIRGAPPED: false
APPSMITH_DISABLE_TELEMETRY: true
APPSMITH_GOOGLE_MAPS_API_KEY: ${{ secrets.APPSMITH_GOOGLE_MAPS_API_KEY }}
POSTGRES_PASSWORD: postgres
CYPRESS_VERIFY_TIMEOUT: 100000
run: |
cd app/client
chmod a+x ./cypress/setup-test-ci.sh
./cypress/setup-test-ci.sh
- uses: browser-actions/setup-chrome@latest
with:
chrome-version: stable
- run: |
echo "BROWSER_PATH=$(which chrome)" >> $GITHUB_ENV
# - name: Set Pull Request Title
# env:
# EVENT_COMMITS: ${{ toJson(github.event.commits[0].message) }}
# run: |
# echo "${{ env.EVENT_COMMITS }}" | awk -F '\\\\n' '{print $1}'
- name: Save Git values
# pass env variables from this step to other steps
# using GitHub Actions environment file
# https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#environment-files
run: |
PR_NUMBER=${{ inputs.pr }}
echo COMMIT_INFO_BRANCH=$(git rev-parse --abbrev-ref HEAD) >> $GITHUB_ENV
echo COMMIT_INFO_MESSAGE=OkToTest run on PR# ${{ inputs.pr }} >> $GITHUB_ENV
echo COMMIT_INFO_EMAIL=$(git show -s --pretty=%ae) >> $GITHUB_ENV
echo COMMIT_INFO_AUTHOR=$(git show -s --pretty=%an) >> $GITHUB_ENV
echo COMMIT_INFO_SHA=$(git show -s --pretty=%H) >> $GITHUB_ENV
echo COMMIT_INFO_TIMESTAMP=$(git show -s --pretty=%ct) >> $GITHUB_ENV
echo COMMIT_INFO_REMOTE=$(git config --get remote.origin.url) >> $GITHUB_ENV
# delete the .git folder afterwords to use the environment values
rm -rf .git
- name: Show Git values
run: |
echo Branch $COMMIT_INFO_BRANCH
echo Message $COMMIT_INFO_MESSAGE
echo Email $COMMIT_INFO_EMAIL
echo Author $COMMIT_INFO_AUTHOR
echo SHA $COMMIT_INFO_SHA
echo Timestamp $COMMIT_INFO_TIMESTAMP
echo Remote $COMMIT_INFO_REMOTE
- name: Set Commit Message
env:
EVENT_COMMITS: ${{ toJson(github.event.commits[0].message) }}
run: |
if [[ ${{ github.event_name }} == 'schedule' ]]; then
echo "COMMIT_INFO_MESSAGE=Scheduled workflow run for gsheet tests" >> $GITHUB_ENV
else
echo "COMMIT_INFO_MESSAGE=$(echo \"${{ env.EVENT_COMMITS }}\" | awk -F '\\\\n' '{print $1}' | sed 's/^\"//')" >> $GITHUB_ENV
fi
- name: Run the cypress test
if: steps.run_result.outputs.run_result != 'success' && steps.run_result.outputs.run_result != 'failedtest'
id: cypress_test
uses: cypress-io/github-action@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}
CYPRESS_USERNAME: ${{ secrets.CYPRESS_USERNAME }}
CYPRESS_PASSWORD: ${{ secrets.CYPRESS_PASSWORD }}
CYPRESS_TESTUSERNAME1: ${{ secrets.CYPRESS_TESTUSERNAME1 }}
CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }}
CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }}
CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }}
CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }}
CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }}
CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }}
CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }}
CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }}
CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }}
CYPRESS_GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
CYPRESS_AIRTABLE_BEARER: ${{ secrets.AIRTABLE_BEARER }}
CYPRESS_FIRESTORE_PRIVATE_KEY: ${{ secrets.FIRESTORE_PRIVATE_KEY }}
CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }}
CYPRESS_TEST_GITHUB_USER_NAME: ${{ secrets.CYPRESS_TEST_GITHUB_USER_NAME }}
CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET }}
CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET }}
CYPRESS_OAUTH_SAML_EMAIL: ${{ secrets.CYPRESS_OAUTH_SAML_EMAIL }}
CYPRESS_OAUTH_SAML_ENTITY_ID: ${{ secrets.CYPRESS_OAUTH_SAML_ENTITY_ID }}
CYPRESS_OAUTH_SAML_METADATA_URL: ${{ secrets.CYPRESS_OAUTH_SAML_METADATA_URL }}
CYPRESS_OAUTH_SAML_METADATA_XML: ${{ secrets.CYPRESS_OAUTH_SAML_METADATA_XML }}
CYPRESS_OAUTH_SAML_PUB_CERT: ${{ secrets.CYPRESS_OAUTH_SAML_PUB_CERT }}
CYPRESS_OAUTH_SAML_SSO_URL: ${{ secrets.CYPRESS_OAUTH_SAML_SSO_URL }}
CYPRESS_OAUTH_SAML_REDIRECT_URL: ${{ secrets.CYPRESS_OAUTH_SAML_REDIRECT_URL }}
CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_SECRET }}
CYPRESS_APPSMITH_OAUTH2_OIDC_AUTH_URL: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_AUTH_URL }}
CYPRESS_APPSMITH_OAUTH2_OIDC_TOKEN_URL: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_TOKEN_URL }}
CYPRESS_APPSMITH_OAUTH2_OIDC_USER_INFO: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_USER_INFO }}
CYPRESS_APPSMITH_OAUTH2_OIDC_JWKS_URL: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_JWKS_URL }}
CYPRESS_EXCLUDE_TAGS: "airgap"
CYPRESS_AIRGAPPED: false
APPSMITH_DISABLE_TELEMETRY: true
APPSMITH_GOOGLE_MAPS_API_KEY: ${{ secrets.APPSMITH_GOOGLE_MAPS_API_KEY }}
COMMIT_INFO_MESSAGE: ${{ env.COMMIT_INFO_MESSAGE }}
CYPRESS_VERIFY_TIMEOUT: 100000
with:
browser: ${{ env.BROWSER_PATH }}
record: true
install: false
parallel: true
config-file: cypress_ci_hosted.config.ts
group: "Chrome-Fat Container tests"
spec: "cypress/e2e/GSheet/**/**/*"
working-directory: app/client
# tag will be either "push" or "pull_request"
tag: ${{ github.event_name }}
env: "NODE_ENV=development"
# In case of second attempt only run failed specs
- name: Run the cypress test with failed tests
if: steps.run_result.outputs.run_result == 'failedtest'
id: cypress_test_failedtest
uses: cypress-io/github-action@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}
CYPRESS_USERNAME: ${{ secrets.CYPRESS_USERNAME }}
CYPRESS_PASSWORD: ${{ secrets.CYPRESS_PASSWORD }}
CYPRESS_TESTUSERNAME1: ${{ secrets.CYPRESS_TESTUSERNAME1 }}
CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }}
CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }}
CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }}
CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }}
CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }}
CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }}
CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }}
CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }}
CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }}
CYPRESS_GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
CYPRESS_AIRTABLE_BEARER: ${{ secrets.AIRTABLE_BEARER }}
CYPRESS_FIRESTORE_PRIVATE_KEY: ${{ secrets.FIRESTORE_PRIVATE_KEY }}
CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }}
CYPRESS_TEST_GITHUB_USER_NAME: ${{ secrets.CYPRESS_TEST_GITHUB_USER_NAME }}
CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET }}
CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET }}
CYPRESS_OAUTH_SAML_EMAIL: ${{ secrets.CYPRESS_OAUTH_SAML_EMAIL }}
CYPRESS_OAUTH_SAML_ENTITY_ID: ${{ secrets.CYPRESS_OAUTH_SAML_ENTITY_ID }}
CYPRESS_OAUTH_SAML_METADATA_URL: ${{ secrets.CYPRESS_OAUTH_SAML_METADATA_URL }}
CYPRESS_OAUTH_SAML_METADATA_XML: ${{ secrets.CYPRESS_OAUTH_SAML_METADATA_XML }}
CYPRESS_OAUTH_SAML_PUB_CERT: ${{ secrets.CYPRESS_OAUTH_SAML_PUB_CERT }}
CYPRESS_OAUTH_SAML_SSO_URL: ${{ secrets.CYPRESS_OAUTH_SAML_SSO_URL }}
CYPRESS_OAUTH_SAML_REDIRECT_URL: ${{ secrets.CYPRESS_OAUTH_SAML_REDIRECT_URL }}
CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_CLIENT_SECRET }}
CYPRESS_APPSMITH_OAUTH2_OIDC_AUTH_URL: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_AUTH_URL }}
CYPRESS_APPSMITH_OAUTH2_OIDC_TOKEN_URL: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_TOKEN_URL }}
CYPRESS_APPSMITH_OAUTH2_OIDC_USER_INFO: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_USER_INFO }}
CYPRESS_APPSMITH_OAUTH2_OIDC_JWKS_URL: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_OIDC_JWKS_URL }}
CYPRESS_EXCLUDE_TAGS: "airgap"
CYPRESS_AIRGAPPED: false
APPSMITH_DISABLE_TELEMETRY: true
APPSMITH_GOOGLE_MAPS_API_KEY: ${{ secrets.APPSMITH_GOOGLE_MAPS_API_KEY }}
COMMIT_INFO_MESSAGE: ${{ env.COMMIT_INFO_MESSAGE }}
with:
browser: ${{ env.BROWSER_PATH }}
record: true
install: false
parallel: true
config-file: cypress_ci_hosted.config.ts
group: "Chrome-Fat Container tests"
spec: ${{ env.failed_spec_env }}
working-directory: app/client
# tag will be either "push" or "pull_request"
tag: ${{ github.event_name }}
env: "NODE_ENV=development"
- name: Collect CI container logs
if: failure()
working-directory: "."
run: |
mkdir -p ~/dockerlogs
docker logs appsmith 2>&1 > ~/dockerlogs.txt
# Upload docker logs
- name: Upload failed test list artifact
if: failure()
uses: actions/upload-artifact@v3
with:
name: dockerlogs
path: ~/dockerlogs.txt
# Set status = failedtest
- name: Set fail if there are test failures
if: failure()
run: |
echo "run_result=failedtest" >> $GITHUB_OUTPUT
echo "failedtest" > ~/run_result
# add list failed tests to a file
- name: In case of test failures copy them to a file
if: failure()
run: |
cd ${{ github.workspace }}/app/client/cypress/
find screenshots -type f \( -iname "*\(attempt 2\).png" -o -iname "*before all hook*" -o -iname "*after all hook*" \) | sed 's/screenshots/cypress\/e2e/g'| sed 's:/[^/]*$::' | sort -u > ~/failed_spec_ci
# reset the failed_spec_ci file in case of success
- name: In case of test success reset the failed_spec_ci file
if: success()
run: |
touch ~/failed_spec_ci
# Upload failed test list using common path for all matrix job
- name: Upload failed test list artifact
if: always()
uses: actions/upload-artifact@v3
with:
name: failed_spec_ci
path: ~/failed_spec_ci
# Force store previous run result to cache
- name: Store the previous run result
if: failure()
uses: actions/cache/save@v3
with:
path: |
~/run_result
key: ${{ github.run_id }}-${{ github.job }}
- name: get cypress url dashboard url
id: dashboard_url
if: always()
run: |
if [[ "${{steps.run_result.outputs.run_result }}" != "success" && "${{steps.run_result.outputs.run_result }}" != "failedtest" ]]; then
echo ${{ steps.cypress_test.outputs.resultsUrl }} >> ~/cypress_url
elif [[ "${{steps.run_result.outputs.run_result }}" == "failedtest" ]]; then
echo ${{ steps.cypress_test_failedtest.outputs.resultsUrl }} >> ~/cypress_url
fi
# Upload the log artifact so that it can be used by the test & deploy job in the workflow
- name: Upload server logs bundle on failure
uses: actions/upload-artifact@v3
if: failure()
with:
name: server-logs-${{ matrix.job }}
path: app/server/server-logs.log
# Set status = success
- name: Save the status of the run
run: |
echo "run_result=success" >> $GITHUB_OUTPUT
echo "success" > ~/run_result

View File

@ -44,6 +44,12 @@ jobs:
- name: Set up Depot CLI
uses: depot/setup-action@v1
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
# Check out merge commit
- name: Fork based /ok-to-test checkout
if: inputs.pr != 0
@ -135,22 +141,52 @@ jobs:
run: |
mkdir -p cicontainerlocal/stacks/configuration/
- name: Run Appsmith & TED docker image
- name: Run Appsmith, CS & TED docker image
if: steps.run_result.outputs.run_result != 'success'
working-directory: "."
run: |
sudo /etc/init.d/ssh stop ;
mkdir -p ~/git-server/keys
mkdir -p ~/git-server/repos
docker run --name test-event-driver -d -p 22:22 -p 5001:5001 -p 3306:3306 \
-p 5432:5432 -p 28017:27017 -p 25:25 -p 5000:5000 -p 3001:3000 -p 6001:6001 --privileged --pid=host --ipc=host --volume /:/host -v ~/git-server/keys:/git-server/keys \
-v ~/git-server/repos:/git-server/repos appsmith/test-event-driver:latest
docker run --name test-event-driver -d \
-p 22:22 -p 5001:5001 -p 3306:3306 -p 5432:5432 -p 28017:27017 -p 25:25 -p 5000:5000 -p 3001:3000 -p 6001:6001 \
--privileged --pid=host --ipc=host --volume /:/host -v ~/git-server/keys:/git-server/keys \
-v ~/git-server/repos:/git-server/repos \
appsmith/test-event-driver:latest
docker run --name cloud-services -d -p 8000:80 -p 8090:8090 \
--privileged --pid=host --ipc=host --add-host=host.docker.internal:host-gateway\
-e APPSMITH_CLOUD_SERVICES_MONGODB_URI=mongodb://host.docker.internal:27017 \
-e APPSMITH_CLOUD_SERVICES_MONGODB_DATABASE=cs \
-e APPSMITH_CLOUD_SERVICES_MONGODB_AUTH_DATABASE=admin \
-e APPSMITH_REDIS_URL=redis://host.docker.internal:6379/ \
-e APPSMITH_APPS_API_KEY=dummy-api-key \
-e APPSMITH_REMOTE_API_KEY=dummy-api-key \
-e APPSMITH_GITHUB_API_KEY=dummy-appsmith-gh-api-key \
-e APPSMITH_JWT_SECRET=appsmith-cloud-services-jwt-secret-dummy-key \
-e APPSMITH_ENCRYPTION_SALT=encryption-salt \
-e APPSMITH_ENCRYPTION_PASSWORD=encryption-password \
-e APPSMITH_CLOUD_SERVICES_URL=https://cs-dev.appsmith.com \
-e APPSMITH_CUSTOMER_PORTAL_URL=https://dev.appsmith.com \
-e APPSMITH_CLOUD_SERVICES_BASE_URL=https://cs-dev.appsmith.com \
-e APPSMITH_CLOUD_SERVER_BASE_URL=https://release.app.appsmith.com \
-e AUTH0_ISSUER_URL=https://login.release-customer.appsmith.com/ \
-e AUTH0_CLIENT_ID=dummy-client-id \
-e AUTH0_CLIENT_SECRET=dummy-secret-id \
-e AUTH0_AUDIENCE_URL=https://login.local-customer.appsmith.com/ \
-e CLOUDSERVICES_URL=cs-dev.appsmith.com \
-e CUSTOMER_URL=dev.appsmith.com \
-e ENTERPRISE_USER_NAME=ent-user@appsmith.com \
-e ENTERPRISE_USER_PASSWORD=ent_user_password \
-e ENTERPRISE_ADMIN_NAME=ent-admin@appsmith.com \
-e ENTERPRISE_ADMIN_PASSWORD=ent_admin_password \
appsmith/cloud-services:release
cd cicontainerlocal
docker run -d --name appsmith -p 80:80 -p 9001:9001 \
-v "$PWD/stacks:/appsmith-stacks" \
-e APPSMITH_DISABLE_TELEMETRY=true \
-e APPSMITH_INTERCOM_APP_ID=DUMMY_VALUE \
-e APPSMITH_CLOUD_SERVICES_BASE_URL=http://host.docker.internal:5001 \
-e APPSMITH_CLOUD_SERVICES_SIGNATURE_BASE_URL=http://host.docker.internal:8090 \
--add-host=host.docker.internal:host-gateway --add-host=api.segment.io:host-gateway --add-host=t.appsmith.com:host-gateway \
cicontainer

View File

@ -1,8 +1,6 @@
name: Client Build
on:
# This line enables manual triggering of this workflow.
workflow_dispatch:
workflow_call:
inputs:
pr:
@ -19,12 +17,11 @@ on:
required: false
type: string
default: "false"
branch:
description: "This is the branch to be used for the build."
required: false
type: string
pull_request:
branches: [release, master]
paths:
- "app/client/**"
- "!app/client/cypress/manual_TestSuite/**"
# Change the working directory for all the jobs in this workflow
defaults:
@ -32,7 +29,7 @@ defaults:
working-directory: app/client
jobs:
build:
client-build:
runs-on: ubuntu-latest-8-cores
# Only run this workflow for internally triggered events
if: |
@ -58,10 +55,16 @@ jobs:
fetch-depth: 0
ref: refs/pull/${{ inputs.pr }}/merge
# Check out the specified branch in case this workflow is called by another workflow
- name: Checkout the specified branch
if: inputs.pr == 0 && inputs.branch != ''
uses: actions/checkout@v3
with:
ref: ${{ inputs.branch }}
# Checkout the code in the current branch in case the workflow is called because of a branch push event
- name: Checkout the head commit of the branch
if: inputs.pr == 0
if: inputs.pr == 0 && inputs.branch == ''
uses: actions/checkout@v3
with:
fetch-depth: 0

View File

@ -1,8 +1,6 @@
name: Client Lint
on:
# This line enables manual triggering of this workflow.
workflow_dispatch:
workflow_call:
inputs:
pr:
@ -15,26 +13,14 @@ on:
type: string
default: "false"
pull_request:
branches: [release, master]
paths:
- "app/client/**"
- "!app/client/cypress/manual_TestSuite/**"
# Change the working directory for all the jobs in this workflow
defaults:
run:
working-directory: app/client
jobs:
build:
client-lint:
runs-on: ubuntu-latest-8-cores
# Only run this workflow for internally triggered events
if: |
github.event.pull_request.head.repo.full_name == github.repository ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
github.event_name == 'repository_dispatch'
defaults:
run:
working-directory: app/client
@ -47,20 +33,11 @@ jobs:
# Check out merge commit with the base branch in case this workflow is invoked via pull request
- name: Checkout the merged commit from PR and base branch
if: inputs.pr != 0
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: refs/pull/${{ inputs.pr }}/merge
# Checkout the code in the current branch in case the workflow is called because of a branch push event
- name: Checkout the head commit of the branch
if: inputs.pr == 0
uses: actions/checkout@v3
with:
fetch-depth: 0
# In case this is second attempt try restoring status of the prior attempt from cache
- name: Restore the previous run result
uses: actions/cache@v3

View File

@ -1,8 +1,6 @@
name: Client Unit Tests
on:
# This line enables manual triggering of this workflow.
workflow_dispatch:
workflow_call:
inputs:
pr:
@ -15,26 +13,14 @@ on:
type: string
default: "false"
pull_request:
branches: [release, master]
paths:
- "app/client/**"
- "!app/client/cypress/manual_TestSuite/**"
# Change the working directory for all the jobs in this workflow
defaults:
run:
working-directory: app/client
jobs:
build:
client-unit-tests:
runs-on: ubuntu-latest-8-cores
# Only run this workflow for internally triggered events
if: |
github.event.pull_request.head.repo.full_name == github.repository ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
github.event_name == 'repository_dispatch'
defaults:
run:
working-directory: app/client
@ -47,20 +33,11 @@ jobs:
# Check out merge commit with the base branch in case this workflow is invoked via pull request
- name: Checkout the merged commit from PR and base branch
if: inputs.pr != 0
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: refs/pull/${{ inputs.pr }}/merge
# Checkout the code in the current branch in case the workflow is called because of a branch push event
- name: Checkout the head commit of the branch
if: inputs.pr == 0
uses: actions/checkout@v3
with:
fetch-depth: 0
# In case this is second attempt try restoring status of the prior attempt from cache
- name: Restore the previous run result
uses: actions/cache@v3

View File

@ -0,0 +1,53 @@
# Appsmith External Integration Test Workflow
## Workflow Triggers
This workflow is triggered by the "ok to test" command dispatch. It listens for the `repository_dispatch` event with the type `ok-to-test-command`.
## Workflow Jobs
### `notify`
This job is responsible for notifying about the ongoing test run. It creates a comment on the associated pull request with details about the workflow run, commit, and links to various test results.
### `server-build`
The `server-build` job builds the server-side code. It uses the configuration defined in the `.github/workflows/server-build.yml` file. The job is skipped for running tests.
### `client-build`
The `client-build` job builds the client-side codebase. It uses the configuration from the `.github/workflows/client-build.yml` file and checks for test files.
### `rts-build`
The `rts-build` job builds the "rts" (real-time suggestions) package of the client-side codebase. It uses the configuration defined in the `.github/workflows/rts-build.yml` file.
### `test-shared-modules`
The `test-shared-modules` job runs tests for shared modules. It uses the configuration from the `.github/workflows/shared-modules.yml` file.
### `test-appsmithctl`
The `test-appsmithctl` job runs tests for the `appsmithctl` utility. It uses the configuration from the `.github/workflows/appsmithctl.yml` file.
### `build-docker-image`
The `build-docker-image` job builds and pushes the Docker image for the application. It depends on the successful completion of the `client-build`, `server-build`, and `rts-build` jobs. The Docker image is built with support for both `linux/arm64` and `linux/amd64` platforms.
### `ci-test`
The `ci-test` job runs continuous integration tests on the Docker image. It depends on the successful completion of the `build-docker-image` job.
### `perf-test`
The `perf-test` job performs performance tests on the Docker image. It depends on the successful completion of the `build-docker-image` job.
### `ci-test-result`
The `ci-test-result` job collects the results from the `ci-test` and `perf-test` jobs and generates a report. It always runs, and it processes the test results against known failures. The results are then added as a comment on the associated pull request.
### `package`
The `package` job creates a package for release. It runs only if all previous steps are successful and the reference is either the `release` or `master` branch. The package result is then marked as complete.
Please note that this documentation is based on the information provided in the workflow file. For any specific details and further customization, refer to the actual content of the included `.yml` files for each job.

View File

@ -0,0 +1,60 @@
# GitHub Workflow Documentation: Quality Checks
## Overview
The "Quality checks" GitHub workflow is designed to perform various quality checks on your codebase, particularly when there's a pull request targeting the "release" or "master" branches. The workflow makes use of multiple jobs, each focusing on a specific quality check task.
## Workflow Triggers
The workflow is triggered automatically when a pull request is opened or updated, but only for pull requests targeting the "release" or "master" branches.
```yaml
on:
pull_request:
branches: [release, master]
```
## Workflow Jobs
### `path-filter`
The first job, named `path-filter`, filters the files changed in the pull request to determine which quality checks are applicable. It identifies files related to the server, client, and Cypress tests and sets appropriate output variables accordingly.
Steps:
1. Checks out the merged commit from the pull request and the base branch.
2. Uses the `dorny/paths-filter` action to filter files and set output variables for "server," "client," and "cypress" changes.
### `server-spotless`
This job, named `server-spotless`, performs a code formatting check on the server-side codebase. It only runs if changes related to the server were detected by the `path-filter` job.
### `server-unit-tests`
The `server-unit-tests` job is responsible for running unit tests on the server-side code. It runs if changes related to the server were detected by the `path-filter` job.
### `client-build`
The `client-build` job handles the build process for the client-side codebase. It runs if changes related to the client were detected by the `path-filter` job.
### `client-prettier`
The `client-prettier` job performs code formatting checks on the client-side codebase using Prettier. It runs if changes related to the client were detected by the `path-filter` job.
### `client-unit-tests`
The `client-unit-tests` job runs unit tests for the client-side codebase. It runs if changes related to the client were detected by the `path-filter` job.
### `client-lint`
The `client-lint` job checks the client-side code for linting issues. It runs if changes related to the client were detected by the `path-filter` job.
### `qc-result`
The final job, `qc-result`, collects the results from all the previous jobs and determines whether the quality checks passed or failed. It always runs, regardless of the results of the previous jobs.
Steps:
1. Uses a Bash script to check the results of each job.
2. If any of the previous jobs' results are "failure," the script echoes "Quality checks failed" and exits with an error code (1).
3. In any other non-failing scenario (skipped steps or successful steps), the script echoes "Quality checks successful" and exits with a success code (0).

View File

@ -0,0 +1,60 @@
# Test, build and push Docker Image
This GitHub workflow is triggered manually (`workflow_dispatch`) and automatically for pushes to the `release` and `master` branches. It is designed to perform various steps, including building and pushing Docker images, running tests, and creating release images.
## Workflow Triggers
The workflow can be manually triggered via the GitHub Actions UI by selecting "Run Workflow" from the actions dropdown. Additionally, it is triggered automatically for pushes to the `release` and `master` branches.
```yaml
on:
workflow_dispatch:
push:
branches: [release, master]
paths:
- "app/client/**"
- "app/server/**"
- "app/client/packages/rts/**"
- "!app/client/cypress/manual_TestSuite/**"
```
## Workflow Jobs
### `server-build`
This job, named `server-build`, is responsible for building the server-side code. It uses the configuration defined in `.github/workflows/server-build.yml`. It inherits secrets from the repository.
### `client-build`
The `client-build` job builds the client-side codebase. It uses the configuration from `.github/workflows/client-build.yml` and inherits secrets from the repository.
### `rts-build`
The `rts-build` job builds the "rts" (real-time suggestions) package of the client-side codebase. It uses the configuration defined in `.github/workflows/rts-build.yml` and inherits secrets from the repository.
### `build-docker-image`
This job, named `build-docker-image`, creates and pushes the Docker image for the application. It is dependent on the success of the `client-build`, `server-build`, and `rts-build` jobs. The Docker image is built with two platforms: `linux/arm64` and `linux/amd64`.
### `perf-test`
The `perf-test` job performs performance tests on the Docker image. It depends on the successful completion of the `build-docker-image` job.
### `ci-test`
The `ci-test` job runs continuous integration tests on the Docker image. It depends on the successful completion of the `build-docker-image` job.
### `ci-test-result`
The `ci-test-result` job collects the results from the `ci-test` job and determines whether the integration tests have passed or failed. It always runs, and it is triggered under the following conditions:
- Manual workflow dispatch.
- Push event to the repository.
- Pull request review event with an approved review and originating from the same repository.
### `package-release`
The `package-release` job is responsible for creating a Docker image for release. It runs as soon as the docker image is ready, but only for the `release` branch. It uses OIDC token authentication between Depot and GitHub.
### `package-master`
The `package-master` job creates a Docker image for the `master` branch. It runs only if the tests are successful and is used for nightly builds. It uses OIDC token authentication between Depot and GitHub.

View File

@ -1,64 +0,0 @@
name: Formatting checks
on:
pull_request:
branches: [release, master]
jobs:
path-filter:
runs-on: ubuntu-latest
outputs:
server: ${{ steps.filter.outputs.server }}
client: ${{ steps.filter.outputs.client }}
steps:
# Check out merge commit with the base branch in case this workflow is invoked via pull request
- name: Checkout the merged commit from PR and base branch
uses: actions/checkout@v3
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
server:
- 'app/server/**'
client:
- 'app/client/**'
server-formatting:
name: server-formatting
needs: path-filter
if: needs.path-filter.outputs.server == 'true'
uses: ./.github/workflows/server-spotless.yml
secrets: inherit
with:
pr: ${{ github.event.pull_request.number }}
client-formatting:
name: client-formatting
needs: path-filter
if: needs.path-filter.outputs.client == 'true'
uses: ./.github/workflows/client-prettier.yml
secrets: inherit
with:
pr: ${{ github.event.pull_request.number }}
formatting-result:
name: formatting-result
needs: [server-formatting, client-formatting]
if: always()
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- name: Return status for formatting checks
run: |
if [[ "${{ needs.server-formatting.result }}" == "failure" || "${{ needs.client-formatting.result }}" == "failure" ]]; then
echo "Formatting checks failed";
exit 1;
else
echo "Formatting checks successful";
exit 0;
fi

View File

@ -20,35 +20,14 @@ jobs:
Commit: `${{ github.event.client_payload.slash_command.args.named.sha }}`.
PR: ${{ github.event.client_payload.pull_request.number }}.
Perf tests will be available at <https://app.appsmith.com/app/performance-infra-dashboard/pr-details-638dd7cd2913ba43778b915e?pr=${{ github.event.client_payload.pull_request.number }}&runId=${{ github.run_id }}_${{github.run_attempt}}>
changeset:
runs-on: ubuntu-latest
# Required permissions
permissions:
pull-requests: read
# Set job outputs to values from filter step
outputs:
backend: ${{ steps.filter.outputs.backend }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: dorny/paths-filter@v2
id: filter
with:
base: ${{ github.event.client_payload.pull_request.base.ref }}
ref: ${{ github.event.client_payload.pull_request.head.sha }}
filters: |
backend:
- 'app/server/**'
server-build:
name: server-build
needs: [changeset]
uses: ./.github/workflows/server-build.yml
secrets: inherit
with:
pr: ${{ github.event.client_payload.pull_request.number }}
skip-tests: ${{ needs.changeset.outputs.backend == 'false' }}
skip-tests: true
client-build:
name: client-build
@ -158,12 +137,18 @@ jobs:
path: ~/failed_spec_ci
# In case for any ci job failure, create combined failed spec
- name: "combine all specs for CI"
- name: combine all specs for CI
id: combine_ci
if: needs.ci-test.result != 'success'
run: |
echo "Debugging: failed specs in ~/failed_spec_ci/failed_spec_ci*"
cat ~/failed_spec_ci/failed_spec_ci*
cat ~/failed_spec_ci/failed_spec_ci* | sort -u >> ~/combined_failed_spec_ci
if [[ -z $(grep '[^[:space:]]' ~/combined_failed_spec_ci) ]] ; then
echo "specs_failed=0" >> $GITHUB_OUTPUT
else
echo "specs_failed=1" >> $GITHUB_OUTPUT
fi
# Upload combined failed CI spec list to a file
# This is done for debugging.
@ -191,7 +176,7 @@ jobs:
echo "EOF" >> $GITHUB_ENV
- name: Add a comment on the PR with new CI failures
if: needs.ci-test.result != 'success'
if: needs.ci-test.result != 'success' && steps.combine_ci.outputs.specs_failed == '1'
uses: peter-evans/create-or-update-comment@v1
with:
issue-number: ${{ github.event.client_payload.pull_request.number }}
@ -203,7 +188,7 @@ jobs:
To know the list of identified flaky tests - <a href="https://app.appsmith.com/applications/613868bedd7786286ddd4a6a/pages/63ec710e8e503f763651791a" target="_blank">Refer here</a>
- name: Add a comment on the PR when ci-test is success
if: needs.ci-test.result == 'success'
if: needs.ci-test.result == 'success' || steps.combine_ci.outputs.specs_failed == '0'
uses: peter-evans/create-or-update-comment@v1
with:
issue-number: ${{ github.event.client_payload.pull_request.number }}

View File

@ -90,17 +90,28 @@ jobs:
run: vercel build --yes --token=${{ secrets.VERCEL_TOKEN }}
- name: Deploy Project Artifacts to Vercel
id: set-dpurl
run: |
vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} >> ~/run_result.txt
echo "::set-output name=dpurl::$(cat ~/run_result.txt)"
vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} | tee -a ~/run_result.txt
- name: vercel-notify
uses: peter-evans/create-or-update-comment@v2
- uses: actions/github-script@v6
with:
issue-number: ${{ github.event.client_payload.pull_request.number }}
body: |
Deploy-Preview-URL: ${{ steps.set-dpurl.outputs.dpurl }}
script: |
const dpUrl = require("fs").readFileSync(process.env.HOME + "/run_result.txt", "utf8")
const bodyLines = ["Deploy-Preview-URL: " + dpUrl]
if (context.repo.repo === "appsmith") {
bodyLines.push(
"",
"🚨 *Note*: The release environment runs EE code, so using a frontend-only DP on this repo, will",
"likely behave unexpectedly. Consider using a full DP instead.",
"[Learn more](https://notion.so/031b87bce3404e3a95240d4c14c82e46).",
)
}
github.rest.issues.createComment({
issue_number: context.payload.pull_request.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: bodyLines.join("\n"),
})
push-image:

107
.github/workflows/quality-checks.yml vendored Normal file
View File

@ -0,0 +1,107 @@
name: Quality checks
on:
pull_request:
branches: [release, master]
jobs:
path-filter:
runs-on: ubuntu-latest
outputs:
server: ${{ steps.filter.outputs.server }}
client: ${{ steps.filter.outputs.client == 'true' && steps.filter.outputs.not-cypress-manual == 'true' }}
steps:
# Check out merge commit with the base branch in case this workflow is invoked via pull request
- name: Checkout the merged commit from PR and base branch
uses: actions/checkout@v3
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
server:
- 'app/server/**'
client:
- 'app/client/**'
not-cypress-manual:
- '!app/client/cypress/manual_TestSuite/**'
server-spotless:
name: server-spotless
needs: path-filter
if: needs.path-filter.outputs.server == 'true'
uses: ./.github/workflows/server-spotless.yml
secrets: inherit
with:
pr: ${{ github.event.pull_request.number }}
server-unit-tests:
name: server-unit-tests
needs: path-filter
if: needs.path-filter.outputs.server == 'true'
uses: ./.github/workflows/server-build.yml
secrets: inherit
with:
pr: ${{ github.event.pull_request.number }}
client-build:
name: client-build
needs: path-filter
if: needs.path-filter.outputs.client == 'true'
uses: ./.github/workflows/client-build.yml
secrets: inherit
with:
pr: ${{ github.event.pull_request.number }}
client-prettier:
name: client-prettier
needs: path-filter
if: needs.path-filter.outputs.client == 'true'
uses: ./.github/workflows/client-prettier.yml
secrets: inherit
with:
pr: ${{ github.event.pull_request.number }}
client-unit-tests:
name: client-unit-tests
needs: path-filter
if: needs.path-filter.outputs.client == 'true'
uses: ./.github/workflows/client-unit-tests.yml
secrets: inherit
with:
pr: ${{ github.event.pull_request.number }}
client-lint:
name: client-lint
needs: path-filter
if: needs.path-filter.outputs.client == 'true'
uses: ./.github/workflows/client-lint.yml
secrets: inherit
with:
pr: ${{ github.event.pull_request.number }}
qc-result:
name: qc-result
needs: [server-spotless, server-unit-tests, client-build, client-prettier, client-unit-tests, client-lint]
if: always()
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- name: Return status for quality checks
run: |
if [[ "${{ needs.server-spotless.result }}" == "failure" || \
"${{ needs.server-unit-tests.result }}" == "failure" || \
"${{ needs.client-build.result }}" == "failure" || \
"${{ needs.client-prettier.result }}" == "failure" || \
"${{ needs.client-unit-tests.result }}" == "failure" || \
"${{ needs.client-lint.result }}" == "failure" ]]; then
echo "Quality checks failed";
exit 1;
else
echo "Quality checks successful";
exit 0;
fi

View File

@ -10,6 +10,10 @@ on:
description: "This is the PR number in case the workflow is being called in a pull request"
required: false
type: number
branch:
description: "This is the branch to be used for the build."
required: false
type: string
pull_request:
branches: [release, master]
@ -44,9 +48,17 @@ jobs:
fetch-depth: 0
ref: refs/pull/${{ inputs.pr }}/merge
# Check out the specified branch in case this workflow is called by another workflow
- name: Checkout the specified branch
if: inputs.pr == 0 && inputs.branch != ''
uses: actions/checkout@v3
with:
ref: ${{ inputs.branch }}
# Checkout the code in the current branch in case the workflow is called because of a branch push event
- name: Checkout the head commit of the branch
if: inputs.pr == 0
if: inputs.pr == 0 && inputs.branch == ''
uses: actions/checkout@v3
with:
fetch-depth: 0

View File

@ -2,8 +2,6 @@
name: Appsmith Server Workflow
on:
# This line enables manual triggering of this workflow.
workflow_dispatch:
workflow_call:
inputs:
pr:
@ -15,12 +13,10 @@ on:
required: false
type: string
default: "false"
pull_request:
branches: [release, master]
paths:
- "app/server/**"
branch:
description: "This is the branch to be used for the build."
required: false
type: string
# Change the working directory for all the jobs in this workflow
defaults:
@ -28,14 +24,8 @@ defaults:
working-directory: app/server
jobs:
build:
server-unit-tests:
runs-on: ubuntu-latest-8-cores
# Only run this workflow for internally triggered events
if: |
github.event.pull_request.head.repo.full_name == github.repository ||
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
github.event_name == 'repository_dispatch'
# Service containers to run with this job. Required for running tests
services:
@ -60,10 +50,17 @@ jobs:
fetch-depth: 0
ref: refs/pull/${{ inputs.pr }}/merge
# Check out the specified branch in case this workflow is called by another workflow
- name: Checkout the specified branch
if: inputs.pr == 0 && inputs.branch != ''
uses: actions/checkout@v3
with:
ref: ${{ inputs.branch }}
# Checkout the code in the current branch in case the workflow is called because of a branch push event
- name: Check out the head commit of the branch
uses: actions/checkout@v3
if: inputs.pr == 0
if: inputs.pr == 0 && inputs.branch == ''
with:
fetch-depth: 0

View File

@ -135,37 +135,19 @@ jobs:
exit 1;
fi
package:
needs: ci-test
package-release:
needs: build-docker-image
runs-on: ubuntu-latest
# Set permissions since we're using OIDC token authentication between Depot and GitHub
permissions:
contents: read
id-token: write
# Run this job irrespective of tests failing, if this is the release branch; or only if the tests pass, if this is the master branch.
if: (success() && github.ref == 'refs/heads/master') ||
( always() &&
(
github.event_name == 'workflow_dispatch' ||
github.event_name == 'push' ||
(
github.event_name == 'pull_request_review' &&
github.event.review.state == 'approved' &&
github.event.pull_request.head.repo.full_name == github.repository
)
) &&
github.ref == 'refs/heads/release'
)
# Run this job as soon as the docker image is ready, if this is the release branch
if: ( always() && github.ref == 'refs/heads/release' )
steps:
# Checkout the code
- name: Checkout the merged commit from PR and base branch
if: github.event_name == 'pull_request_review'
uses: actions/checkout@v3
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: Checkout the head commit of the branch
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
@ -200,12 +182,6 @@ jobs:
echo "Cleaning up the tar files"
rm app/client/packages/rts/dist/rts-dist.tar
# Here, the GITHUB_REF is of type /refs/head/<branch_name>. We extract branch_name from this by removing the
# first 11 characters. This can be used to build images for several branches
- name: Get the version to tag the Docker image
id: vars
run: echo tag=$(echo ${GITHUB_REF:11}) >> $GITHUB_OUTPUT
- name: Set up Depot CLI
uses: depot/setup-action@v1
@ -216,7 +192,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and push release image to Docker Hub
if: success() && github.ref == 'refs/heads/release' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
if: success()
uses: depot/build-push-action@v1
with:
context: .
@ -225,10 +201,65 @@ jobs:
build-args: |
APPSMITH_SEGMENT_CE_KEY=${{ secrets.APPSMITH_SEGMENT_CE_KEY_RELEASE }}
tags: |
${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:${{steps.vars.outputs.tag}}
${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:release
package-master:
needs: ci-test
runs-on: ubuntu-latest
# Set permissions since we're using OIDC token authentication between Depot and GitHub
permissions:
contents: read
id-token: write
# Run this job irrespective of tests failing, if this is the release branch; or only if the tests pass, if this is the master branch.
if: ( success() && github.ref == 'refs/heads/master' )
steps:
- name: Checkout the head commit of the branch
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
- name: Download the react build artifact
uses: actions/download-artifact@v3
with:
name: client-build
path: app/client
- name: Unpack the client build artifact
if: steps.run_result.outputs.run_result != 'success'
run: |
mkdir -p app/client/build
tar -xvf app/client/build.tar -C app/client/build
- name: Download the server build artifact
uses: actions/download-artifact@v3
with:
name: server-build
path: app/server/dist
- name: Download the rts build artifact
uses: actions/download-artifact@v3
with:
name: rts-dist
path: app/client/packages/rts/dist
- name: Untar the rts folder
run: |
tar -xvf app/client/packages/rts/dist/rts-dist.tar -C app/client/packages/rts/
echo "Cleaning up the tar files"
rm app/client/packages/rts/dist/rts-dist.tar
- name: Set up Depot CLI
uses: depot/setup-action@v1
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and push master image to Docker Hub with commit tag
if: success() && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
if: success()
uses: depot/build-push-action@v1
with:
context: .
@ -238,26 +269,4 @@ jobs:
APPSMITH_SEGMENT_CE_KEY=${{ secrets.APPSMITH_SEGMENT_CE_KEY }}
tags: |
${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:${{ github.sha }}
${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:nightly
# - name: Check and push CI image to Docker Hub with commit tag
# if: success() && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/release') && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
# working-directory: "."
# run: |
# if [[ "${{ github.ref }}" == "refs/heads/master" ]]; then
# tag=nightly
# else
# tag="${{ steps.vars.outputs.tag }}"
# fi
# docker run --detach --publish 80:80 --name appsmith \
# "${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:$tag"
# sleep 180
# cd deploy/docker
# if bash run-test.sh; then
# echo "Fat container test passed. Pushing image."
# docker push --all-tags ${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-ce
# else
# echo "Fat container test FAILED. Not pushing image."
# # Temporarily pushing even if test fails.
# docker push --all-tags ${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-ce
# fi
${{ vars.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:nightly

View File

@ -27,7 +27,7 @@ RUN curl --silent --show-error --location https://www.mongodb.org/static/pgp/ser
&& echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-5.0.list \
&& curl --silent --show-error --location https://deb.nodesource.com/setup_16.x | bash - \
&& echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list \
&& curl --silent --show-error --location https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \
&& curl --silent --show-error --location https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \
&& apt update \
&& apt-get install --no-install-recommends --yes mongodb-org=5.0.14 nodejs redis build-essential postgresql-13 \
&& apt-get clean \
@ -51,6 +51,10 @@ VOLUME [ "/appsmith-stacks" ]
# Add backend server - Application Layer
ARG JAR_FILE=./app/server/dist/server-*.jar
ARG PLUGIN_JARS=./app/server/dist/plugins/*.jar
ARG APPSMITH_CLOUD_SERVICES_BASE_URL
ENV APPSMITH_CLOUD_SERVICES_BASE_URL=${APPSMITH_CLOUD_SERVICES_BASE_URL}
ARG APPSMITH_SEGMENT_CE_KEY
ENV APPSMITH_SEGMENT_CE_KEY=${APPSMITH_SEGMENT_CE_KEY}
#Create the plugins directory

View File

@ -182,8 +182,8 @@ Lets build great software together.
[![aswathkk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/10436935?v=4&w=50&h=50&mask=circle)](https://github.com/aswathkk)
[![sbalaji1192](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5328605?v=4&w=50&h=50&mask=circle)](https://github.com/sbalaji1192)
[![SatishGandham](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/441914?v=4&w=50&h=50&mask=circle)](https://github.com/SatishGandham)
[![rahulramesha](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/71900764?v=4&w=50&h=50&mask=circle)](https://github.com/rahulramesha)
[![sarojsarab](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/43822041?v=4&w=50&h=50&mask=circle)](https://github.com/sarojsarab)
[![rahulramesha](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/71900764?v=4&w=50&h=50&mask=circle)](https://github.com/rahulramesha)
[![prsidhu](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5424788?v=4&w=50&h=50&mask=circle)](https://github.com/prsidhu)
[![yatinappsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/84702014?v=4&w=50&h=50&mask=circle)](https://github.com/yatinappsmith)
[![somangshu](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11089579?v=4&w=50&h=50&mask=circle)](https://github.com/somangshu)
@ -191,26 +191,26 @@ Lets build great software together.
[![pranavkanade](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13262095?v=4&w=50&h=50&mask=circle)](https://github.com/pranavkanade)
[![AmanAgarwal041](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7565635?v=4&w=50&h=50&mask=circle)](https://github.com/AmanAgarwal041)
[![Parthvi12](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/80334441?v=4&w=50&h=50&mask=circle)](https://github.com/Parthvi12)
[![albinAppsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/87797149?v=4&w=50&h=50&mask=circle)](https://github.com/albinAppsmith)
[![ayushpahwa](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8526215?v=4&w=50&h=50&mask=circle)](https://github.com/ayushpahwa)
[![albinAppsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/87797149?v=4&w=50&h=50&mask=circle)](https://github.com/albinAppsmith)
[![marks0351](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35134347?v=4&w=50&h=50&mask=circle)](https://github.com/marks0351)
[![berzerkeer](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/74818788?v=4&w=50&h=50&mask=circle)](https://github.com/berzerkeer)
[![sneha122](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30018882?v=4&w=50&h=50&mask=circle)](https://github.com/sneha122)
[![pratapaprasanna](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/15846947?v=4&w=50&h=50&mask=circle)](https://github.com/pratapaprasanna)
[![ashit-rath](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/88306433?v=4&w=50&h=50&mask=circle)](https://github.com/ashit-rath)
[![keyurparalkar](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/14138515?v=4&w=50&h=50&mask=circle)](https://github.com/keyurparalkar)
[![pratapaprasanna](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/15846947?v=4&w=50&h=50&mask=circle)](https://github.com/pratapaprasanna)
[![Vijetha-Kaja](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/119562824?v=4&w=50&h=50&mask=circle)](https://github.com/Vijetha-Kaja)
[![ChandanBalajiBP](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/104058110?v=4&w=50&h=50&mask=circle)](https://github.com/ChandanBalajiBP)
[![vishnu-gp](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/9128194?v=4&w=50&h=50&mask=circle)](https://github.com/vishnu-gp)
[![areyabhishek](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30255708?v=4&w=50&h=50&mask=circle)](https://github.com/areyabhishek)
[![nsarupr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20905988?v=4&w=50&h=50&mask=circle)](https://github.com/nsarupr)
[![dhruvikn](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/22471214?v=4&w=50&h=50&mask=circle)](https://github.com/dhruvikn)
[![sum35h](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20785806?v=4&w=50&h=50&mask=circle)](https://github.com/sum35h)
[![sondermanish](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/107841575?v=4&w=50&h=50&mask=circle)](https://github.com/sondermanish)
[![sum35h](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20785806?v=4&w=50&h=50&mask=circle)](https://github.com/sum35h)
[![dhruvikn](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/22471214?v=4&w=50&h=50&mask=circle)](https://github.com/dhruvikn)
[![NilanshBansal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25542733?v=4&w=50&h=50&mask=circle)](https://github.com/NilanshBansal)
[![rajatagrawal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1189106?v=4&w=50&h=50&mask=circle)](https://github.com/rajatagrawal)
[![megaconfidence](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17744578?v=4&w=50&h=50&mask=circle)](https://github.com/megaconfidence)
[![tanvibhakta](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13763558?v=4&w=50&h=50&mask=circle)](https://github.com/tanvibhakta)
[![rajatagrawal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1189106?v=4&w=50&h=50&mask=circle)](https://github.com/rajatagrawal)
[![subrata71](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3524599?v=4&w=50&h=50&mask=circle)](https://github.com/subrata71)
[![Druthi](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20187542?v=4&w=50&h=50&mask=circle)](https://github.com/Druthi)
[![ichik](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/80973?v=4&w=50&h=50&mask=circle)](https://github.com/ichik)
@ -218,17 +218,17 @@ Lets build great software together.
[![vsvamsi1](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/121419957?v=4&w=50&h=50&mask=circle)](https://github.com/vsvamsi1)
[![rahulbarwal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/6761673?v=4&w=50&h=50&mask=circle)](https://github.com/rahulbarwal)
[![ankitsrivas14](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/67647761?v=4&w=50&h=50&mask=circle)](https://github.com/ankitsrivas14)
[![tkAppsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/131347120?v=4&w=50&h=50&mask=circle)](https://github.com/tkAppsmith)
[![dipyamanbiswas07](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/26247571?v=4&w=50&h=50&mask=circle)](https://github.com/dipyamanbiswas07)
[![rohitagarwal88](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/890915?v=4&w=50&h=50&mask=circle)](https://github.com/rohitagarwal88)
[![tkAppsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/131347120?v=4&w=50&h=50&mask=circle)](https://github.com/tkAppsmith)
[![brayn003](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8724051?v=4&w=50&h=50&mask=circle)](https://github.com/brayn003)
[![ramsaptami](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/79509062?v=4&w=50&h=50&mask=circle)](https://github.com/ramsaptami)
[![Pranay105](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48308728?v=4&w=50&h=50&mask=circle)](https://github.com/Pranay105)
[![gitstart-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/114667275?v=4&w=50&h=50&mask=circle)](https://github.com/gitstart-appsmith)
[![rohan-arthur](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/94514895?v=4&w=50&h=50&mask=circle)](https://github.com/rohan-arthur)
[![sharanya-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/135708039?v=4&w=50&h=50&mask=circle)](https://github.com/sharanya-appsmith)
[![vivonk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25587962?v=4&w=50&h=50&mask=circle)](https://github.com/vivonk)
[![kocharrahul7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20532920?v=4&w=50&h=50&mask=circle)](https://github.com/kocharrahul7)
[![sharanya-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/135708039?v=4&w=50&h=50&mask=circle)](https://github.com/sharanya-appsmith)
[![abm17](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13333024?v=4&w=50&h=50&mask=circle)](https://github.com/abm17)
[![jacquesikot](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/40626453?v=4&w=50&h=50&mask=circle)](https://github.com/jacquesikot)
[![RakshaKShetty](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/45958978?v=4&w=50&h=50&mask=circle)](https://github.com/RakshaKShetty)

View File

@ -0,0 +1,300 @@
/// <reference types="Cypress" />
import { GSHEET_DATA } from "../../fixtures/test-data-gsheet";
import {
homePage,
gsheetHelper,
dataSources,
agHelper,
entityExplorer,
} from "../../support/Objects/ObjectsCore";
const workspaceName = "gsheet apps";
const dataSourceName = "gsheet";
let appName = "gsheet-app";
let spreadSheetName = "test-sheet";
describe("GSheet-Functional Tests With All Access", function () {
before("Setup app and spreadsheet", function () {
//Add a new app and an add new spreadsheet query
//Setting up the spreadsheet name
const uuid = Cypress._.random(0, 10000);
spreadSheetName = spreadSheetName + "_" + uuid;
appName = appName + "-" + uuid;
//Adding query to insert a new spreadsheet
homePage.NavigateToHome();
homePage.CreateAppInWorkspace(workspaceName);
homePage.RenameApplication(appName);
gsheetHelper.AddNewSpreadsheetQuery(
dataSourceName,
spreadSheetName,
JSON.stringify([GSHEET_DATA[0]]),
);
cy.get("@postExecute").then((interception: any) => {
expect(
interception.response.body.data.body.properties.title,
).to.deep.equal(spreadSheetName);
});
});
it("1. Add and verify fetch details query", () => {
entityExplorer.CreateNewDsQuery(dataSourceName);
agHelper.RenameWithInPane("Fetch_Details");
dataSources.ValidateNSelectDropdown(
"Operation",
"Fetch Many",
"Fetch Details",
);
dataSources.ValidateNSelectDropdown("Entity", "Spreadsheet");
agHelper.Sleep(500);
dataSources.ValidateNSelectDropdown("Spreadsheet", "", spreadSheetName);
dataSources.RunQuery();
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.name).to.deep.equal(
spreadSheetName,
);
});
});
it("2. Verify Insert one and Insert many queries", () => {
// add insert one query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Insert One",
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA[1]),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Inserted row successfully!",
);
});
// add insert many query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Insert Many",
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA.slice(2, 10)),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Inserted rows successfully!",
);
});
});
it("3. Verify Update one and Update many queries", () => {
// add update one query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Update One",
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA[1]),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Updated sheet successfully!",
);
});
// add update many query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Update Many",
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA.slice(2, 4)),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Updated sheet successfully!",
);
});
});
it("4. Verify Fetch many query", () => {
// Add simple Fetch many query and verify
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName,
spreadSheetName,
);
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, GSHEET_DATA[0].uniq_id);
dataSources.AssertQueryTableResponse(1, "ホーンビィ 2014 カタログ"); // Asserting other language
dataSources.AssertQueryTableResponse(2, "₹, $, €, ¥, £"); // Asserting different symbols
dataSources.AssertQueryTableResponse(3, "!@#$%^&*"); // Asserting special chars
// Update query to fetch only 1 column and verify
gsheetHelper.SelectMultiDropDownValue("Columns", "product_name");
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, GSHEET_DATA[0].product_name);
//Remove column filter and add Sort By Ascending and verify
gsheetHelper.SelectMultiDropDownValue("Columns", "product_name"); //unselect the Columns dd value
agHelper.EnterValue("price", {
propFieldName: "",
directInput: false,
inputFieldName: "Sort By",
});
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, "5afbaf65680c9f378af5b3a3ae22427e");
dataSources.AssertQueryTableResponse(
1,
"ラーニング カーブ チャギントン インタラクティブ チャッツワース",
); // Asserting other language
dataSources.AssertQueryTableResponse(2, "₹, $, €, ¥, £"); // Asserting different symbols
dataSources.AssertQueryTableResponse(3, "!@#$%^&*"); // Asserting special chars
// Sort by descending and verify
dataSources.ClearSortByOption(); //clearing previous sort option
dataSources.EnterSortByValues("price", "Descending");
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(
1,
"ホーンビー ゲージ ウェスタン エクスプレス デジタル トレイン セット (eLink および TTS ロコ トレイン セット付き)",
); // Asserting other language
dataSources.AssertQueryTableResponse(
4,
"Hornby Gauge Western Express Digital Train Set with eLink and TTS Loco Train Set",
);
// Filter by where clause and verify
agHelper.TypeDynamicInputValueNValidate(
"price",
dataSources._nestedWhereClauseKey(0),
);
agHelper.TypeDynamicInputValueNValidate(
"100",
dataSources._nestedWhereClauseValue(0),
);
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(8);
dataSources.AssertQueryTableResponse(0, "87bbb472ef9d90dcef140a551665c929");
// Currently commenting this until https://github.com/appsmithorg/appsmith/issues/25447 is fixed.
// Filter by cell range and verify
// dataSources.ValidateNSelectDropdown(
// "Filter Format",
// "Where Clause",
// "Cell range",
// );
// agHelper.EnterValue("A2:A5", {
// propFieldName: "",
// directInput: false,
// inputFieldName: "Cell range",
// });
// dataSources.RunQuery();
// dataSources.RunQueryNVerifyResponseViews(8);
// dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
});
it("5. Update a record which is not present and verify the error", () => {
//preparing data
let data = GSHEET_DATA[1];
data.rowIndex = "15";
// add update one query and verify
gsheetHelper.EnterBasicQueryValues(
"Update One",
dataSourceName,
spreadSheetName,
false,
);
agHelper.EnterValue(JSON.stringify(data), {
propFieldName: "",
directInput: false,
inputFieldName: "Update row object",
});
dataSources.RunQuery({
expectedStatus: false,
});
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"No data found at this row index.",
);
});
});
it("6. Convert field to JS and verify", () => {
// Switch js on sheet name then run query and verify
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName,
spreadSheetName,
false,
);
agHelper.GetNClick(
dataSources._getJSONswitchLocator("Sheet name"),
0,
true,
);
dataSources.RunQueryNVerifyResponseViews(10);
dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
//Enter a wrong sheet name then run query and verify
agHelper.EnterValue("Sheet 2", {
propFieldName: "",
directInput: false,
inputFieldName: "Sheet name",
});
dataSources.RunQuery({
expectedStatus: false,
});
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Unable to parse range: 'Sheet 2'!1:1",
);
});
//Covert sheet name field to dropdown then run query and verify
agHelper.HoverElement(dataSources._getJSONswitchLocator("Sheet name"));
agHelper.AssertTooltip("Clear the field to toggle back");
agHelper.EnterValue("", {
propFieldName: "",
directInput: false,
inputFieldName: "Sheet name",
}); //Clearing the sheet name field
agHelper.GetNClick(
dataSources._getJSONswitchLocator("Sheet name"),
0,
true,
); // Converting the field to dropdown
dataSources.ValidateNSelectDropdown("Sheet name", "", "Sheet1");
dataSources.RunQueryNVerifyResponseViews(10);
dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
});
it("7. Verify Delete query", function () {
// Delete spreadsheet and app
gsheetHelper.EnterBasicQueryValues(
"Delete One",
dataSourceName,
spreadSheetName,
);
agHelper.EnterValue(GSHEET_DATA[0].rowIndex, {
propFieldName: "",
directInput: false,
inputFieldName: "Row Index",
});
dataSources.RunQuery();
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Deleted row successfully!",
);
});
});
after("Delete spreadsheet and app", function () {
// Delete spreadsheet and app
gsheetHelper.DeleteSpreadsheetQuery(dataSourceName, spreadSheetName);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Deleted spreadsheet successfully!",
);
});
homePage.NavigateToHome();
homePage.DeleteApplication(appName);
});
});

View File

@ -0,0 +1,314 @@
/// <reference types="Cypress" />
import { GSHEET_DATA } from "../../fixtures/test-data-gsheet";
import {
homePage,
gsheetHelper,
dataSources,
agHelper,
entityExplorer,
} from "../../support/Objects/ObjectsCore";
describe("GSheet-Functional Tests With Read/Write Access", function () {
const workspaceName = "gsheet apps";
const dataSourceName = {
allAccess: "gsheet",
readNWrite: "gsheet-read-write",
};
let appName = "gsheet-app";
let spreadSheetName = "test-sheet";
before("Setup app and spreadsheet", function () {
//Add a new app and an add new spreadsheet query
//Setting up the spreadsheet name
const uuid = Cypress._.random(0, 10000);
spreadSheetName = spreadSheetName + "_" + uuid;
appName = appName + "-" + uuid;
//Adding query to insert a new spreadsheet
homePage.NavigateToHome();
homePage.CreateAppInWorkspace(workspaceName);
homePage.RenameApplication(appName);
gsheetHelper.AddNewSpreadsheetQuery(
dataSourceName.readNWrite,
spreadSheetName,
JSON.stringify([GSHEET_DATA[0]]),
);
cy.get("@postExecute").then((interception: any) => {
expect(
interception.response.body.data.body.properties.title,
).to.deep.equal(spreadSheetName);
});
});
it("1. Add and verify fetch details query", () => {
entityExplorer.CreateNewDsQuery(dataSourceName.readNWrite);
agHelper.RenameWithInPane("Fetch_Details");
dataSources.ValidateNSelectDropdown(
"Operation",
"Fetch Many",
"Fetch Details",
);
dataSources.ValidateNSelectDropdown("Entity", "Spreadsheet");
agHelper.Sleep(500);
dataSources.ValidateNSelectDropdown("Spreadsheet", "", spreadSheetName);
dataSources.RunQuery();
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.name).to.deep.equal(
spreadSheetName,
);
});
});
it("2. Verify Insert one and Insert many queries", () => {
// add insert one query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Insert One",
dataSourceName.readNWrite,
spreadSheetName,
JSON.stringify(GSHEET_DATA[1]),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Inserted row successfully!",
);
});
// add insert many query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Insert Many",
dataSourceName.readNWrite,
spreadSheetName,
JSON.stringify(GSHEET_DATA.slice(2, 10)),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Inserted rows successfully!",
);
});
});
it("3. Verify Update one and Update many queries", () => {
// add update one query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Update One",
dataSourceName.readNWrite,
spreadSheetName,
JSON.stringify(GSHEET_DATA[1]),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Updated sheet successfully!",
);
});
// add update many query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Update Many",
dataSourceName.readNWrite,
spreadSheetName,
JSON.stringify(GSHEET_DATA.slice(2, 4)),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Updated sheet successfully!",
);
});
});
it("4. Verify Fetch many query", () => {
// Add simple Fetch many query and verify
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName.readNWrite,
spreadSheetName,
);
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, GSHEET_DATA[0].uniq_id);
dataSources.AssertQueryTableResponse(1, "ホーンビィ 2014 カタログ"); // Asserting other language
dataSources.AssertQueryTableResponse(2, "₹, $, €, ¥, £"); // Asserting different symbols
dataSources.AssertQueryTableResponse(3, "!@#$%^&*"); // Asserting special chars
// Update query to fetch only 1 column and verify
gsheetHelper.SelectMultiDropDownValue("Columns", "product_name");
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, GSHEET_DATA[0].product_name);
//Remove column filter and add Sort By Ascending and verify
gsheetHelper.SelectMultiDropDownValue("Columns", "product_name"); //unselect the Columns dd value
agHelper.EnterValue("price", {
propFieldName: "",
directInput: false,
inputFieldName: "Sort By",
});
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, "5afbaf65680c9f378af5b3a3ae22427e");
dataSources.AssertQueryTableResponse(
1,
"ラーニング カーブ チャギントン インタラクティブ チャッツワース",
); // Asserting other language
dataSources.AssertQueryTableResponse(2, "₹, $, €, ¥, £"); // Asserting different symbols
dataSources.AssertQueryTableResponse(3, "!@#$%^&*"); // Asserting special chars
// Sort by descending and verify
dataSources.ClearSortByOption(); //clearing previous sort option
dataSources.EnterSortByValues("price", "Descending");
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(
1,
"ホーンビー ゲージ ウェスタン エクスプレス デジタル トレイン セット (eLink および TTS ロコ トレイン セット付き)",
); // Asserting other language
dataSources.AssertQueryTableResponse(
4,
"Hornby Gauge Western Express Digital Train Set with eLink and TTS Loco Train Set",
);
// Filter by where clause and verify
agHelper.TypeDynamicInputValueNValidate(
"price",
dataSources._nestedWhereClauseKey(0),
);
agHelper.TypeDynamicInputValueNValidate(
"100",
dataSources._nestedWhereClauseValue(0),
);
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(8);
dataSources.AssertQueryTableResponse(0, "87bbb472ef9d90dcef140a551665c929");
// Currently commenting this until https://github.com/appsmithorg/appsmith/issues/25447 is fixed.
// Filter by cell range and verify
// dataSources.ValidateNSelectDropdown(
// "Filter Format",
// "Where Clause",
// "Cell range",
// );
// agHelper.EnterValue("A2:A5", {
// propFieldName: "",
// directInput: false,
// inputFieldName: "Cell range",
// });
// dataSources.RunQuery();
// dataSources.RunQueryNVerifyResponseViews(8);
// dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
});
it("5. Update a record which is not present and verify the error", () => {
//preparing data
let data = GSHEET_DATA[1];
data.rowIndex = "15";
// add update one query and verify
gsheetHelper.EnterBasicQueryValues(
"Update One",
dataSourceName.readNWrite,
spreadSheetName,
false,
);
agHelper.EnterValue(JSON.stringify(data), {
propFieldName: "",
directInput: false,
inputFieldName: "Update row object",
});
dataSources.RunQuery({
expectedStatus: false,
});
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"No data found at this row index.",
);
});
});
it("6. Convert field to JS and verify", () => {
// Switch js on sheet name then run query and verify
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName.readNWrite,
spreadSheetName,
false,
);
agHelper.GetNClick(
dataSources._getJSONswitchLocator("Sheet name"),
0,
true,
);
dataSources.RunQueryNVerifyResponseViews(10);
dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
//Enter a wrong sheet name then run query and verify
agHelper.EnterValue("Sheet 2", {
propFieldName: "",
directInput: false,
inputFieldName: "Sheet name",
});
dataSources.RunQuery({
expectedStatus: false,
});
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Unable to parse range: 'Sheet 2'!1:1",
);
});
//Covert sheet name field to dropdown then run query and verify
agHelper.HoverElement(dataSources._getJSONswitchLocator("Sheet name"));
agHelper.AssertTooltip("Clear the field to toggle back");
agHelper.EnterValue("", {
propFieldName: "",
directInput: false,
inputFieldName: "Sheet name",
}); //Clearing the sheet name field
agHelper.GetNClick(
dataSources._getJSONswitchLocator("Sheet name"),
0,
true,
); // Converting the field to dropdown
dataSources.ValidateNSelectDropdown("Sheet name", "", "Sheet1");
dataSources.RunQueryNVerifyResponseViews(10);
dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
});
it("7. Delete row with Read/Write | All google sheets permission", () => {
gsheetHelper.EnterBasicQueryValues(
"Delete One",
dataSourceName.readNWrite,
spreadSheetName,
);
agHelper.EnterValue(GSHEET_DATA[0].rowIndex, {
propFieldName: "",
directInput: false,
inputFieldName: "Row Index",
});
dataSources.RunQuery();
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Deleted row successfully!",
);
});
});
it("8. Delete spreadsheet with Read/Write | All google sheets permission", () => {
gsheetHelper.EnterBasicQueryValues(
"Delete One",
dataSourceName.readNWrite,
spreadSheetName,
false,
"Spreadsheet",
);
dataSources.RunQuery({
expectedStatus: false,
});
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Request had insufficient authentication scopes.",
);
});
});
after("Delete spreadsheet and app", function () {
// Delete spreadsheet and app
gsheetHelper.DeleteSpreadsheetQuery(
dataSourceName.allAccess,
spreadSheetName,
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Deleted spreadsheet successfully!",
);
});
homePage.NavigateToHome();
homePage.DeleteApplication(appName);
});
});

View File

@ -0,0 +1,296 @@
/// <reference types="Cypress" />
import { GSHEET_DATA } from "../../fixtures/test-data-gsheet";
import {
homePage,
gsheetHelper,
dataSources,
agHelper,
entityExplorer,
} from "../../support/Objects/ObjectsCore";
describe("GSheet-Functional Tests With Read Access", function () {
const workspaceName = "gsheet apps";
const dataSourceName = {
allAccess: "gsheet",
readOnly: "gsheet-read",
};
let appName = "gsheet-app";
let spreadSheetName = "test-sheet";
before("Setup app and spreadsheet", function () {
//Add a new app and an add new spreadsheet query
//Setting up the spreadsheet name
const uuid = Cypress._.random(0, 10000);
spreadSheetName = spreadSheetName + "_" + uuid;
appName = appName + "-" + uuid;
//Adding query to insert a new spreadsheet
homePage.NavigateToHome();
homePage.CreateAppInWorkspace(workspaceName);
homePage.RenameApplication(appName);
gsheetHelper.AddNewSpreadsheetQuery(
dataSourceName.allAccess,
spreadSheetName,
JSON.stringify(GSHEET_DATA),
);
cy.get("@postExecute").then((interception: any) => {
expect(
interception.response.body.data.body.properties.title,
).to.deep.equal(spreadSheetName);
});
});
it("1. Add and verify fetch details query", () => {
entityExplorer.CreateNewDsQuery(dataSourceName.readOnly);
agHelper.RenameWithInPane("Fetch_Details");
dataSources.ValidateNSelectDropdown(
"Operation",
"Fetch Many",
"Fetch Details",
);
dataSources.ValidateNSelectDropdown("Entity", "Spreadsheet");
agHelper.Sleep(500);
dataSources.ValidateNSelectDropdown("Spreadsheet", "", spreadSheetName);
dataSources.RunQuery();
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.name).to.deep.equal(
spreadSheetName,
);
});
});
it("2. Verify error responses for Insert one and Insert many queries for read only access", () => {
// add insert one query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Insert One",
dataSourceName.readOnly,
spreadSheetName,
JSON.stringify(GSHEET_DATA[1]),
false,
);
dataSources.RunQuery({ expectedStatus: false });
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Request had insufficient authentication scopes.",
);
});
// add insert many query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Insert Many",
dataSourceName.readOnly,
spreadSheetName,
JSON.stringify(GSHEET_DATA.slice(2, 10)),
false,
);
dataSources.RunQuery({ expectedStatus: false });
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Request had insufficient authentication scopes.",
);
});
});
it("3. Verify error responses for Update one and Update many queries for Access only ", () => {
// add update one query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Update One",
dataSourceName.readOnly,
spreadSheetName,
JSON.stringify(GSHEET_DATA[1]),
false,
);
dataSources.RunQuery({ expectedStatus: false });
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Request had insufficient authentication scopes.",
);
});
// add update many query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Update Many",
dataSourceName.readOnly,
spreadSheetName,
JSON.stringify(GSHEET_DATA.slice(2, 4)),
false,
);
dataSources.RunQuery({ expectedStatus: false });
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Request had insufficient authentication scopes.",
);
});
});
it("4. Verify Fetch many query", () => {
// Add simple Fetch many query and verify
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName.readOnly,
spreadSheetName,
);
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, GSHEET_DATA[0].uniq_id);
dataSources.AssertQueryTableResponse(1, "ホーンビィ 2014 カタログ"); // Asserting other language
dataSources.AssertQueryTableResponse(2, "₹, $, €, ¥, £"); // Asserting different symbols
dataSources.AssertQueryTableResponse(3, "!@#$%^&*"); // Asserting special chars
// Update query to fetch only 1 column and verify
gsheetHelper.SelectMultiDropDownValue("Columns", "product_name");
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, GSHEET_DATA[0].product_name);
//Remove column filter and add Sort By Ascending and verify
gsheetHelper.SelectMultiDropDownValue("Columns", "product_name"); //unselect the Columns dd value
agHelper.EnterValue("price", {
propFieldName: "",
directInput: false,
inputFieldName: "Sort By",
});
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, "5afbaf65680c9f378af5b3a3ae22427e");
dataSources.AssertQueryTableResponse(
1,
"ラーニング カーブ チャギントン インタラクティブ チャッツワース",
); // Asserting other language
dataSources.AssertQueryTableResponse(2, "₹, $, €, ¥, £"); // Asserting different symbols
dataSources.AssertQueryTableResponse(3, "!@#$%^&*"); // Asserting special chars
// Sort by descending and verify
dataSources.ClearSortByOption(); //clearing previous sort option
dataSources.EnterSortByValues("price", "Descending");
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(
1,
"ホーンビー ゲージ ウェスタン エクスプレス デジタル トレイン セット (eLink および TTS ロコ トレイン セット付き)",
); // Asserting other language
dataSources.AssertQueryTableResponse(
4,
"Hornby Gauge Western Express Digital Train Set with eLink and TTS Loco Train Set",
);
// Filter by where clause and verify
agHelper.TypeDynamicInputValueNValidate(
"price",
dataSources._nestedWhereClauseKey(0),
);
agHelper.TypeDynamicInputValueNValidate(
"100",
dataSources._nestedWhereClauseValue(0),
);
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(8);
dataSources.AssertQueryTableResponse(0, "87bbb472ef9d90dcef140a551665c929");
// Currently commenting this until https://github.com/appsmithorg/appsmith/issues/25447 is fixed.
// Filter by cell range and verify
// dataSources.ValidateNSelectDropdown(
// "Filter Format",
// "Where Clause",
// "Cell range",
// );
// agHelper.EnterValue("A2:A5", {
// propFieldName: "",
// directInput: false,
// inputFieldName: "Cell range",
// });
// dataSources.RunQuery();
// dataSources.RunQueryNVerifyResponseViews(8);
// dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
});
it("5. Convert field to JS and verify", () => {
// Switch js on sheet name then run query and verify
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName.readOnly,
spreadSheetName,
false,
);
agHelper.GetNClick(
dataSources._getJSONswitchLocator("Sheet name"),
0,
true,
);
dataSources.RunQueryNVerifyResponseViews(10);
dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
//Enter a wrong sheet name then run query and verify
agHelper.EnterValue("Sheet 2", {
propFieldName: "",
directInput: false,
inputFieldName: "Sheet name",
});
dataSources.RunQuery({
expectedStatus: false,
});
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Unable to parse range: 'Sheet 2'!1:1",
);
});
//Covert sheet name field to dropdown then run query and verify
agHelper.HoverElement(dataSources._getJSONswitchLocator("Sheet name"));
agHelper.AssertTooltip("Clear the field to toggle back");
agHelper.EnterValue("", {
propFieldName: "",
directInput: false,
inputFieldName: "Sheet name",
}); //Clearing the sheet name field
agHelper.GetNClick(
dataSources._getJSONswitchLocator("Sheet name"),
0,
true,
); // Converting the field to dropdown
dataSources.ValidateNSelectDropdown("Sheet name", "", "Sheet1");
dataSources.RunQueryNVerifyResponseViews(10);
dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
});
it("6. Delete row with Read | All google sheets permission", () => {
gsheetHelper.EnterBasicQueryValues(
"Delete One",
dataSourceName.readOnly,
spreadSheetName,
);
agHelper.EnterValue(GSHEET_DATA[0].rowIndex, {
propFieldName: "",
directInput: false,
inputFieldName: "Row Index",
});
dataSources.RunQuery({ expectedStatus: false });
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Request had insufficient authentication scopes.",
);
});
});
it("7. Delete spreadsheet with Read | All google sheets permission", () => {
gsheetHelper.EnterBasicQueryValues(
"Delete One",
dataSourceName.readOnly,
spreadSheetName,
false,
"Spreadsheet",
);
dataSources.RunQuery({
expectedStatus: false,
});
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Request had insufficient authentication scopes.",
);
});
});
after("Delete spreadsheet and app", function () {
// Delete spreadsheet and app
gsheetHelper.DeleteSpreadsheetQuery(
dataSourceName.allAccess,
spreadSheetName,
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Deleted spreadsheet successfully!",
);
});
homePage.NavigateToHome();
homePage.DeleteApplication(appName);
});
});

View File

@ -0,0 +1,299 @@
/// <reference types="Cypress" />
import { GSHEET_DATA } from "../../fixtures/test-data-gsheet";
import {
homePage,
gsheetHelper,
dataSources,
agHelper,
entityExplorer,
} from "../../support/Objects/ObjectsCore";
const workspaceName = "gsheet apps";
const dataSourceName = "gsheet-selected";
let appName = "gsheet-app";
let spreadSheetName = "test-sheet-automation-selected";
describe("GSheet-Functional Tests With Selected Access", function () {
before("Setup app", function () {
//Setting up app name
const uuid = Cypress._.random(0, 10000);
appName = appName + "-" + uuid;
//Adding app
homePage.NavigateToHome();
homePage.CreateAppInWorkspace(workspaceName);
homePage.RenameApplication(appName);
});
it("1. Add and verify fetch details query", () => {
entityExplorer.CreateNewDsQuery(dataSourceName);
agHelper.RenameWithInPane("Fetch_Details");
dataSources.ValidateNSelectDropdown(
"Operation",
"Fetch Many",
"Fetch Details",
);
dataSources.ValidateNSelectDropdown("Entity", "Spreadsheet");
agHelper.Sleep(500);
dataSources.ValidateNSelectDropdown("Spreadsheet", "", spreadSheetName);
dataSources.RunQuery();
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.name).to.deep.equal(
spreadSheetName,
);
});
});
it("2. Verify Insert One and Insert Many queries", () => {
// add insert one query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Insert One",
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA[0]),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Inserted row successfully!",
);
});
// add insert many query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Insert Many",
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA.slice(1, 10)),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Inserted rows successfully!",
);
});
});
it("3. Verify Update one and Update many queries", () => {
// add update one query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Update One",
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA[1]),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Updated sheet successfully!",
);
});
// add update many query and verify
gsheetHelper.AddInsertOrUpdateQuery(
"Update Many",
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA.slice(2, 4)),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Updated sheet successfully!",
);
});
});
it("4. Verify Fetch many query", () => {
// Add simple Fetch many query and verify
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName,
spreadSheetName,
);
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, GSHEET_DATA[0].uniq_id);
dataSources.AssertQueryTableResponse(1, "ホーンビィ 2014 カタログ"); // Asserting other language
dataSources.AssertQueryTableResponse(2, "₹, $, €, ¥, £"); // Asserting different symbols
dataSources.AssertQueryTableResponse(3, "!@#$%^&*"); // Asserting special chars
// Update query to fetch only 1 column and verify
gsheetHelper.SelectMultiDropDownValue("Columns", "product_name");
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, GSHEET_DATA[0].product_name);
//Remove column filter and add Sort By Ascending and verify
gsheetHelper.SelectMultiDropDownValue("Columns", "product_name"); //unselect the Columns dd value
agHelper.EnterValue("price", {
propFieldName: "",
directInput: false,
inputFieldName: "Sort By",
});
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(0, "5afbaf65680c9f378af5b3a3ae22427e");
dataSources.AssertQueryTableResponse(
1,
"ラーニング カーブ チャギントン インタラクティブ チャッツワース",
); // Asserting other language
dataSources.AssertQueryTableResponse(2, "₹, $, €, ¥, £"); // Asserting different symbols
dataSources.AssertQueryTableResponse(3, "!@#$%^&*"); // Asserting special chars
// Sort by descending and verify
dataSources.ClearSortByOption(); //clearing previous sort option
dataSources.EnterSortByValues("price", "Descending");
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(GSHEET_DATA.length);
dataSources.AssertQueryTableResponse(
1,
"ホーンビー ゲージ ウェスタン エクスプレス デジタル トレイン セット (eLink および TTS ロコ トレイン セット付き)",
); // Asserting other language
dataSources.AssertQueryTableResponse(
4,
"Hornby Gauge Western Express Digital Train Set with eLink and TTS Loco Train Set",
);
// Filter by where clause and verify
agHelper.TypeDynamicInputValueNValidate(
"price",
dataSources._nestedWhereClauseKey(0),
);
agHelper.TypeDynamicInputValueNValidate(
"100",
dataSources._nestedWhereClauseValue(0),
);
dataSources.RunQuery();
dataSources.RunQueryNVerifyResponseViews(8);
dataSources.AssertQueryTableResponse(0, "87bbb472ef9d90dcef140a551665c929");
// Currently commenting this until https://github.com/appsmithorg/appsmith/issues/25447 is fixed.
// Filter by cell range and verify
// dataSources.ValidateNSelectDropdown(
// "Filter Format",
// "Where Clause",
// "Cell range",
// );
// agHelper.EnterValue("A2:A5", {
// propFieldName: "",
// directInput: false,
// inputFieldName: "Cell range",
// });
// dataSources.RunQuery();
// dataSources.RunQueryNVerifyResponseViews(8);
// dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
});
it("5. Update a record which is not present and verify the error", () => {
//preparing data
const data = GSHEET_DATA[1];
data.rowIndex = "15";
// add update one query and verify
gsheetHelper.EnterBasicQueryValues(
"Update One",
dataSourceName,
spreadSheetName,
false,
);
agHelper.EnterValue(JSON.stringify(data), {
propFieldName: "",
directInput: false,
inputFieldName: "Update row object",
});
dataSources.RunQuery({
expectedStatus: false,
});
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"No data found at this row index.",
);
});
//reset the row index
data.rowIndex = "1";
});
it("6. Convert field to JS and verify", () => {
// Switch js on sheet name then run query and verify
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName,
spreadSheetName,
false,
);
agHelper.GetNClick(
dataSources._getJSONswitchLocator("Sheet name"),
0,
true,
);
dataSources.RunQueryNVerifyResponseViews(10);
dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
//Enter a wrong sheet name then run query and verify
agHelper.EnterValue("Sheet 2", {
propFieldName: "",
directInput: false,
inputFieldName: "Sheet name",
});
dataSources.RunQuery({
expectedStatus: false,
});
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal(
"Unable to parse range: 'Sheet 2'!1:1",
);
});
//Covert sheet name field to dropdown then run query and verify
agHelper.HoverElement(dataSources._getJSONswitchLocator("Sheet name"));
agHelper.AssertTooltip("Clear the field to toggle back");
agHelper.EnterValue("", {
propFieldName: "",
directInput: false,
inputFieldName: "Sheet name",
}); //Clearing the sheet name field
agHelper.GetNClick(
dataSources._getJSONswitchLocator("Sheet name"),
0,
true,
); // Converting the field to dropdown
dataSources.ValidateNSelectDropdown("Sheet name", "", "Sheet1");
dataSources.RunQueryNVerifyResponseViews(10);
dataSources.AssertQueryTableResponse(0, "eac7efa5dbd3d667f26eb3d3ab504464");
});
it("7. Verify Delete query", function () {
// Delete spreadsheet and app
gsheetHelper.EnterBasicQueryValues(
"Delete One",
dataSourceName,
spreadSheetName,
);
GSHEET_DATA.reverse().forEach((d) => {
agHelper.EnterValue(d.rowIndex, {
propFieldName: "",
directInput: false,
inputFieldName: "Row Index",
});
dataSources.RunQuery();
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Deleted row successfully!",
);
});
agHelper.Sleep(500);
});
// Fetch many to verify all the data is deleted
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName,
spreadSheetName,
false,
);
dataSources.RunQuery();
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body).to.deep.equal([]);
});
});
after("Delete app", function () {
// Delete app
homePage.NavigateToHome();
homePage.DeleteApplication(appName);
});
});

View File

@ -0,0 +1,87 @@
/// <reference types="Cypress" />
import { GSHEET_DATA } from "../../fixtures/test-data-gsheet";
import {
homePage,
gsheetHelper,
dataSources,
agHelper,
entityExplorer,
propPane,
table,
draggableWidgets,
} from "../../support/Objects/ObjectsCore";
import { Widgets } from "../../support/Pages/DataSources";
import oneClickBindingLocator from "../../locators/OneClickBindingLocator";
const workspaceName = "gsheet apps";
const dataSourceName = "gsheet";
let appName = "gsheet-app";
let spreadSheetName = "test-sheet";
describe("GSheet-widget binding", function () {
before("Setup app and spreadsheet", function () {
//Setting up the app name
const uuid = Cypress._.random(0, 10000);
spreadSheetName = spreadSheetName + "_" + uuid;
appName = appName + "-" + uuid;
//Adding app
homePage.NavigateToHome();
homePage.CreateAppInWorkspace(workspaceName);
homePage.RenameApplication(appName);
gsheetHelper.AddNewSpreadsheetQuery(
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA),
);
cy.get("@postExecute").then((interception: any) => {
expect(
interception.response.body.data.body.properties.title,
).to.deep.equal(spreadSheetName);
});
});
it("1. Verify 'Add to widget [Widget Suggestion]' functionality - GSheet", () => {
//Adding query
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName,
spreadSheetName,
);
dataSources.RunQueryNVerifyResponseViews(10);
// Adding suggested widgets and verify
dataSources.AddSuggesstedWidget(Widgets.Table);
table.ReadTableRowColumnData(0, 0, "v2").then((cellData) => {
expect(cellData).to.eq("eac7efa5dbd3d667f26eb3d3ab504464");
});
agHelper.GetNClick(propPane._deleteWidget);
});
it("2. One click binding to table widget functionality - GSheet", () => {
//Adding table widget
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TABLE, 450, 200);
agHelper.GetNClick(oneClickBindingLocator.datasourceDropdownSelector);
agHelper.GetNClick(
oneClickBindingLocator.datasourceQuerySelector("fetch_many_query"),
);
// Assert table data
table.ReadTableRowColumnData(0, 0, "v2").then((cellData) => {
expect(cellData).to.eq("eac7efa5dbd3d667f26eb3d3ab504464");
});
agHelper.GetNClick(propPane._deleteWidget);
});
after("Delete app", function () {
// Delete spreadsheet and app
entityExplorer.NavigateToSwitcher("Explorer");
gsheetHelper.DeleteSpreadsheetQuery(dataSourceName, spreadSheetName);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Deleted spreadsheet successfully!",
);
});
homePage.NavigateToHome();
homePage.DeleteApplication(appName);
});
});

View File

@ -0,0 +1,100 @@
/// <reference types="Cypress" />
import { GSHEET_DATA } from "../../fixtures/test-data-gsheet";
import {
homePage,
gsheetHelper,
dataSources,
agHelper,
entityExplorer,
propPane,
table,
draggableWidgets,
} from "../../support/Objects/ObjectsCore";
import { Widgets } from "../../support/Pages/DataSources";
import oneClickBindingLocator from "../../locators/OneClickBindingLocator";
const workspaceName = "gsheet apps";
const dataSourceName = "gsheet-selected";
let appName = "gsheet-app";
let spreadSheetName = "test-sheet-automation-selected";
describe("GSheet-widget binding for selected sheet access", function () {
before("Setup app and spreadsheet", function () {
//Setting up the app name
const uuid = Cypress._.random(0, 10000);
appName = appName + "-" + uuid;
//Adding app and data to the selected sheet
homePage.NavigateToHome();
homePage.CreateAppInWorkspace(workspaceName);
homePage.RenameApplication(appName);
gsheetHelper.AddInsertOrUpdateQuery(
"Insert Many",
dataSourceName,
spreadSheetName,
JSON.stringify(GSHEET_DATA),
);
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Inserted rows successfully!",
);
});
});
it("1. Verify 'Add to widget [Widget Suggestion]' functionality for selected sheet access - GSheet", () => {
//Adding query
gsheetHelper.EnterBasicQueryValues(
"Fetch Many",
dataSourceName,
spreadSheetName,
);
dataSources.RunQueryNVerifyResponseViews(10);
// Adding suggested widgets and verify
dataSources.AddSuggesstedWidget(Widgets.Table);
table.ReadTableRowColumnData(0, 0, "v2").then((cellData) => {
expect(cellData).to.eq("eac7efa5dbd3d667f26eb3d3ab504464");
});
agHelper.GetNClick(propPane._deleteWidget);
});
it("2. One click binding to table widget functionality for selected sheet access - GSheet", () => {
//Adding table widget
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TABLE, 450, 200);
agHelper.GetNClick(oneClickBindingLocator.datasourceDropdownSelector);
agHelper.GetNClick(
oneClickBindingLocator.datasourceQuerySelector("fetch_many_query"),
);
// Assert table data
table.ReadTableRowColumnData(0, 0, "v2").then((cellData) => {
expect(cellData).to.eq("eac7efa5dbd3d667f26eb3d3ab504464");
});
agHelper.GetNClick(propPane._deleteWidget);
});
after("Delete app", function () {
// Delete data in spreadsheet and app
entityExplorer.NavigateToSwitcher("Explorer");
gsheetHelper.EnterBasicQueryValues(
"Delete One",
dataSourceName,
spreadSheetName,
);
GSHEET_DATA.reverse().forEach((d) => {
agHelper.EnterValue(d.rowIndex, {
propFieldName: "",
directInput: false,
inputFieldName: "Row Index",
});
dataSources.RunQuery();
cy.get("@postExecute").then((interception: any) => {
expect(interception.response.body.data.body.message).to.deep.equal(
"Deleted row successfully!",
);
});
agHelper.Sleep(500);
});
homePage.NavigateToHome();
homePage.DeleteApplication(appName);
});
});

View File

@ -146,7 +146,7 @@ describe("AForce - Community Issues page validations", function () {
entityExplorer.SelectEntityByName("Table1", "Widgets");
//propPane.EnterJSContext("Default search text", "Question", false);
propPane.TypeTextIntoField("Default search text", "Quest");
propPane.TypeTextIntoField("Default search text", "Quest", true, false);
deployMode.DeployApp();
table.AssertSearchText("Quest", 2);

View File

@ -6,14 +6,14 @@ import {
gitSync,
apiPage,
dataSources,
tedTestConfig,
} from "../../../support/Objects/ObjectsCore";
describe("Content Management System App", function () {
before(() => {
homePage.NavigateToHome();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
homePage.CreateNewWorkspace("EchoApiCMS" + uid);
homePage.CreateNewWorkspace("EchoApiCMS" + uid, true);
homePage.CreateAppInWorkspace("EchoApiCMS" + uid, "EchoApiCMSApp");
agHelper.AddDsl("CMSdsl");
});
@ -21,55 +21,52 @@ describe("Content Management System App", function () {
let repoName;
it("1.Create Get echo Api call", function () {
cy.fixture("datasources").then((datasourceFormData) => {
apiPage.CreateAndFillApi(datasourceFormData["echoApiUrl"], "get_data");
// creating get request using echo
apiPage.EnterHeader(
"info",
'[{"due":"2021-11-23","assignee":"Dan.Wyman@hotmail.com","title":"Recusan","description":"Ut quisquam eum beatae facere eos aliquam laborum ea.","id":"1"},{"due":"2021-11-23","assignee":"Dashawn_Maggio30@gmail.com","title":"Dignissimos eaque","description":"Consequatur corrupti et possimus en.","id":"2"},{"due":"2021-11-24","assignee":"Curt50@gmail.com","title":"Voluptas explicabo","description":"Quia ratione optio et maiores.","id":"3"},{"due":"2021-11-23","assignee":"Shanna63@hotmail.com","title":"Aut omnis.","description":"Neque rerum numquam veniam voluptatum id. Aut daut.","id":"4"}]',
);
// entering the data in header
apiPage.RunAPI();
apiPage.ResponseStatusCheck("200");
});
apiPage.CreateAndFillApi(
tedTestConfig.dsValues[tedTestConfig.defaultEnviorment].echoApiUrl,
"get_data",
);
// creating get request using echo
apiPage.EnterHeader(
"info",
'[{"due":"2021-11-23","assignee":"Dan.Wyman@hotmail.com","title":"Recusan","description":"Ut quisquam eum beatae facere eos aliquam laborum ea.","id":"1"},{"due":"2021-11-23","assignee":"Dashawn_Maggio30@gmail.com","title":"Dignissimos eaque","description":"Consequatur corrupti et possimus en.","id":"2"},{"due":"2021-11-24","assignee":"Curt50@gmail.com","title":"Voluptas explicabo","description":"Quia ratione optio et maiores.","id":"3"},{"due":"2021-11-23","assignee":"Shanna63@hotmail.com","title":"Aut omnis.","description":"Neque rerum numquam veniam voluptatum id. Aut daut.","id":"4"}]',
);
// entering the data in header
apiPage.RunAPI();
apiPage.ResponseStatusCheck("200");
});
it("2. Create Post echo Api call", function () {
cy.fixture("datasources").then((datasourceFormData) => {
apiPage.CreateAndFillApi(
datasourceFormData["echoApiUrl"],
"send_mail",
10000,
"POST",
);
apiPage.SelectPaneTab("Body");
apiPage.SelectSubTab("JSON");
// creating post request using echo
dataSources.EnterQuery(
'{"to":"{{to_input.text}}","subject":"{{subject.text}}","content":"{{content.text}}"}',
);
apiPage.RunAPI();
apiPage.ResponseStatusCheck("200");
});
apiPage.CreateAndFillApi(
tedTestConfig.dsValues[tedTestConfig.defaultEnviorment].echoApiUrl,
"send_mail",
10000,
"POST",
);
apiPage.SelectPaneTab("Body");
apiPage.SelectSubTab("JSON");
// creating post request using echo
dataSources.EnterQuery(
'{"to":"{{to_input.text}}","subject":"{{subject.text}}","content":"{{content.text}}"}',
);
apiPage.RunAPI();
apiPage.ResponseStatusCheck("200");
});
it("3. Create Delete echo Api call", function () {
cy.fixture("datasources").then((datasourceFormData) => {
apiPage.CreateAndFillApi(
datasourceFormData["echoApiUrl"],
"delete_proposal",
10000,
"DELETE",
);
apiPage.SelectPaneTab("Body");
apiPage.SelectSubTab("JSON");
// creating post request using echo
dataSources.EnterQuery(
'{"title":"{{title.text}}","due":"{{due.text}}","assignee":"{{assignee.text}}"}',
);
apiPage.RunAPI();
apiPage.ResponseStatusCheck("200");
});
apiPage.CreateAndFillApi(
tedTestConfig.dsValues[tedTestConfig.defaultEnviorment].echoApiUrl,
"delete_proposal",
10000,
"DELETE",
);
apiPage.SelectPaneTab("Body");
apiPage.SelectSubTab("JSON");
// creating post request using echo
dataSources.EnterQuery(
'{"title":"{{title.text}}","due":"{{due.text}}","assignee":"{{assignee.text}}"}',
);
apiPage.RunAPI();
apiPage.ResponseStatusCheck("200");
});
it("4. Send mail and verify post request body", function () {

View File

@ -12,10 +12,9 @@ describe("Shopping cart App", function () {
let datasourceName: string, repoName: any;
before(() => {
homePage.NavigateToHome();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
homePage.CreateNewWorkspace("MongoDBShop" + uid);
homePage.CreateNewWorkspace("MongoDBShop" + uid, true);
homePage.CreateAppInWorkspace("MongoDBShop" + uid, "MongoDBShopApp");
agHelper.AddDsl("mongoAppdsl");
});
@ -28,7 +27,6 @@ describe("Shopping cart App", function () {
it("1. Create MongoDB datasource and add Insert, Find, Update and Delete queries", function () {
dataSources.CreateQueryAfterDSSaved("", "GetProduct");
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "Productnames",
});
@ -42,7 +40,6 @@ describe("Shopping cart App", function () {
"Update document(s)",
);
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "Productnames",
});
@ -73,7 +70,6 @@ describe("Shopping cart App", function () {
"Insert document(s)",
);
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "Productnames",
});
@ -101,7 +97,6 @@ describe("Shopping cart App", function () {
"Delete document(s)",
);
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "Productnames",
});

View File

@ -5,12 +5,14 @@ import {
jsEditor,
propPane,
apiPage,
} from "../../../../../support/Objects/ObjectsCore";
draggableWidgets,
} from "../../../../support/Objects/ObjectsCore";
describe("UI to Code", () => {
before(() => {
agHelper.AddDsl("buttondsl");
apiPage.CreateApi("Api1", "GET");
entityExplorer.DragDropWidgetNVerify(draggableWidgets.BUTTON);
entityExplorer.NavigateToSwitcher("Explorer");
apiPage.CreateApi();
apiPage.CreateApi("Api2", "POST");
});
@ -372,18 +374,18 @@ describe("UI to Code", () => {
// Edit the success callback of the nested Api2.run
propPane.SelectActionByTitleAndValue("Execute a query", "Api2.run");
cy.get(
jsEditor._lineinPropertyPaneJsEditor(
2,
propPane._actionSelectorFieldContentByLabel("Params"),
),
).type("val: 1");
agHelper.EnterActionValue(
"Params",
`{{{
val: 1
}}}`,
);
agHelper.GetNClick(propPane._actionSelectorPopupClose);
propPane.ValidateJSFieldValue(
"onClick",
`{{Api1.run().then(() => {  Api2.run({    val: 1    // "key": "value", }).then(() => {    showAlert("Hello"); }).catch(() => {    showAlert("World"); });});}}`,
`{{Api1.run().then(() => {  Api2.run({    val: 1 }).then(() => {    showAlert("Hello"); }).catch(() => {    showAlert("World"); });});}}`,
);
});
@ -414,13 +416,13 @@ describe("UI to Code", () => {
it("9. correctly configures a setInterval action", () => {
propPane.SelectPlatformFunction("onClick", "Set interval");
cy.get(
jsEditor._lineinPropertyPaneJsEditor(
2,
propPane._actionSelectorFieldContentByLabel("Callback function"),
),
).type("{enter}showAlert('Hello'){enter}//");
agHelper.EnterActionValue(
"Callback function",
`{{() => {
// add code here
showAlert('Hello')
}}}`,
);
agHelper.TypeText(
propPane._actionSelectorFieldByLabel("Id"),
@ -430,7 +432,7 @@ describe("UI to Code", () => {
propPane.ValidateJSFieldValue(
"onClick",
`{{setInterval(() => {  // add c  showAlert(\'Hello\');  // ode here}, 5000, \'interval-id\');}}`,
`{{setInterval(() => {  // add code here  showAlert('Hello');}, 5000, 'interval-id');}}`,
);
});
});

View File

@ -256,7 +256,8 @@ describe("Autocomplete tests", () => {
});
});
it("7. Autocompletion for bindings inside array and objects", () => {
//To fix soon
it.skip("7. Autocompletion for bindings inside array and objects", () => {
dataSources.CreateDataSource("Mongo", true, false);
dataSources.CreateQueryAfterDSSaved();

View File

@ -33,11 +33,11 @@ describe("Text-Chart Binding Functionality", function () {
this.dataSet.Chartval[1],
this.dataSet.Chartval[2],
];
[0, 1, 2].forEach((k) => {
[0, 2].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.eq(k)
.first()
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
cy.get(viewWidgetsPage.Chartlabel).contains(labels[k]);
});
deployMode.DeployApp();
});
@ -51,8 +51,8 @@ describe("Text-Chart Binding Functionality", function () {
this.dataSet.Chartval[2],
];
[0, 1, 2].forEach((k) => {
cy.get(publish.rectChart).eq(k).trigger("mousemove", { force: true });
cy.get(publish.chartLab).eq(k).should("have.text", labels[k]);
cy.get(publish.rectChart).first().trigger("mousemove", { force: true });
cy.get(publish.chartLab).contains(labels[k]);
});
cy.get(commonlocators.TextInside).should(
"have.text",

View File

@ -42,7 +42,7 @@ describe("Text-Table Binding Functionality", function () {
cy.get(viewWidgetsPage.chartWidget)
.find("svg")
.find("text")
.should("contain.text", "Product2");
.should("contain.text", "Product1");
cy.get(viewWidgetsPage.chartWidget)
.find("svg")

View File

@ -1,21 +0,0 @@
import * as _ from "../../../../support/Objects/ObjectsCore";
describe("Test Create Api and Bind to Table widget V2", function () {
before(() => {
_.agHelper.AddDsl("tableV2WidgetDsl");
});
it("1. Test_Add users api, execute it and go to sniping mode.", function () {
cy.createAndFillApi(this.dataSet.userApi, "/mock-api?records=10");
cy.RunAPI();
cy.get(".t--select-in-canvas").click();
cy.get(".t--sniping-mode-banner").should("be.visible");
//Click on table name controller to bind the data and exit sniping mode
cy.get(".t--draggable-tablewidgetv2").trigger("mouseover");
cy.get(".t--settings-sniping-control").click();
cy.get(".t--property-control-tabledata .CodeMirror").contains(
"{{Api1.data}}",
);
cy.get(".t--sniping-mode-banner").should("not.exist");
});
});

View File

@ -0,0 +1,32 @@
import {
agHelper,
apiPage,
table,
tedTestConfig,
} from "../../../../support/Objects/ObjectsCore";
import OneClickBinding from "../../../../locators/OneClickBindingLocator";
import FirstTimeUserOnboarding from "../../../../locators/FirstTimeUserOnboarding.json";
describe("Test Create Api and Bind to Table widget V2", function () {
before(() => {
agHelper.AddDsl("tableV2WidgetDsl");
});
it("1. Test_Add users api, execute it and go to sniping mode.", function () {
apiPage.CreateAndFillApi(
tedTestConfig.dsValues[tedTestConfig.defaultEnviorment].mockApiUrl,
);
apiPage.RunAPI();
agHelper.GetNClick(FirstTimeUserOnboarding.selectWidgetBtn);
agHelper.AssertElementVisible(FirstTimeUserOnboarding.snipingBanner);
//Click on table name controller to bind the data and exit sniping mode
agHelper.GetNClick(table._tableV2Widget);
agHelper.AssertContains(
"Api1",
"exist",
OneClickBinding.datasourceDropdownSelector,
);
agHelper.AssertElementAbsence(FirstTimeUserOnboarding.snipingBanner);
});
});

View File

@ -1,19 +1,26 @@
import { featureFlagIntercept } from "../../../../support/Objects/FeatureFlags";
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
const agHelper = ObjectsRegistry.AggregateHelper,
dataSources = ObjectsRegistry.DataSources,
ee = ObjectsRegistry.EntityExplorer;
import {
agHelper,
entityItems,
dataSources,
entityExplorer,
homePage,
} from "../../../../support/Objects/ObjectsCore";
let guid;
let dataSourceName: string;
describe("Datasource form related tests", function () {
before(() => {
homePage.CreateNewWorkspace("FetchSchemaOnce", true);
homePage.CreateAppInWorkspace("FetchSchemaOnce");
});
it("1. Bug - 17238 Verify datasource structure refresh on save - invalid datasource", () => {
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
guid = uid;
dataSourceName = "Postgres " + guid;
ee.ExpandCollapseEntity("Datasources");
entityExplorer.ExpandCollapseEntity("Datasources");
dataSources.NavigateToDSCreateNew();
dataSources.CreatePlugIn("PostgreSQL");
agHelper.RenameWithInPane(dataSourceName, false);
@ -36,14 +43,16 @@ describe("Datasource form related tests", function () {
it("2. Verify if schema was fetched once #18448", () => {
agHelper.RefreshPage();
ee.ExpandCollapseEntity("Datasources");
ee.ExpandCollapseEntity(dataSourceName, false);
cy.intercept("GET", dataSources._getStructureReq).as("getDSStructure");
ee.ExpandCollapseEntity("Datasources");
ee.ExpandCollapseEntity(dataSourceName);
entityExplorer.ExpandCollapseEntity("Datasources");
entityExplorer.ExpandCollapseEntity(dataSourceName, false);
entityExplorer.ExpandCollapseEntity("Datasources");
entityExplorer.ExpandCollapseEntity(dataSourceName);
agHelper.Sleep(1500);
agHelper.VerifyCallCount(`@getDatasourceStructure`, 1);
dataSources.DeleteQuery("Query1");
agHelper.ActionContextMenuWithInPane({
action: "Delete",
entityType: entityItems.Query,
});
dataSources.DeleteDatasouceFromWinthinDS(dataSourceName);
});
@ -61,7 +70,7 @@ describe("Datasource form related tests", function () {
dataSources.CreateMockDB("Users");
dataSources.CreateQueryAfterDSSaved();
dataSources.VerifyTableSchemaOnQueryEditor("public.users");
ee.ExpandCollapseEntity("public.users");
entityExplorer.ExpandCollapseEntity("public.users");
dataSources.VerifyColumnSchemaOnQueryEditor("id");
dataSources.FilterAndVerifyDatasourceSchemaBySearch(
"gender",

View File

@ -9,10 +9,9 @@ let tempBranch3: any;
describe("Git Bugs", function () {
before(() => {
_.homePage.NavigateToHome();
_.agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
_.homePage.CreateNewWorkspace("GitBugs" + uid);
_.homePage.CreateNewWorkspace("GitBugs" + uid, true);
_.homePage.CreateAppInWorkspace("GitBugs" + uid);
});
});

View File

@ -1,43 +1,51 @@
import * as _ from "../../../../support/Objects/ObjectsCore";
import {
agHelper,
locators,
entityExplorer,
propPane,
deployMode,
draggableWidgets,
assertHelper,
} from "../../../../support/Objects/ObjectsCore";
describe("Dynamic Height Width validation for text widget", function () {
before(() => {
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.TEXT);
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TEXT);
});
it("1. Text widget validation of height with dynamic height feature", function () {
const textMsg =
"Dynamic height validation for text widget validation with respect to Auto height";
//changing the Text and verifying
_.propPane.AssertPropertiesDropDownCurrentValue("Height", "Auto Height");
propPane.AssertPropertiesDropDownCurrentValue("Height", "Auto Height");
_.agHelper.GetHeight(_.locators._widgetInDeployed(_.draggableWidgets.TEXT));
propPane.AssertPropertiesDropDownValues("Height", [
"Auto Height",
"Auto Height with limits",
"Fixed",
]);
agHelper.GetHeight(locators._widgetInDeployed(draggableWidgets.TEXT));
cy.get("@eleHeight").then(($initalHeight) => {
_.propPane.UpdatePropertyFieldValue("Text", textMsg);
_.propPane.MoveToTab("Style");
_.propPane.SelectPropertiesDropDown("Font size", "L");
_.assertHelper.AssertNetworkStatus("@updateLayout", 200); //for textMsg update
_.agHelper.GetHeight(
_.locators._widgetInDeployed(_.draggableWidgets.TEXT),
);
propPane.UpdatePropertyFieldValue("Text", textMsg);
propPane.MoveToTab("Style");
propPane.SelectPropertiesDropDown("Font size", "L");
assertHelper.AssertNetworkStatus("@updateLayout", 200); //for textMsg update
agHelper.GetHeight(locators._widgetInDeployed(draggableWidgets.TEXT));
cy.get("@eleHeight").then(($addedtextHeight) => {
expect($addedtextHeight).to.not.equal($initalHeight);
_.deployMode.DeployApp(_.locators._textWidgetInDeployed);
_.agHelper
.GetText(_.locators._textWidgetInDeployed)
.then(($text: any) => {
expect($text).to.eq(textMsg);
});
deployMode.DeployApp(locators._textWidgetInDeployed);
agHelper.GetText(locators._textWidgetInDeployed).then(($text: any) => {
expect($text).to.eq(textMsg);
});
_.agHelper.AssertAttribute(
_.locators._textWidgetStyleInDeployed,
agHelper.AssertAttribute(
locators._textWidgetStyleInDeployed,
"font-size",
"1.25rem", //for Font size 'L'
);
_.agHelper.GetHeight(
_.locators._widgetInDeployed(_.draggableWidgets.TEXT),
);
agHelper.GetHeight(locators._widgetInDeployed(draggableWidgets.TEXT));
cy.get("@eleHeight").then(($deployedAutoHeight) => {
expect($deployedAutoHeight).not.eq($initalHeight);
});

View File

@ -11,8 +11,7 @@ let ee = ObjectsRegistry.EntityExplorer,
describe("Entity explorer API pane related testcases", function () {
it("1. Empty Message validation for Widgets/API/Queries", function () {
homePage.NavigateToHome();
homePage.CreateNewWorkspace("EmptyMsgCheck");
homePage.CreateNewWorkspace("EmptyMsgCheck", true);
homePage.CreateAppInWorkspace("EmptyMsgCheck");
ee.ExpandCollapseEntity("Widgets");
agHelper.AssertElementVisible(

View File

@ -32,11 +32,10 @@ describe("Fork application with multiple datasources", function () {
it("1. Bug Id: 24708 - fork and test the forked application", function () {
// Create a new workspace and fork application
const appname: string = localStorage.getItem("AppName") || "randomApp";
homePage.NavigateToHome();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
workspaceId = "forkApp" + uid;
homePage.CreateNewWorkspace(workspaceId);
homePage.CreateNewWorkspace(workspaceId, true);
agHelper.PressEscape();
cy.log("------------------" + workspaceId);
homePage.ForkApplication(appname, workspaceId);

View File

@ -8,13 +8,12 @@ let currentWorkspace: string, currentAppName: string, forkWorkspaceName: string;
describe("Fork application across workspaces", function () {
it("Bug 24702: Signed user should be able to fork a public forkable app & Check if the forked application has the same dsl as the original", function () {
homePage.NavigateToHome();
// Create new workspace to create App in
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
currentWorkspace = "CurrentWorkspace " + uid;
currentAppName = "MongoQueryApp " + uid;
homePage.CreateNewWorkspace(currentWorkspace);
homePage.CreateNewWorkspace(currentWorkspace, true);
homePage.CreateAppInWorkspace(currentWorkspace, currentAppName);
// Create datasource and query
@ -23,12 +22,11 @@ describe("Fork application across workspaces", function () {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(2000);
homePage.NavigateToHome();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
forkWorkspaceName = "ForkApplication" + uid;
homePage.CreateNewWorkspace(forkWorkspaceName);
homePage.CreateNewWorkspace(forkWorkspaceName, true);
homePage.FilterApplication(currentAppName);
agHelper.Sleep(500);

View File

@ -9,7 +9,6 @@ describe("Mongo Form to Native conversion works", () => {
_.dataSources.CreateQueryAfterDSSaved();
_.assertHelper.AssertNetworkStatus("@trigger");
_.dataSources.EnterJSContext({
fieldProperty: _.dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "listingAndReviews",
});

View File

@ -19,11 +19,10 @@ describe("Import and validate older app (app created in older versions of Appsmi
keyId: any,
workspaceName: any;
before(() => {
homePage.NavigateToHome();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
workspaceName = "GitImport_" + uid;
homePage.CreateNewWorkspace(workspaceName);
homePage.CreateNewWorkspace(workspaceName, true);
});
//Import App From Gitea
gitSync.ImportAppFromGit(workspaceName, appRepoName, true);

View File

@ -79,7 +79,6 @@ describe("MaintainContext&Focus", function () {
cy.wait(1000);
_.dataSources.EnterJSContext({
fieldProperty: _.dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "TestCollection",
});

View File

@ -1,7 +1,7 @@
import homePage from "../../../../locators/HomePage";
import * as _ from "../../../../support/Objects/ObjectsCore";
describe.skip("Visual regression tests", () => {
describe("Visual regression tests", () => {
// for any changes in UI, update the screenshot in snapshot folder, to do so:
// 1. Delete the required screenshot which you want to update.
// 2. Run test in headless mode with any browser

View File

@ -1,6 +1,6 @@
import * as _ from "../../../../support/Objects/ObjectsCore";
describe.skip("Visual tests for datasources", () => {
describe("Visual tests for datasources", () => {
// for any changes in UI, update the screenshot in snapshot folder, to do so:
// 1. Delete the required screenshot which you want to update.
// 2. Run test in headless mode with any browser

View File

@ -3,7 +3,7 @@ import { ObjectsRegistry } from "../../../../support/Objects/Registry";
let jsEditor = ObjectsRegistry.JSEditor,
agHelper = ObjectsRegistry.AggregateHelper;
describe.skip("JSEditor Comment - Visual tests", () => {
describe("JSEditor Comment - Visual tests", () => {
it("1. comments code on the editor", () => {
jsEditor.CreateJSObject(
`export default {

View File

@ -6,7 +6,7 @@ import {
jsEditor,
} from "../../../../support/Objects/ObjectsCore";
describe.skip("JSEditor Indendation - Visual tests", () => {
describe("JSEditor Indendation - Visual tests", () => {
it("6. TC 1933 - jSEditor prettify verification on cloned application", () => {
const appname = localStorage.getItem("AppName");
jsEditor.CreateJSObject(

View File

@ -1,6 +1,6 @@
import * as _ from "../../../../support/Objects/ObjectsCore";
describe.skip("JS Editor Save and Auto-indent: Visual tests", () => {
describe("JS Editor Save and Auto-indent: Visual tests", () => {
it("1. Auto indents and saves the code when Ctrl/Cmd+s is pressed", () => {
_.jsEditor.CreateJSObject(
`export default {

View File

@ -1,4 +1,4 @@
describe.skip("Visual regression tests", () => {
describe("Visual regression tests", () => {
// for any changes in UI, update the screenshot in snapshot folder, to do so:
// 1. Delete the required screenshot which you want to update
// 2. Run test in headless mode with chrome (to maintain same resolution in CI)

View File

@ -7,6 +7,8 @@ import {
entityExplorer,
} from "../../../../../support/Objects/ObjectsCore";
const widgetLocators = require("../../../../../locators/Widgets.json");
let dataSet: any, dsl: any;
describe("Input widget test with default value from chart datapoint", () => {
@ -38,7 +40,7 @@ describe("Input widget test with default value from chart datapoint", () => {
);
deployMode.DeployApp();
agHelper.Sleep(1500); //waiting for chart to load!
agHelper.GetNClick("//*[local-name()='rect']", 13);
agHelper.GetNClick(widgetLocators.chartDataPoint);
cy.get(locators._widgetInputSelector("inputwidgetv2"))
.first()
.invoke("val")

View File

@ -0,0 +1,46 @@
import {
agHelper,
locators,
entityExplorer,
draggableWidgets,
} from "../../../../../support/Objects/ObjectsCore";
describe("Chart renders widget errors", () => {
it("1. If there are syntax errors, the errors are displayed inside the chart widget", function () {
entityExplorer.DragDropWidgetNVerify(draggableWidgets.CHART);
agHelper.AssertContains(
"Sales Report",
"exist",
locators._widgetInDeployed(draggableWidgets.CHART),
);
agHelper.AssertContains(
"Error in Chart Data/Configuration",
"not.exist",
locators._widgetInDeployed(draggableWidgets.CHART),
);
agHelper.EnterActionValue("Series data", "{{Button1.text}}");
agHelper.AssertContains(
"Error in Chart Data/Configuration",
"exist",
locators._widgetInDeployed(draggableWidgets.CHART),
);
agHelper.AssertContains(
"Sales Report",
"not.exist",
locators._widgetInDeployed(draggableWidgets.CHART),
);
});
it("2. shows no chart data available is series data is missing", function () {
agHelper.EnterActionValue("Series data", "");
agHelper.AssertContains(
"No chart data to display",
"exist",
locators._widgetInDeployed(draggableWidgets.CHART),
);
});
});

View File

@ -1,283 +0,0 @@
const viewWidgetsPage = require("../../../../../locators/ViewWidgets.json");
const widgetsPage = require("../../../../../locators/Widgets.json");
import * as _ from "../../../../../support/Objects/ObjectsCore";
describe("Chart Widget Functionality", function () {
before(() => {
_.agHelper.AddDsl("chartUpdatedDsl");
});
beforeEach(() => {
cy.openPropertyPane("chartwidget");
});
it("1. Fill the Chart Widget Properties.", function () {
//changing the Chart Name
/**
* @param{Text} Random Text
* @param{ChartWidget}Mouseover
* @param{ChartPre Css} Assertion
*/
cy.widgetText(
"Test",
viewWidgetsPage.chartWidget,
widgetsPage.widgetNameSpan,
);
cy.EnableAllCodeEditors();
//changing the Chart Title
/**
* @param{Text} Random Input Value
*/
cy.testJsontext("title", this.dataSet.chartIndata);
cy.get(viewWidgetsPage.chartInnerText)
.click()
.contains("App Sign Up")
.should("have.text", "App Sign Up");
//Entering the Chart data
cy.testJsontext(
"chart-series-data-control",
JSON.stringify(this.dataSet.chartInput),
);
cy.get(".t--propertypane").click("right");
// Asserting Chart Height
cy.get(viewWidgetsPage.chartWidget)
.should("be.visible")
.and((chart) => {
expect(chart.height()).to.be.greaterThan(200);
});
//Entring the label of x-axis
cy.get(viewWidgetsPage.xlabel)
.click({ force: true })
.type(this.dataSet.command)
.type(this.dataSet.plan);
//Entring the label of y-axis
cy.get(viewWidgetsPage.ylabel)
.click({ force: true })
.type(this.dataSet.command)
.click({ force: true })
.type(this.dataSet.ylabel);
_.deployMode.DeployApp();
});
it("2. Pie Chart Widget Functionality", function () {
//changing the Chart type
cy.UpdateChartType("Pie chart");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.last()
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.PieChartLabel)
.eq(k)
.should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("3. Line Chart Widget Functionality", function () {
//changing the Chart type
cy.UpdateChartType("Line chart");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.last()
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("4. Bar Chart Widget Functionality", function () {
//changing the Chart type
cy.UpdateChartType("Bar chart");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.eq(k)
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("5. Area Chart Widget Functionality", function () {
//changing the Chart type
cy.UpdateChartType("Area chart");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.last()
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("6. Column Chart Widget Functionality", function () {
//changing the Chart type
cy.UpdateChartType("Column chart");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.eq(k)
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("7. Toggle JS - Pie Chart Widget Functionality", function () {
//changing the Chart type
cy.get(widgetsPage.toggleChartType).click({ force: true });
cy.testJsontext("charttype", "PIE_CHART");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.last()
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.PieChartLabel)
.eq(k)
.should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("8. Toggle JS - Line Chart Widget Functionality", function () {
//changing the Chart type
cy.testJsontext("charttype", "LINE_CHART");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.last()
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("9. Toggle JS - Bar Chart Widget Functionality", function () {
//changing the Chart type
cy.testJsontext("charttype", "BAR_CHART");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.eq(k)
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("10. Toggle JS - Area Chart Widget Functionality", function () {
//changing the Chart type
cy.testJsontext("charttype", "AREA_CHART");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.last()
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("11. Toggle JS - Column Chart Widget Functionality", function () {
//changing the Chart type
cy.testJsontext("charttype", "COLUMN_CHART");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
.eq(k)
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
it("12. Check Chart widget reskinning config", function () {
cy.get(widgetsPage.toggleChartType).click({ force: true });
cy.UpdateChartType("Column chart");
// Check plot fill color
cy.get("g[class*='plot-group'] rect").should(
"have.css",
"fill",
"rgb(85, 61, 233)",
);
// Check axis name font size
cy.get("g[class*='dataset-axis-name'] text").should(
"have.css",
"font-size",
"14px",
);
// Check axis value font size and fill color
cy.get("g[class$='dataset-axis'] text")
.should("have.css", "font-size", "12px")
.should("have.css", "fill", "rgb(113, 110, 110)");
// Check axis caption's fontSize, and fill color
cy.get("g[class$='caption'] text")
.should("have.css", "font-size", "24px")
.should("have.css", "fill", "rgb(35, 31, 32)");
// Check base font family
cy.get(".fusioncharts-container").should(
"have.css",
"font-family",
'"Nunito Sans", sans-serif',
);
cy.UpdateChartType("Pie chart");
cy.get("g[class$='item'] text").should(
"have.css",
"font-family",
'"Nunito Sans"',
);
cy.get("g[class$='labels'] text").should(
"have.css",
"font-family",
'"Nunito Sans"',
);
_.deployMode.DeployApp();
});
afterEach(() => {
_.deployMode.NavigateBacktoEditor();
});
});

View File

@ -1,124 +0,0 @@
const commonlocators = require("../../../../../locators/commonlocators.json");
const viewWidgetsPage = require("../../../../../locators/ViewWidgets.json");
const publish = require("../../../../../locators/publishWidgetspage.json");
const modalWidgetPage = require("../../../../../locators/ModalWidget.json");
const widgetsPage = require("../../../../../locators/Widgets.json");
import * as _ from "../../../../../support/Objects/ObjectsCore";
describe("Chart Widget Functionality", function () {
before(() => {
_.agHelper.AddDsl("chartUpdatedDsl");
});
beforeEach(() => {
cy.openPropertyPane("chartwidget");
});
it("1. Fill the Chart Widget Properties.", function () {
//changing the Chart Name
/**
* @param{Text} Random Text
* @param{ChartWidget}Mouseover
* @param{ChartPre Css} Assertion
*/
cy.widgetText(
"Test",
viewWidgetsPage.chartWidget,
widgetsPage.widgetNameSpan,
);
cy.EnableAllCodeEditors();
//changing the Chart Title
/**
* @param{Text} Random Input Value
*/
cy.testJsontext("title", this.dataSet.chartIndata);
cy.get(viewWidgetsPage.chartInnerText)
.click()
.contains("App Sign Up")
.should("have.text", "App Sign Up");
//Entering the Chart data
cy.testJsontext(
"chart-series-data-control",
JSON.stringify(this.dataSet.chartInput),
);
cy.get(".t--propertypane").click("right");
// Asserting Chart Height
cy.get(viewWidgetsPage.chartWidget)
.should("be.visible")
.and((chart) => {
expect(chart.height()).to.be.greaterThan(200);
});
//Entring the label of x-axis
cy.get(viewWidgetsPage.xlabel)
.click({ force: true })
.type(this.dataSet.command)
.type(this.dataSet.plan);
//Entring the label of y-axis
cy.get(viewWidgetsPage.ylabel)
.click({ force: true })
.type(this.dataSet.command)
.click({ force: true })
.type(this.dataSet.ylabel);
_.deployMode.DeployApp();
});
it("2. Chart - Modal", function () {
//creating the Modal and verify Modal name
cy.createModal(this.dataSet.ModalName, "onDataPointClick");
_.deployMode.DeployApp();
cy.get(widgetsPage.chartPlotGroup).children().first().click();
cy.get(modalWidgetPage.modelTextField).should(
"have.text",
this.dataSet.ModalName,
);
});
it("3. Chart-Unckeck Visible field Validation", function () {
// Making the widget invisible
cy.togglebarDisable(commonlocators.visibleCheckbox);
_.deployMode.DeployApp();
cy.get(publish.chartWidget).should("not.exist");
});
it("4. Chart-Check Visible field Validation", function () {
// Making the widget visible
cy.togglebar(commonlocators.visibleCheckbox);
_.deployMode.DeployApp();
cy.get(publish.chartWidget).should("be.visible");
});
it("5. Toggle JS - Chart-Unckeck Visible field Validation", function () {
//Uncheck the disabled checkbox using JS and validate
cy.get(widgetsPage.toggleVisible).click({ force: true });
cy.testJsontext("visible", "false");
_.deployMode.DeployApp();
cy.get(publish.chartWidget).should("not.exist");
});
it("6. Toggle JS - Chart-Check Visible field Validation", function () {
//Check the disabled checkbox using JS and Validate
cy.testJsontext("visible", "true");
_.deployMode.DeployApp();
cy.get(publish.chartWidget).should("be.visible");
});
it("7. Chart Widget Functionality To Uncheck Horizontal Scroll Visible", function () {
cy.togglebarDisable(commonlocators.allowScroll);
_.deployMode.DeployApp();
cy.get(publish.horizontalTab).should("not.exist");
});
it("8. Chart Widget Functionality To Check Horizontal Scroll Visible", function () {
cy.togglebar(commonlocators.allowScroll);
_.deployMode.DeployApp();
cy.get(publish.horizontalTab).eq(1).should("exist");
});
afterEach(() => {
_.deployMode.NavigateBacktoEditor();
});
});

View File

@ -77,10 +77,12 @@ describe("Chart Widget Functionality around custom chart feature", function () {
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
cy.get(viewWidgetsPage.fusionRectangleChart)
.eq(k)
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
cy.get(viewWidgetsPage.FusionChartlabel)
.eq(k)
.should("have.text", labels[k]);
});
_.deployMode.DeployApp();
});
@ -94,17 +96,19 @@ describe("Chart Widget Functionality around custom chart feature", function () {
"response.body.responseMeta.status",
200,
);
cy.get(viewWidgetsPage.Chartlabel + ":first-child", {
cy.get(viewWidgetsPage.FusionChartlabel + ":first-child", {
timeout: 10000,
}).should("have.css", "opacity", "1");
//Verifying X-axis labels
cy.get(viewWidgetsPage.chartWidget).should("have.css", "opacity", "1");
const labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"];
[0, 1, 2, 3, 4, 5, 6].forEach((k) => {
cy.get(viewWidgetsPage.rectangleChart)
cy.get(viewWidgetsPage.fusionRectangleChart)
.eq(k)
.trigger("mousemove", { force: true });
cy.get(viewWidgetsPage.Chartlabel).eq(k).should("have.text", labels[k]);
cy.get(viewWidgetsPage.FusionChartlabel)
.eq(k)
.should("have.text", labels[k]);
});
//Close edit prop

View File

@ -18,9 +18,10 @@ describe("Dropdown Widget", function () {
it("1. Dropdown-Modal Validation", function () {
entityExplorer.ExpandCollapseEntity("Container3", "Widgets");
entityExplorer.SelectEntityByName("Dropdown1", "Widgets");
cy.EnableAllCodeEditors();
cy.testJsontext("sourcedata", JSON.stringify(this.dataSet.input));
propPane.UpdatePropertyFieldValue(
"Source Data",
JSON.stringify(this.dataSet.input),
);
//creating the Modal and verify Modal name //to fix below
// cy.createModal("Modal1", false);
// deployMode.DeployApp();

View File

@ -57,13 +57,15 @@ describe(" File Picker Widget", function () {
// Test for isValid === True
cy.dragAndDropToWidget("textwidget", "listwidgetv2", {
x: 550,
y: 50,
y: 100,
});
cy.RenameWidgetFromPropertyPane("textwidget", "Text1", "FilePicker_Widget");
propPane.RenameWidget("Text1", "FilePicker_Widget");
propPane.UpdatePropertyFieldValue(
"Text",
"{{currentView.FilePicker1.isDirty}}_{{currentView.FilePicker1.isValid}}_{{currentView.FilePicker1.files[0]?.name}}",
false,
);
cy.get(
`${widgetSelector("FilePicker_Widget")} ${commonlocators.bodyTextStyle}`,

View File

@ -77,7 +77,7 @@ describe("Input Widgets", function () {
it("2. Input Widgets isValid", function () {
// Test for isValid === True
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.TEXT, 600, 300);
cy.RenameWidgetFromPropertyPane("textwidget", "Text1", "Input_Widget");
_.propPane.RenameWidget("Text1", "Input_Widget");
cy.wait(1000);
_.propPane.UpdatePropertyFieldValue(
"Text",
@ -87,8 +87,9 @@ describe("Input Widgets", function () {
.first()
.should("have.text", "true");
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.TEXT, 600, 100);
cy.RenameWidgetFromPropertyPane("textwidget", "Text1", "Currency_Widget");
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.TEXT, 700, 100);
_.propPane.RenameWidget("Text1", "Currency_Widget");
cy.wait(1000);
_.propPane.UpdatePropertyFieldValue(
"Text",
@ -100,9 +101,8 @@ describe("Input Widgets", function () {
.first()
.should("have.text", "true");
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.TEXT, 600, 200);
cy.RenameWidgetFromPropertyPane("textwidget", "Text1", "PhoneInput_Widget");
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.TEXT, 500, 400);
_.propPane.RenameWidget("Text1", "PhoneInput_Widget");
cy.wait(1000);
_.propPane.UpdatePropertyFieldValue(
"Text",

View File

@ -3,6 +3,7 @@ import {
deployMode,
entityExplorer,
jsEditor,
locators,
} from "../../../../../support/Objects/ObjectsCore";
const commonlocators = require("../../../../../locators/commonlocators.json");
@ -78,10 +79,9 @@ describe("List widget V2 Serverside Pagination", () => {
cy.wrap(data).should("deep.equal", {});
});
// Select First Row
cy.get(`${widgetSelector("List1")} ${containerWidgetSelector}`)
.eq(0)
.click();
// Select First Row in List
agHelper.GetNClick(locators._imgWidgetInsideList, 0, true);
cy.wait(200);
cy.get(

View File

@ -85,25 +85,12 @@ describe(" Nested List Widgets ", function () {
y: 100,
},
);
propPane.RemoveText("Text");
propPane.TypeTextIntoField("Text", "{{level_1.currentView.");
cy.get(".t--property-control-text .CodeMirror textarea").type(
"{{level_1.currentView.",
{
force: true,
},
);
checkAutosuggestion("Text1", "Object");
checkAutosuggestion("List1Copy", "Object");
propPane.RemoveText("Text", false);
cy.get(".t--property-control-text .CodeMirror textarea").type(
"{{level_1.currentView.List1Copy.",
{
force: true,
},
);
propPane.TypeTextIntoField("Text", "{{level_1.currentView.List1Copy.");
checkAutosuggestion("backgroundColor", "String");
checkAutosuggestion("itemSpacing", "Number");
checkAutosuggestion("isVisible", "Boolean");
@ -123,10 +110,7 @@ describe(" Nested List Widgets ", function () {
cy.wrap($el).should("not.have.text", "triggeredItemView");
});
cy.get(".CodeMirror-hints")
.contains("pageNo")
.first()
.click({ force: true });
agHelper.GetNClickByContains(".CodeMirror-hints", "pageNo", 0, true);
cy.get(`${widgetSelector("Text2")} .bp3-ui-text span`).should(
"have.text",

View File

@ -4,6 +4,8 @@ import {
agHelper,
draggableWidgets,
entityExplorer,
locators,
propPane,
} from "../../../../../support/Objects/ObjectsCore";
const toggleJSButton = (name) => `.t--property-control-${name} .t--js-toggle`;
const widgetSelector = (name) => `[data-widgetname-cy="${name}"]`;
@ -69,10 +71,11 @@ describe("List widget v2 onItemClick", () => {
});
it("2. List widget V2 with onItemClick should be triggered when child widget without event is clicked", () => {
cy.get(widgetSelector("Image1")).first().click({ force: true });
//Select first row Image within list
agHelper.GetNClick(locators._imgWidgetInsideList, 0, true);
agHelper.WaitUntilToastDisappear("ListWidget_Blue_0");
cy.get(widgetSelector("Text1")).first().click({ force: true });
agHelper.GetNClickByContains(locators._textWidget, "Blue", 0, true);
agHelper.WaitUntilToastDisappear("ListWidget_Blue_0");
deleteAllWidgetsInContainer();
@ -84,10 +87,8 @@ describe("List widget v2 onItemClick", () => {
draggableWidgets.CONTAINER,
);
cy.get(`${widgetSelector("Input1")} input`)
.first()
.click({ force: true });
validateToastDoestExist();
agHelper.GetNClick(`${locators._widgetByName("Input1")} input`, 0, true);
agHelper.AssertElementAbsence(locators._toastMsg);
deleteAllWidgetsInContainer();
@ -98,10 +99,9 @@ describe("List widget v2 onItemClick", () => {
draggableWidgets.CONTAINER,
);
cy.get(`${widgetSelector("Select1")} button`)
.first()
.click({ force: true });
validateToastDoestExist();
//This is clicking Select Widget
agHelper.ClickButton("Green", 0);
agHelper.AssertElementAbsence(locators._toastMsg);
deleteAllWidgetsInContainer();
@ -112,20 +112,13 @@ describe("List widget v2 onItemClick", () => {
draggableWidgets.CONTAINER,
);
cy.get(`${widgetSelector("Button1")} button`)
.first()
.click({ force: true });
agHelper.ClickButton("Submit", 0);
agHelper.WaitUntilToastDisappear("ListWidget_Blue_0");
cy.get(widgetsPage.toggleOnClick).click({ force: true });
cy.get(".t--property-control-onclick").then(($el) => {
cy.updateCodeInput($el, "{{clearStore()}}");
});
cy.wait(1000);
propPane.EnterJSContext("onClick", "{{clearStore()}}");
agHelper.Sleep(1000);
cy.get(`${widgetSelector("Button1")} button`)
.first()
.click({ force: true });
validateToastDoestExist();
agHelper.ClickButton("Submit", 0);
agHelper.AssertElementAbsence(locators._toastMsg);
});
});

View File

@ -4,6 +4,7 @@ import {
agHelper,
entityExplorer,
draggableWidgets,
propPane,
} from "../../../../../support/Objects/ObjectsCore";
describe("Video Widget Functionality", function () {
@ -70,27 +71,32 @@ describe("Video Widget Functionality", function () {
});
it("4. Checks if video widget is reset on button click", function () {
cy.testCodeMirror(testdata.videoUrl2);
propPane.UpdatePropertyFieldValue("URL", testdata.videoUrl2);
entityExplorer.DragDropWidgetNVerify(draggableWidgets.BUTTON, 200, 200);
cy.selectResetWidget("onClick");
cy.selectWidgetForReset("Video1");
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TEXT, 300, 300);
cy.updateCodeInput(".t--property-control-text", `{{Video1.playState}}`);
propPane.UpdatePropertyFieldValue("Text", "{{Video1.playState}}");
agHelper.Sleep(1500); // Wait time added for the widget to load current video state
cy.openPropertyPane("videowidget");
cy.get(widgetsPage.autoPlay).click({ force: true });
// Wait time added, allowing a second to pass between playing and pausing the widget, before it is reset to zero
cy.wait(1000);
cy.get(widgetsPage.autoPlay).click({ force: true });
cy.get(widgetsPage.widgetBtn).click({ force: true });
propPane.TogglePropertyState("Autoplay", "On");
agHelper.Sleep(1500); // Wait time added for the widget to load current video state
cy.get(".t--widget-textwidget").should("contain", "ENDED");
propPane.TogglePropertyState("Autoplay", "Off");
agHelper.Sleep(1500); // Wait time added, allowing a second to pass between playing and pausing the widget, before it is reset to zero
cy.get(".t--widget-textwidget").should("contain", "PAUSED");
agHelper.ClickButton("Submit");
cy.wait(1000);
cy.get(`${widgetsPage.videoWidget} video`).then(($video) => {
const video = $video.get(0);
expect(video.currentTime).to.equal(0);
});
agHelper.Sleep(1500); // Wait time added for the widget to load current video state
cy.get(".t--widget-textwidget").should("contain", "NOT_STARTED");
});

View File

@ -59,9 +59,15 @@ describe("Admin settings page", function () {
cy.go(-1);
}
});
it("3. should test that Business features shows upgrade button and direct to pricing page", () => {
cy.visit("/settings/general", { timeout: 60000 });
if (CURRENT_REPO === REPO.CE) {
cy.get(adminsSettings.accessControl).within(() => {
cy.get(adminsSettings.businessTag)
.should("exist")
.should("contain", "Business");
});
cy.get(adminsSettings.accessControl).click();
cy.url().should("contain", "/settings/access-control");
stubPricingPage();
@ -69,6 +75,11 @@ describe("Admin settings page", function () {
cy.get("@pricingPage").should("be.called");
cy.wait(2000);
cy.go(-1);
cy.get(adminsSettings.auditLogs).within(() => {
cy.get(adminsSettings.businessTag)
.should("exist")
.should("contain", "Business");
});
cy.get(adminsSettings.auditLogs).click();
cy.url().should("contain", "/settings/audit-logs");
stubPricingPage();
@ -76,6 +87,18 @@ describe("Admin settings page", function () {
cy.get("@pricingPage").should("be.called");
cy.wait(2000);
cy.go(-1);
cy.get(adminsSettings.provisioning).within(() => {
cy.get(adminsSettings.businessTag)
.should("exist")
.should("contain", "Enterprise");
});
cy.get(adminsSettings.provisioning).click();
cy.url().should("contain", "/settings/provisioning");
stubPricingPage();
cy.xpath(adminsSettings.upgrade).click();
cy.get("@pricingPage").should("be.called");
cy.wait(2000);
cy.go(-1);
}
});
});

View File

@ -0,0 +1,232 @@
import {
agHelper,
entityExplorer,
deployMode,
appSettings,
dataSources,
table,
locators,
assertHelper,
draggableWidgets,
} from "../../../../support/Objects/ObjectsCore";
import { Widgets } from "../../../../support/Pages/DataSources";
describe("Validate Mongo URI CRUD with JSON Form", () => {
let dsName: any;
it("1. Create DS & Generate CRUD template", () => {
dataSources.NavigateToDSCreateNew();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
dataSources.CreatePlugIn("MongoDB");
dsName = "Mongo" + uid;
agHelper.RenameWithInPane(dsName, false);
dataSources.FillMongoDatasourceFormWithURI();
dataSources.TestSaveDatasource();
entityExplorer.AddNewPage("Generate page with data");
agHelper.GetNClick(dataSources._selectDatasourceDropdown);
agHelper.GetNClickByContains(dataSources._dropdownOption, dsName);
assertHelper.AssertNetworkStatus("@getDatasourceStructure"); //Making sure table dropdown is populated
agHelper.GetNClick(dataSources._selectTableDropdown, 0, true);
agHelper.GetNClickByContains(dataSources._dropdownOption, "mongomart");
GenerateCRUDNValidateDeployPage(
"/img/products/mug.jpg",
"Coffee Mug",
`Kitchen`,
4,
);
deployMode.NavigateBacktoEditor();
table.WaitUntilTableLoad();
//Should not be able to delete ds until app is published again
//coz if app is published & shared then deleting ds may cause issue, So!
dataSources.DeleteDatasouceFromActiveTab(dsName as string, 409);
});
});
it("2. Verify Update data from Deploy page - on mongomart - existing record", () => {
//Update documents query to handle the int _id data
entityExplorer.NavigateToSwitcher("Explorer", 0, true);
entityExplorer.SelectEntityByName("UpdateQuery");
agHelper.EnterValue(`{ _id: {{data_table.selectedRow._id}}}`, {
propFieldName: "",
directInput: false,
inputFieldName: "Query",
});
deployMode.DeployApp(locators._widgetInDeployed(draggableWidgets.TABLE_V1));
agHelper.GetNAssertElementText(
locators._textWidgetInDeployed,
"mongomart Data",
);
//Validating loaded table
table.SelectTableRow(2);
agHelper.AssertElementExist(dataSources._selectedRow);
table.ReadTableRowColumnData(2, 0, "v1", 200).then(($cellData) => {
expect($cellData).to.be.empty;
});
table.ReadTableRowColumnData(2, 6, "v1", 2000).then(($cellData) => {
expect($cellData).to.eq("WiredTiger T-shirt");
});
table.ReadTableRowColumnData(2, 7, "v1", 200).then(($cellData) => {
expect($cellData).to.eq("Apparel");
});
table.SelectTableRow(8);
deployMode.ClearJSONFieldValue("Slogan");
deployMode.ClearJSONFieldValue("Category");
agHelper.ClickButton("Update");
agHelper.AssertElementAbsence(locators._toastMsg); //Validating fix for Bug 14063
for (let i = 7; i <= 8; i++) {
table.ReadTableRowColumnData(8, i, "v1").then(($cellData) => {
expect($cellData).to.be.empty;
});
}
deployMode.EnterJSONInputValue(
"Slogan",
"Write Your Story with Elegance: The Pen of Choice!",
);
agHelper.GetNClick(deployMode._jsonFormNumberFieldByName("Stars", "up")); //1
agHelper.GetNClick(deployMode._jsonFormNumberFieldByName("Stars", "up")); //2
agHelper.GetNClick(deployMode._jsonFormNumberFieldByName("Stars", "up")); //3
agHelper.ClickButton("Update");
agHelper.AssertElementAbsence(locators._toastMsg); //Validating fix for Bug 14063
table.ReadTableRowColumnData(8, 8, "v1", 200).then(($cellData) => {
expect($cellData).to.eq(
"Write Your Story with Elegance: The Pen of Choice!",
);
});
table.ReadTableRowColumnData(8, 5, "v1", 200).then(($cellData) => {
expect($cellData).to.eq("3");
});
});
it("3. Verify Add/Insert from Deploy page - on MongoMart - new record - few validations", () => {
agHelper.GetNClick(dataSources._addIcon);
agHelper.Sleep();
//agHelper.AssertElementVisible(locators._jsonFormWidget, 1); //Insert Modal
agHelper.AssertElementVisible(locators._visibleTextDiv("Insert Document"));
agHelper.AssertElementEnabledDisabled(
locators._spanButton("Submit") + "/parent::div",
0,
false,
);
agHelper.ClickButton("Submit");
for (let i = 0; i <= 1; i++) {
table.ReadTableRowColumnData(i, 6, "v1").then(($cellData) => {
expect($cellData).contains("Coffee Mug");
});
}
});
it("4. Verify Delete from Deploy page - on MongoMart - newly added record", () => {
agHelper.ClickButton("Delete", 0);
agHelper.AssertElementVisible(locators._modal);
agHelper.AssertElementVisible(
dataSources._visibleTextSpan(
"Are you sure you want to delete this document?",
),
);
agHelper.ClickButton("Confirm");
assertHelper.AssertNetworkStatus("@postExecute", 200);
assertHelper.AssertNetworkStatus("@postExecute", 200);
table.ReadTableRowColumnData(0, 6, "v1", 200).then(($cellData) => {
expect($cellData).to.eq("Coffee Mug");
});
table.ReadTableRowColumnData(1, 6, "v1", 200).then(($cellData) => {
expect($cellData).to.eq("Track Jacket");
});
});
it("5 Verify Filter & Search & Download from Deploy page - on MongoMart - existing record", () => {
table.SearchTable("Swag");
agHelper.Sleep(2500); //for search to load
for (let i = 0; i <= 1; i++) {
table.ReadTableRowColumnData(i, 6, "v1").then(($cellData) => {
expect($cellData).to.eq("Swag");
});
}
table.ResetSearch();
table.OpenNFilterTable("title", "contains", "USB");
for (let i = 0; i < 3; i++) {
table.ReadTableRowColumnData(i, 5, "v1").then(($cellData) => {
expect($cellData).contains("USB");
});
}
table.CloseFilter();
table.DownloadFromTable("Download as CSV");
table.ValidateDownloadNVerify("data_table.csv", "USB Stick (Green)");
table.DownloadFromTable("Download as Excel");
table.ValidateDownloadNVerify("data_table.xlsx", "USB Stick (Leaf)");
table.OpenFilter();
table.RemoveFilter();
agHelper
.GetText(table._filtersCount)
.then(($filters) => expect($filters).to.eq("Filters"));
});
it("6. Suggested Widget - Table", () => {
table.SelectTableRow(8);
agHelper.GetNClick(deployMode._jsonFormNumberFieldByName("Stars", "down")); //2
agHelper.GetNClick(deployMode._jsonFormNumberFieldByName("Stars", "down")); //1
agHelper.GetNClick(deployMode._jsonFormNumberFieldByName("Stars", "down")); //0
agHelper.ClickButton("Update");
deployMode.NavigateBacktoEditor();
table.WaitUntilTableLoad();
entityExplorer.AddNewPage();
dataSources.NavigateFromActiveDS(dsName, true);
dataSources.ValidateNSelectDropdown("Collection", "", "mongomart");
dataSources.RunQuery({ toValidateResponse: false });
dataSources.AddSuggesstedWidget(Widgets.Table);
table.ReadTableRowColumnData(0, 3, "v2").then((cellData) => {
expect(cellData).to.eq("1");
});
});
});
function GenerateCRUDNValidateDeployPage(
col1Text: string,
col6Text: string,
col7Text: string,
idIndex: number,
) {
agHelper.GetNClick(dataSources._generatePageBtn);
assertHelper.AssertNetworkStatus("@replaceLayoutWithCRUDPage", 201);
agHelper.AssertContains("Successfully generated a page"); // Commenting this since FindQuery failure appears sometimes
assertHelper.AssertNetworkStatus("@getActions", 200);
assertHelper.AssertNetworkStatus("@postExecute", 200);
agHelper.GetNClick(dataSources._visibleTextSpan("Got it"));
assertHelper.AssertNetworkStatus("@updateLayout", 200);
appSettings.OpenPaneAndChangeTheme("Pacific");
deployMode.DeployApp(locators._widgetInDeployed("tablewidget"));
//Validating loaded table
agHelper.AssertElementExist(dataSources._selectedRow);
table.ReadTableRowColumnData(0, 1, "v1", 2000).then(($cellData) => {
expect($cellData).to.eq(col1Text);
});
table.ReadTableRowColumnData(0, 6, "v1", 200).then(($cellData) => {
expect($cellData).to.eq(col6Text);
});
table.ReadTableRowColumnData(0, 7, "v1", 200).then(($cellData) => {
expect($cellData).to.eq(col7Text);
});
//Validating loaded JSON form
cy.xpath(locators._spanButton("Update")).then((selector) => {
cy.wrap(selector)
.invoke("attr", "class")
.then((classes) => {
//cy.log("classes are:" + classes);
expect(classes).not.contain("bp3-disabled");
});
});
dataSources.AssertJSONFormHeader(0, idIndex, "Id", "", true);
}

View File

@ -3,6 +3,8 @@ const datasourceEditor = require("../../../../locators/DatasourcesEditor.json");
const commonlocators = require("../../../../locators/commonlocators.json");
import {
agHelper,
dataSources,
entityExplorer,
deployMode,
homePage,
@ -34,13 +36,8 @@ describe("Generate New CRUD Page Inside from entity explorer", function () {
cy.wrap(datasourceName).as("dSName");
});
//TestData source
cy.get(".t--test-datasource").click();
cy.wait("@testDatasource");
//Save source
cy.get(".t--save-datasource").click();
//TestData & save datasource
dataSources.TestSaveDatasource();
// fetch bucket
cy.wait("@getDatasourceStructure").should(
"have.nested.property",
@ -48,13 +45,13 @@ describe("Generate New CRUD Page Inside from entity explorer", function () {
200,
);
cy.get(generatePage.selectTableDropdown).click();
cy.get(generatePage.dropdownOption)
.contains("assets-test.appsmith.com")
.scrollIntoView()
.should("be.visible")
.click();
cy.get(generatePage.generatePageFormSubmitBtn).click();
agHelper.AssertContains("Generate from data");
agHelper.GetNClick(generatePage.selectTableDropdown);
agHelper.GetNClickByContains(
generatePage.dropdownOption,
"assets-test.appsmith.com",
);
agHelper.GetNClick(generatePage.generatePageFormSubmitBtn);
cy.wait("@put_replaceLayoutCRUD").should(
"have.nested.property",

View File

@ -83,7 +83,6 @@ describe("Validate Mongo query commands", function () {
);
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "listingAndReviews",
});
@ -153,7 +152,6 @@ describe("Validate Mongo query commands", function () {
"Count",
);
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "listingAndReviews",
});
@ -182,7 +180,6 @@ describe("Validate Mongo query commands", function () {
"Distinct",
);
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "listingAndReviews",
});
@ -215,7 +212,6 @@ describe("Validate Mongo query commands", function () {
"Aggregate",
);
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "listingAndReviews",
});
@ -411,7 +407,6 @@ describe("Validate Mongo query commands", function () {
{"_id":3, "Från" :"Olivia" , "Frõ" :"Active", "Leverantör":"De Bolster", "Frö":"Sallad - Oakleaf 'Red Salad Bowl'"}]`;
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "NonAsciiTest",
});

View File

@ -287,7 +287,6 @@ describe("Validate Mongo Query Pane Validations", () => {
);
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "AuthorNAwards",
});
@ -777,7 +776,6 @@ describe("Validate Mongo Query Pane Validations", () => {
agHelper.RenameWithInPane("InsertBirthNDeath");
dataSources.EnterJSContext({
fieldProperty: dataSources._mongoCollectionPath,
fieldLabel: "Collection",
fieldValue: "BirthNDeath",
});

View File

@ -8,7 +8,7 @@ import {
} from "../../../support/Objects/ObjectsCore";
let dsName: any, jsonSpecies: any, offset: any, insertedRecordId: any;
describe.skip("excludeForAirgap", "Validate Airtable Ds", () => {
describe("excludeForAirgap", "Validate Airtable Ds", () => {
before("Create a new Airtable DS", () => {
dataSources.CreateDataSource("Airtable", true, false);
cy.get("@dsName").then(($dsName) => {
@ -259,7 +259,8 @@ describe.skip("excludeForAirgap", "Validate Airtable Ds", () => {
});
});
it("2. Create/Retrieve/Update/Delete records", () => {
//Skipping since Create records sometimes not giving results
it.skip("2. Create/Retrieve/Update/Delete records", () => {
let createReq = `[{"fields": {
"Species_ID": "SF",
"Genus": "Sigmodon",
@ -379,6 +380,6 @@ describe.skip("excludeForAirgap", "Validate Airtable Ds", () => {
action: "Delete",
entityType: entityItems.Datasource,
});
assertHelper.AssertNetworkStatus("@deleteDatasource", 200);
//assertHelper.AssertNetworkStatus("@deleteDatasource", 200); //to uncomment after 2nd case is fixed
});
});

View File

@ -41,7 +41,8 @@ describe("Authenticated API Datasource", function () {
dataSources.DeleteDatasouceFromActiveTab(dsName);
});
it("4. Bug: 18051 - Save and Authorise should return to datasource page in view mode and not new datasource page", () => {
//skipping this test as it is failing in pipeline - "authorizationURL": "https://oauth.mocklab.io/oauth/authorize",
it.skip("4. Bug: 18051 - Save and Authorise should return to datasource page in view mode and not new datasource page", () => {
cy.NavigateToAPI_Panel();
cy.get(apiwidget.createAuthApiDatasource).click();
cy.generateUUID().then((uuid) => {

View File

@ -241,12 +241,8 @@ describe("Validate MsSQL connection & basic querying with UI flows", () => {
});
after("Verify Deletion of the datasource", () => {
entityExplorer.SelectEntityByName(dsName, "Datasources");
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: dsName,
action: "Delete",
entityType: entityItems.Datasource,
});
cy.intercept("DELETE", "/api/v1/datasources/*").as("deleteDatasource"); //Since intercept from before is not working
dataSources.DeleteDatasouceFromWinthinDS(dsName);
//dataSources.StopNDeleteContainer(containerName); //commenting to check if MsSQL specific container deletion is causing issues
});

View File

@ -12,10 +12,8 @@ describe("Datasource form OAuth2 client credentials related tests", function ()
it("1. Create an API with app url and save as Datasource for Client Credentials test", function () {
apiPage.CreateAndFillApi(testdata.appUrl, "TestOAuth");
agHelper.GetNClick(apiPage._saveAsDS);
// agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566
});
it("2. Add Oauth details to datasource and save", function () {
// Add Oauth details to datasource and save
cy.get(datasource.saveBtn).should("not.be.disabled");
dataSources.AddOAuth2AuthorizationCodeDetails(
testdata.accessTokenUrl,
@ -27,7 +25,7 @@ describe("Datasource form OAuth2 client credentials related tests", function ()
// since we are moving to different, it will show unsaved changes dialog
// save datasource and then proceed
dataSources.SaveDatasource();
agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566
entityExplorer.SelectEntityByName("TestOAuth", "Queries/JS");
agHelper.ActionContextMenuWithInPane({
action: "Delete",
@ -35,13 +33,10 @@ describe("Datasource form OAuth2 client credentials related tests", function ()
});
});
it("3. Create an API with app url and save as Datasource for Authorization code details test", function () {
it("2. Create an API with app url and save as Datasource for Authorization code details test", function () {
apiPage.CreateAndFillApi(testdata.appUrl, "TestOAuth");
agHelper.GetNClick(apiPage._saveAsDS);
// agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566
});
it("4. Add Oauth details to datasource and save", function () {
//Add Oauth details to datasource and save
cy.get(datasource.saveBtn).should("not.be.disabled");
dataSources.AddOAuth2AuthorizationCodeDetails(
testdata.accessTokenUrl,
@ -51,7 +46,8 @@ describe("Datasource form OAuth2 client credentials related tests", function ()
);
});
it("5. Validate save and Authorise", function () {
//skipping this test as it is failing in pipeline - "authorizationURL": "https://oauth.mocklab.io/oauth/authorize",
it.skip("3. Validate save and Authorise", function () {
cy.get(datasource.saveAndAuthorize).click();
cy.contains("#login-submit", "Login");
cy.url().should("include", "oauth.mocklab.io/oauth/authorize");

View File

@ -0,0 +1,192 @@
export const GSHEET_DATA = [
{
rowIndex: "0",
uniq_id: "eac7efa5dbd3d667f26eb3d3ab504464",
japanese_name: "ホーンビィ 2014 カタログ",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name: "Hornby 2014 Catalogue",
manufacturer: "Hornby",
price: "3.42",
number_available_in_stock: "5 new",
number_of_reviews: "15",
number_of_answered_questions: "1",
average_review_rating: "4.9 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Rail Vehicles > Trains",
customers_who_bought_this_item_also_bought:
"http://www.amazon.co.uk/Hornby-R8150-Catalogue-2015/dp/B00S9SUUBE | http://www.amazon.co.uk/Hornby-Book-Model-Railways-Edition/dp/1844860957 | http://www.amazon.co.uk/Hornby-Book-Scenic-Railway-Modelling/dp/1844861120 | http://www.amazon.co.uk/Peco-60-Plans-Book/dp/B002QVL16I | http://www.amazon.co.uk/Hornby-Gloucester | http://www.amazon.co.uk/Airfix-5014429781902",
},
{
rowIndex: "1",
uniq_id: "b17540ef7e86e461d37f3ae58b7b72ac",
japanese_name:
"FunkyBuys® ラージ クリスマス ホリデー エクスプレス フェスティバル トレイン セット (SI-TY1017) おもちゃ ライト / サウンド / 電池式 & 煙",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name:
"FunkyBuys® Large Christmas Holiday Express Festive Train Set (SI-TY1017) Toy Light / Sounds / Battery Operated & Smoke",
manufacturer: "FunkyBuys",
price: "16.99",
number_available_in_stock: "null",
number_of_reviews: "2",
number_of_answered_questions: "1",
average_review_rating: "4.5 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Rail Vehicles > Trains",
customers_who_bought_this_item_also_bought:
"http://www.amazon.co.uk/Christmas-Holiday-Express-Festive-Train-Set-Toy/dp/B009R8S8AA | http://www.amazon.co.uk/Goldlok-Holiday-Express-Operated-Multi-Colour/dp/B009R8PAO2 | http://www.amazon.co.uk/FunkyBuys%C2%AE-Christmas-SI-TY1017-Ornaments-Operated/dp/B01437QMHA | http://www.amazon.co.uk/Holiday-Express-Christmas-Ornament-Decoration | http://www.amazon.co.uk/Seasonal-Vision-Christmas-Tree-Train/dp/B0044ZC1W2 | http://www.amazon.co.uk/Coca-Cola-Santa-Express-Train-Set/dp/B004BYSNU0",
},
{
rowIndex: "2",
uniq_id: "348f344247b0c1a935b1223072ef9d8a",
japanese_name:
"クラシックなおもちゃの電車セット トラック車両 ライトエンジン 箱入り男の子 子供用バッテリー",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name:
"CLASSIC TOY TRAIN SET TRACK CARRIAGES LIGHT ENGINE BOXED BOYS KIDS BATTERY",
manufacturer: "ccf",
price: "9.99",
number_available_in_stock: "2 new",
number_of_reviews: "17",
number_of_answered_questions: "2",
average_review_rating: "3.9 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Rail Vehicles > Trains",
customers_who_bought_this_item_also_bought:
"http://www.amazon.co.uk/Classic-Train-Lights-Battery-Operated/dp/B0041L9OHE | http://www.amazon.co.uk/Train-With-Tracks-Battery-Operated-x/dp/B009P540O8 | http://www.amazon.co.uk/13-Piece-Train-Set-Ideal/dp/B0173N6E4W | http://www.amazon.co.uk/Train-Flash-Electric-Sound-Europe/dp/B008D7CEH4 | http://www.amazon.co.uk/Train-Ultimate-Sticker-Book-Stickers/dp/1405314516 | http://www.amazon.co.uk/Train-Stickers-Dover-Little-Activity/dp/0486403106",
},
{
rowIndex: "3",
uniq_id: "e12b92dbb8eaee78b22965d2a9bbbd9f",
japanese_name: "ホーンビー コーチ R4410A BR ホークスワース コリドー 3",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name: "HORNBY Coach R4410A BR Hawksworth Corridor 3rd",
manufacturer: "Hornby",
price: "39.99",
number_available_in_stock: "null",
number_of_reviews: "1",
number_of_answered_questions: "2",
average_review_rating: "5.0 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Rail Vehicles > Trains",
customers_who_bought_this_item_also_bought: "null",
},
{
rowIndex: "4",
uniq_id: "e33a9adeed5f36840ccc227db4682a36",
japanese_name:
"ホーンビー 00 ゲージ 0-4-0 ギルデンロー塩社 蒸気機関車モデル",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name:
"Hornby 00 Gauge 0-4-0 Gildenlow Salt Co. Steam Locomotive Model",
manufacturer: "Hornby",
price: "32.19",
number_available_in_stock: "null",
number_of_reviews: "3",
number_of_answered_questions: "2",
average_review_rating: "4.7 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Rail Vehicles > Trains",
customers_who_bought_this_item_also_bought:
"http://www.amazon.co.uk/Hornby-R6367-RailRoad-Gauge-Rolling/dp/B000WDWSD2 | http://www.amazon.co.uk/Hornby-R3064-RailRoad-Smokey-Locomotive | http://www.amazon.co.uk/Hornby-R8222-Gauge-Track-Extension/dp/B000RK3FZK | http://www.amazon.co.uk/Hornby-R6371-RailRoad-Petrol-Tanker/dp/B000WDS002 | http://www.amazon.co.uk/Hornby-R076-00-Gauge-Footbridge | http://www.amazon.co.uk/Hornby-R6368-RailRoad-Gauge-Brake/dp/B000WDWT22",
},
{
rowIndex: "5",
uniq_id: "cb34f0a84102c1ebc3ef6892d7444d36",
japanese_name: "20 個モデルガーデンライトダブルヘッド街灯スケール 1:100",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name: "20pcs Model Garden Light Double Heads Lamppost Scale 1:100",
manufacturer: "Generic",
price: "6.99",
number_available_in_stock: "null",
number_of_reviews: "2",
number_of_answered_questions: "1",
average_review_rating: "5.0 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Lighting & Signal Engineering > Lamps & Lighting",
customers_who_bought_this_item_also_bought:
"http://www.amazon.co.uk/Single-Head-Garden-Lights-Lamppost-Layout/dp/B008XCSHCA | http://www.amazon.co.uk/douself-100Pcs-OO-Scale-Passenger/dp/B00GRUD8W4 | http://www.amazon.co.uk/Hornby-Digital-Electric-Point-Track/dp/B00105UJ14 | http://www.amazon.co.uk/20Pcs-Scenery-Landscape-Train-Flowers/dp/B00C1843MA | http://www.amazon.co.uk/Scenery-Landscape-100-Made-Plastic-Cement/dp/B007UYIJ48",
},
{
rowIndex: "6",
uniq_id: "f74b562470571dfb689324adf236f82c",
japanese_name:
"Hornby 00 ゲージ 230mm BR ボギー 旅客ブレーキ コーチ モデル (レッド)",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name:
"Hornby 00 Gauge 230mm BR Bogie Passenger Brake Coach Model (Red)",
manufacturer: "Hornby",
price: "24.99",
number_available_in_stock: "null",
number_of_reviews: "2",
number_of_answered_questions: "1",
average_review_rating: "4.5 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Rail Vehicles > Trains",
customers_who_bought_this_item_also_bought:
"http://www.amazon.co.uk/Hornby-R4388-RailRoad-Composite-Gauge/dp/B00260GEXO | http://www.amazon.co.uk/Hornby-R1138-Passenger-Freight-Electric/dp/B006ZL6976",
},
{
rowIndex: "7",
uniq_id: "87bbb472ef9d90dcef140a551665c929",
japanese_name: "ホーンビー サンタの特急列車セット",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name: "Hornby Santa's Express Train Set",
manufacturer: "Hornby",
price: "69.93",
number_available_in_stock: "3 new",
number_of_reviews: "36",
number_of_answered_questions: "7",
average_review_rating: "4.3 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Rail Vehicles > Trains",
customers_who_bought_this_item_also_bought:
"http://www.amazon.co.uk/Hornby-R8221-Gauge-Track-Extension/dp/B000PVFYZ0 | http://www.amazon.co.uk/Hornby-R8222-Gauge-Track-Extension/dp/B000RK3FZK | http://www.amazon.co.uk/Hornby-R6368-RailRoad-Gauge-Brake/dp/B000WDWT22 | http://www.amazon.co.uk/Hornby-R6370-RailRoad-Tredegar-Gauge/dp/B000WDZH58 | http://www.amazon.co.uk/Hornby-R044-Passing-Contact-Switch/dp/B000H5V0RK | http://www.amazon.co.uk/Hornby-Gauge-Logan-Plank-Wagon/dp/B00SWV6RAG",
},
{
rowIndex: "8",
uniq_id: "7e2aa2b4596a39ba852449718413d7cc",
japanese_name:
"ホーンビー ゲージ ウェスタン エクスプレス デジタル トレイン セット (eLink および TTS ロコ トレイン セット付き)",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name:
"Hornby Gauge Western Express Digital Train Set with eLink and TTS Loco Train Set",
manufacturer: "Hornby",
price: "235.58",
number_available_in_stock: "4 new",
number_of_reviews: "1",
number_of_answered_questions: "1",
average_review_rating: "5.0 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Rail Vehicles > Trains",
customers_who_bought_this_item_also_bought:
"http://www.amazon.co.uk/Hornby-Western-Master-E-Link-Electric/dp/B00BUKPXS8 | http://www.amazon.co.uk/Hornby-Gloucester | http://www.amazon.co.uk/Hornby-Majestic-E-Link-Gauge-Electric/dp/B00BUKPXU6 | http://www.amazon.co.uk/Hornby-Gauge-Master-Glens/dp/B00TQNJIIW | http://www.amazon.co.uk/Hornby-Gauge-Eurostar-2014-Train/dp/B00TQNJIIC | http://www.amazon.co.uk/HORNBY-Digital-Train-Layout-Track/dp/B006BRH55Y",
},
{
rowIndex: "9",
uniq_id: "5afbaf65680c9f378af5b3a3ae22427e",
japanese_name:
"ラーニング カーブ チャギントン インタラクティブ チャッツワース",
currencies: "₹, $, €, ¥, £",
specialChars: "!@#$%^&*",
product_name: "Learning Curve Chuggington Interactive Chatsworth",
manufacturer: "Chuggington",
price: "null",
number_available_in_stock: "1 new",
number_of_reviews: "8",
number_of_answered_questions: "1",
average_review_rating: "4.8 out of 5 stars",
amazon_category_and_sub_category:
"Hobbies > Model Trains & Railway Sets > Rail Vehicles > Trains",
customers_who_bought_this_item_also_bought:
"http://www.amazon.co.uk/Learning-Curve-Chuggington | http://www.amazon.co.uk/Chuggington | http://www.amazon.co.uk/Learning-Curve-Chuggington | http://www.amazon.co.uk/Learning-Chuggington",
},
];

View File

@ -29,5 +29,7 @@ export default {
upgrade: "//button//span[text()='Upgrade']",
accessControl: ".t--settings-category-access-control",
auditLogs: ".t--settings-category-audit-logs",
provisioning: ".t--settings-category-provisioning",
upgrageLeftPane: "[data-testid='t--enterprise-settings-category-item-be']",
businessTag: "[data-testid='t--business-tag']",
};

View File

@ -19,5 +19,6 @@
"textWidgetName": ".t--widget-textwidget",
"welcomeTourBtn": ".t--start-building",
"editorWelcomeTourBtn": "[data-testid='editor-welcome-tour']",
"checklistCompletionBanner": "[data-testid='checklist-completion-banner']"
"checklistCompletionBanner": "[data-testid='checklist-completion-banner']",
"selectWidgetBtn": ".t--select-in-canvas"
}

View File

@ -19,7 +19,8 @@
"chartInnerText": ".t--property-control-title",
"inputChartValue": ".t--property-control-chartseries .CodeMirror textarea",
"chartButton": ".t--property-control-chartseries button",
"rectangleChart": ".t--draggable-chartwidget g rect",
"rectangleChart": ".t--draggable-chartwidget g",
"fusionRectangleChart": ".t--draggable-chartwidget g rect",
"xlabel": ".t--property-control-x-axislabel .CodeMirror-code",
"ylabel": ".t--property-control-y-axislabel .CodeMirror-code",
"mapInner": ".t--draggable-mapwidget span.t--widget-name",
@ -30,8 +31,8 @@
"zoomLevel": ".t--property-control-zoomlevel svg",
"sourceImage": ".t--property-control-image .CodeMirror-code",
"defaultImage": ".t--property-control-defaultimage .CodeMirror textarea",
"Chartlabel": ".t--draggable-chartwidget g:nth-child(5) text",
"PieChartLabel": ".t--draggable-chartwidget g g:nth-child(1) g text",
"Chartlabel": ".t--draggable-chartwidget g text",
"FusionChartlabel": ".t--draggable-chartwidget g:nth-child(5) text",
"pickMyLocation": ".t--draggable-mapwidget div[title='Pick My Location']",
"mapChartEntityLabels": ".t--draggable-mapchartwidget g[class*='-entityLabels'] text",
"listWidget": ".t--draggable-listwidget"

View File

@ -156,7 +156,7 @@
"toggleBackground": ".t--property-control-background .t--js-toggle",
"toggleItemBackground": ".t--property-control-itembackground .t--js-toggle",
"toggleShowStepArrows": ".t--property-control-showsteparrows .t--js-toggle",
"chartPlotGroup": "g.raphael-group-63-plot-group",
"chartDataPoint": "g path[d^='M100 205.51']",
"toggleEnableMultirowselection": ".t--property-control-enablemulti-rowselection input",
"toggleEnableMultirowselection_tablev1": ".t--property-control-enablemultirowselection input",
"formWidget": ".t--draggable-formwidget",

View File

@ -21,16 +21,15 @@
"tabWidget": ".t--widget-tabswidget",
"jsonFormWidget": ".t--widget-jsonformwidget",
"chartWidget": ".t--widget-chartwidget",
"horizontalTab": ".t--widget-chartwidget g[class*='-scrollContainer'] rect",
"tableWidget": ".t--widget-tablewidget",
"chartCanvasVal": ".t--widget-chartwidget g[class*='-canvas'] rect ",
"chartCanvasVal": ".t--widget-chartwidget svg rect",
"mapWidget": ".t--widget-mapwidget",
"tableLength": ".t--widget-tablewidget .tbody",
"tableV2Length": ".t--widget-tablewidgetv2 .tbody",
"mapSearch": ".t--widget-mapwidget input",
"pickMyLocation": ".t--widget-mapwidget div[title='Pick My Location']",
"rectChart": ".t--widget-chartwidget g rect",
"chartLab": ".t--widget-chartwidget g:nth-child(5) text",
"rectChart": ".t--widget-chartwidget svg rect",
"chartLab": ".t--widget-chartwidget svg g text",
"searchInput": ".t--search-input",
"downloadBtn": ".t--table-download-btn",
"filterBtn": ".t--table-filter-toggle-btn",

View File

@ -26,7 +26,7 @@
"@typescript-eslint/parser": "^5.25.0",
"chalk": "^4.1.1",
"cy-verify-downloads": "^0.0.5",
"cypress": "^12.16.0",
"cypress": "^12.17.0",
"cypress-file-upload": "^4.1.1",
"cypress-image-snapshot": "^4.0.1",
"cypress-log-to-output": "^1.1.2",

View File

@ -9,6 +9,7 @@ const apiwidget = require("../locators/apiWidgetslocator.json");
const explorer = require("../locators/explorerlocators.json");
import { ObjectsRegistry } from "./Objects/Registry";
let agHelper = ObjectsRegistry.AggregateHelper;
let dataSources = ObjectsRegistry.DataSources;
let apiPage = ObjectsRegistry.ApiPage;
@ -109,6 +110,7 @@ Cypress.Commands.add(
(apiName, baseurl, path, verb, error = false) => {
cy.get(".ads-v2-tabs__list").contains("Logs").click();
cy.get("[data-testid=t--debugger-search]").clear().type(apiName);
agHelper.PressEnter();
if (!error) {
cy.get(".object-key").last().contains("request").click();

View File

@ -3,7 +3,8 @@ export class CommonLocators {
_inputField = "input";
_canvasViewport = "#canvas-viewport";
_emptyPageTxt = ".bp3-heading";
_chevronUp = ".bp3-icon-chevron-up";
_chevronUp = "span[contains(@class, 'bp3-icon-chevron-up')]";
_chevronDown = "span[contains(@class, 'bp3-icon-chevron-down')]";
_loading = "#loading";
_animationSpnner = ".bp3-spinner-animation";
_btnSpinner = ".ads-v2-spinner";
@ -263,6 +264,7 @@ export class CommonLocators {
_adsV2CollapsibleHeader = ".ads-v2-collapsible__header";
_adsV2Text = ".ads-v2-text";
_svg = "svg";
_imgWidgetInsideList = `//div[@data-testid='styledImage']//img`;
public ds_editor_env_filter = (envName: string) =>
`[data-testid="t--ds-data-filter-${envName}"]`;

View File

@ -32,3 +32,4 @@ export const fakerHelper = ObjectsRegistry.FakerHelper;
export const tedTestConfig = ObjectsRegistry.TEDTestConfigs;
export const entityItems = EntityItems;
export const tabs = ObjectsRegistry.Tabs;
export const gsheetHelper = ObjectsRegistry.GSheetHelper;

View File

@ -27,6 +27,7 @@ import { TEDTestConfigs } from "./TestConfigs";
import { AssertHelper } from "../Pages/AssertHelper";
import { ReusableHelper } from "./ReusableHelper";
import { Tabs } from "../Pages/Tabs";
import { GsheetHelper } from "../Pages/GSheetHelper";
export class ObjectsRegistry {
private static aggregateHelper__: AggregateHelper;
@ -260,6 +261,14 @@ export class ObjectsRegistry {
}
return ObjectsRegistry.tedTestConfigs__;
}
private static gsheetHelper__: GsheetHelper;
static get GSheetHelper(): GsheetHelper {
if (ObjectsRegistry.gsheetHelper__ === undefined) {
ObjectsRegistry.gsheetHelper__ = new GsheetHelper();
}
return ObjectsRegistry.gsheetHelper__;
}
}
export const initLocalstorageRegistry = () => {

View File

@ -77,6 +77,7 @@ export class TEDTestConfigs {
mockHttpCodeUrl: "http://host.docker.internal:5001/v1/mock-http-codes/",
AirtableBaseForME: "appubHrVbovcudwN6",
AirtableTableForME: "tblsFCQSskVFf7xNd",
ApiUrlME: "http://host.docker.internal:5001/v1/production",
firestore_database_url: "https://appsmith-22e8b.firebaseio.com",
firestore_projectID: "appsmith-22e8b",
@ -98,7 +99,7 @@ export class TEDTestConfigs {
mongo_authenticationAuthtype: "SCRAM-SHA-1",
mongo_host: "host.docker.internal",
mongo_port: 28017,
mongo_databaseName: "mongo_samples",
mongo_databaseName: "mongo_samples2",
postgres_host: "host.docker.internal",
postgres_port: 5432,
@ -165,6 +166,7 @@ export class TEDTestConfigs {
mockHttpCodeUrl: "http://host.docker.internal:5001/v1/mock-http-codes/",
AirtableBaseForME: "appubHrVbovcudwN6",
AirtableTableForME: "tblsFCQSskVFf7xNd",
ApiUrlME: "http://host.docker.internal:5001/v1/staging",
firestore_database_url: "https://appsmith-22e8b.firebaseio.com",
firestore_projectID: "appsmith-22e8b",

View File

@ -5,7 +5,7 @@ export class AdminSettings {
public locator = ObjectsRegistry.CommonLocators;
public homePage = ObjectsRegistry.HomePage;
private _adminSettingsBtn = '[data-testid="t--admin-settings-menu-option"]';
public _adminSettingsBtn = '[data-testid="t--admin-settings-menu-option"]';
private _settingsList = ".t--settings-category-list";
public _usersTab = ".t--settings-category-users";
public _roles = (user: string) =>

View File

@ -674,11 +674,20 @@ export class AggregateHelper extends ReusableHelper {
.wait(waitTimeInterval);
}
public HoverElement(selector: string, index = 0, waitTimeInterval = 100) {
return (
this.ScrollIntoView(selector, index)
public HoverElement(
selector: string,
index = 0,
realTouch = true,
waitTimeInterval = 100,
) {
let chain = this.ScrollIntoView(selector, index);
if (realTouch) {
chain = chain
.realTouch({ position: "center" })
.realHover({ pointer: "mouse" })
.realHover({ pointer: "mouse" });
}
return (
chain
//.trigger("mousemove", { eventConstructor: "MouseEvent" })
.wait(waitTimeInterval)
);
@ -1055,6 +1064,7 @@ export class AggregateHelper extends ReusableHelper {
input.focus();
this.Sleep(200);
input.setValue(value);
input.execCommand("goLineEnd");
this.Sleep(200);
});
this.Sleep(500); //for value set to settle
@ -1524,4 +1534,10 @@ export class AggregateHelper extends ReusableHelper {
// }
// return items;
// }, { timeout: 5000 });
public AssertTooltip(tooltipText: string) {
cy.get(".rc-tooltip-inner").should(($x) => {
expect($x).contain(tooltipText);
});
}
}

View File

@ -75,6 +75,7 @@ export class ApiPage {
public _autoGeneratedHeaderInfoIcon = (key: string) =>
`.t--auto-generated-${key}-info`;
private _createQuery = ".t--create-query";
public _editorDS = ".t--datasource-editor";
CreateApi(
apiName = "",
@ -115,7 +116,7 @@ export class ApiPage {
) {
this.CreateApi(apiName, apiVerb, aftDSSaved);
this.EnterURL(url);
//this.agHelper.Sleep(2000);// Added because api name edit takes some time to reflect in api sidebar after the call passes.
this.agHelper.Sleep(2000); // Added because api name edit takes some time to reflect in api sidebar after the call passes.
this.AssertRunButtonDisability();
if (queryTimeout != 10000) this.SetAPITimeout(queryTimeout);
}
@ -130,8 +131,8 @@ export class ApiPage {
directInput: true,
inputFieldName: "",
});
this.agHelper.AssertAutoSave();
//this.agHelper.GetNClick(this._resourceUrl);
this.agHelper.Sleep();
if (evaluatedValue) {
this.agHelper.VerifyEvaluatedValue(evaluatedValue);
}

View File

@ -47,6 +47,7 @@ export class DataSources {
}; //Container KeyValuePair
private _dsCreateNewTab = "[data-testid=t--tab-CREATE_NEW]";
private _dsReviewSection = "[data-testid='t--ds-review-section']";
private _addNewDataSource = ".t--entity-add-btn.datasources button";
private _createNewPlgin = (pluginName: string) =>
".t--plugin-name:contains('" + pluginName + "')";
@ -91,6 +92,7 @@ export class DataSources {
ddTitle +
"']/following-sibling::span//button";
_reconnectModal = "[data-testid='reconnect-datasource-modal']";
_reconnect = ".t--reconnect-btn";
_dropdown = (ddTitle: string) =>
"//span[contains(@title, '" +
ddTitle +
@ -180,7 +182,6 @@ export class DataSources {
_gsScopeOptions = ".ads-v2-select__dropdown .rc-select-item-option";
private _queryTimeout =
"//input[@name='actionConfiguration.timeoutInMillisecond']";
_getStructureReq = "/api/v1/datasources/*/structure?ignoreCache=true";
_editDatasourceFromActiveTab = (dsName: string) =>
".t--datasource-name:contains('" + dsName + "')";
private _suggestedWidget = (widgetType: string) =>
@ -193,7 +194,7 @@ export class DataSources {
dbName +
"']/ancestor::div[contains(@class, 't--entity-item')]/following-sibling::div//p[text()='Schema not available']";
// Authenticated API locators
private _authApiDatasource = ".t--createAuthApiDatasource";
public _authApiDatasource = ".t--createAuthApiDatasource";
private _authType = "[data-testid=authType]";
private _oauth2 = ".rc-select-item-option:contains('OAuth 2.0')";
private _accessTokenUrl =
@ -215,8 +216,10 @@ export class DataSources {
public _cancelEditDatasourceButton = ".t--cancel-edit-datasource";
public _urlInputControl = "input[name='url']";
public _mongoCollectionPath = "t--actionConfiguration.formData.collection";
private _getJSONswitchLocator = (fieldLocator: string) =>
`[data-testid='${fieldLocator}.data-JS']`;
_getJSONswitchLocator = (fieldName: string) =>
"//p[contains(text(),'" +
fieldName +
"')]/ancestor::div[@class='form-config-top']//button";
_nestedWhereClauseKey = (index: number) =>
".t--actionConfiguration\\.formData\\.where\\.data\\.children\\[" +
index +
@ -231,12 +234,17 @@ export class DataSources {
_bodyCodeMirror = "//div[contains(@class, 't--actionConfiguration.body')]";
private _reconnectModalDSToolTip = ".t--ds-list .t--ds-list-title";
private _reconnectModalDSToopTipIcon = ".t--ds-list .ads-v2-icon";
_multiSelectDropdown = (ddName: string) =>
"//p[contains(text(),'" +
ddName +
"')]/ancestor::div[@class='form-config-top']/following-sibling::div//div[contains(@class, 'rc-select-multiple')]";
private _datasourceTableSchemaInQueryEditor =
".datasourceStructure-query-editor";
private _datasourceSchemaRefreshBtn = ".datasourceStructure-refresh";
private _datasourceStructureHeader = ".datasourceStructure-header";
private _datasourceColumnSchemaInQueryEditor = ".t--datasource-column";
private _datasourceStructureSearchInput = ".datasourceStructure-search input";
_jsModeSortingControl = ".t--actionConfiguration\\.formData\\.sortBy\\.data";
public _queryEditorCollapsibleIcon = ".collapsible-icon";
public AssertDSEditViewMode(mode: "Edit" | "View") {
@ -436,6 +444,20 @@ export class DataSources {
this.ValidateNSelectDropdown("SSL mode", "Default");
}
public ValidatePostgresDSForm(
environment = this.tedTestConfig.defaultEnviorment,
) {
const databaseName =
this.tedTestConfig.dsValues[environment].postgres_databaseName;
this.agHelper.AssertContains(databaseName, "exist", this._dsReviewSection);
}
public ValidateMongoForm(environment = this.tedTestConfig.defaultEnviorment) {
const databaseName =
this.tedTestConfig.dsValues[environment].mongo_databaseName;
this.agHelper.AssertContains(this._dsReviewSection, databaseName);
}
public FillOracleDSForm(
environment = this.tedTestConfig.defaultEnviorment,
shouldAddTrailingSpaces = false,
@ -902,6 +924,18 @@ export class DataSources {
}
}
public CreateQueryForDS(datasourceName: string, query = "", queryName = "") {
this.NavigateToActiveTab();
cy.get(this._datasourceCard)
.contains(datasourceName)
.scrollIntoView()
.should("be.visible")
.click();
this.agHelper.Sleep(); //for the Datasource page to open
this.agHelper.GetNClick(this._cancelEditDatasourceButton, 0, true, 200);
this.CreateQueryAfterDSSaved(query, queryName);
}
DeleteQuery(queryName: string) {
this.ee.ExpandCollapseEntity("Queries/JS");
this.ee.ActionContextMenuByEntityName({
@ -929,6 +963,17 @@ export class DataSources {
}
}
public ValidateReviewModeConfig(
dsName: "PostgreSQL" | "MongoDB",
environment = this.tedTestConfig.defaultEnviorment,
) {
if (dsName === "PostgreSQL") {
this.ValidatePostgresDSForm(environment);
} else if (dsName === "MongoDB") {
this.ValidateMongoForm(environment);
}
}
public ReconnectSingleDSNAssert(
dbName: string,
dsName: "PostgreSQL" | "MySQL" | "MongoDB",
@ -943,6 +988,18 @@ export class DataSources {
this.assertHelper.AssertNetworkStatus("getWorkspace");
}
public AssertReconnectDS(datasourceName: string) {
cy.get(this._datasourceCard, { withinSubject: null })
.find(this._activeDS)
.contains(datasourceName)
.scrollIntoView()
.should("be.visible")
.closest(this._datasourceCard)
.scrollIntoView()
.within(() => {
this.agHelper.AssertElementVisible(this._reconnect, 0, 20000);
});
}
public ReconnectModalValidation(
dbName: string,
dsName: "PostgreSQL" | "MySQL" | "MongoDB",
@ -1265,7 +1322,6 @@ export class DataSources {
schema: string,
isUpdate = false,
) {
cy.intercept("GET", this._getStructureReq).as("getDSStructure");
if (isUpdate) {
this.UpdateDatasource();
} else {
@ -1275,7 +1331,7 @@ export class DataSources {
entityNameinLeftSidebar: dataSourceName,
action: "Refresh",
});
cy.wait("@getDSStructure").then(() => {
cy.wait("@getDatasourceStructure").then(() => {
cy.get(".bp3-collapse-body").contains(schema);
});
}
@ -1572,21 +1628,19 @@ export class DataSources {
public EnterJSContext({
fieldLabel,
fieldProperty,
fieldValue,
}: {
fieldProperty: string;
fieldValue: string;
fieldLabel: string;
}) {
this.agHelper.Sleep();
this.agHelper
.GetElement(this._getJSONswitchLocator(fieldProperty))
.GetElement(this._getJSONswitchLocator(fieldLabel))
.invoke("attr", "data-selected")
.then(($state: any) => {
if (!$state.includes("true"))
this.agHelper.GetNClick(
this._getJSONswitchLocator(fieldProperty),
this._getJSONswitchLocator(fieldLabel),
0,
true,
);
@ -1658,4 +1712,40 @@ export class DataSources {
});
});
}
public EnterSortByValues(sortBy: string, option: string, index = 0) {
this.agHelper
.GetElement(this._jsModeSortingControl)
.eq(0)
.children()
.eq(index)
.then((ele) => {
cy.wrap(ele)
.children()
.eq(0)
.find("textarea")
.type(sortBy, { force: true });
cy.wrap(ele).children().eq(1).find("input").click();
});
this.agHelper.GetNClickByContains(this._dropdownOption, option);
}
public ClearSortByOption(index = 0) {
this.agHelper.Sleep(500);
this.agHelper
.GetElement(this._jsModeSortingControl)
.eq(0)
.children()
.eq(index)
.find(`button[data-testid='t--sorting-delete-[${index}]']`)
.click({ force: true });
this.agHelper.Sleep(500);
}
public AddNewSortByParameter() {
this.agHelper
.GetElement(this._jsModeSortingControl)
.find("button[data-testid='t--sorting-add-field']")
.click({ force: true });
}
}

View File

@ -15,6 +15,13 @@ export class DeployMode {
`//p[text()='${fieldName}']/ancestor::div[@direction='column']//div[@data-testid='radiogroup-container']//input`;
_jsonFormDatepickerFieldByName = (fieldName: string) =>
`//p[text()='${fieldName}']/ancestor::div[@direction='column']//div[@data-testid='datepicker-container']//input`;
_jsonFormNumberFieldByName = (
fieldName: string,
direction: "up" | "down" = "up",
) =>
`//p[text()='${fieldName}']/ancestor::div[@direction='column']//div[@data-testid='input-container']// ${
direction == "up" ? this.locator._chevronUp : this.locator._chevronDown
}`;
_jsonSelectDropdown = "button.select-button";
private _jsonFormMultiSelectByName = (fieldName: string) =>
`//p[text()='${fieldName}']/ancestor::div[@direction='column']//div[@data-testid='multiselect-container']//div[contains(@class, 'rc-select-show-arrow')]`;

View File

@ -28,7 +28,8 @@ interface EntityActionParams {
| "Copy to page"
| "Move to page"
| "Hide"
| "Refresh";
| "Refresh"
| "Set as home page";
subAction?: string;
entityType?: EntityItems;
toAssertAction?: boolean;
@ -270,9 +271,7 @@ export class EntityExplorer {
}
public ValidateDuplicateMessageToolTip(tooltipText: string) {
cy.get(".rc-tooltip-inner").should(($x) => {
expect($x).contain(tooltipText.concat(" is already being used."));
});
this.agHelper.AssertTooltip(tooltipText.concat(" is already being used."));
}
public DeleteAllQueriesForDB(dsName: string) {
@ -407,16 +406,20 @@ export class EntityExplorer {
}
public PinUnpinEntityExplorer(pin = true) {
this.agHelper
.GetElement(this._entityExplorer)
.invoke("attr", "class")
.then(($classes) => {
if (pin && !$classes?.includes("fixed"))
this.agHelper.GetNClick(this._pinEntityExplorer, 0, false, 1000);
else if (!pin && $classes?.includes("fixed"))
this.agHelper.GetNClick(this._pinEntityExplorer, 0, false, 1000);
else this.agHelper.Sleep(200); //do nothing
});
cy.get("body").then(($ele) => {
if ($ele.find(this._pinEntityExplorer).length) {
this.agHelper
.GetElement(this._entityExplorer)
.invoke("attr", "class")
.then(($classes) => {
if (pin && !$classes?.includes("fixed"))
this.agHelper.GetNClick(this._pinEntityExplorer, 0, false, 1000);
else if (!pin && $classes?.includes("fixed"))
this.agHelper.GetNClick(this._pinEntityExplorer, 0, false, 1000);
else this.agHelper.Sleep(200); //do nothing
});
}
});
}
public RenameEntityFromExplorer(

View File

@ -0,0 +1,138 @@
import { ObjectsRegistry } from "../Objects/Registry";
type operation =
| "Insert One"
| "Insert Many"
| "Update One"
| "Update Many"
| "Fetch Details"
| "Fetch Many"
| "Delete One";
export class GsheetHelper {
public agHelper = ObjectsRegistry.AggregateHelper;
public locator = ObjectsRegistry.CommonLocators;
private dataSources = ObjectsRegistry.DataSources;
private entityExplorer = ObjectsRegistry.EntityExplorer;
public AddNewSpreadsheetQuery(
dataSourceName: string,
spreadsheet: string,
rowData: string,
) {
this.entityExplorer.CreateNewDsQuery(dataSourceName);
this.dataSources.ValidateNSelectDropdown(
"Operation",
"Fetch Many",
"Insert One",
);
this.dataSources.ValidateNSelectDropdown(
"Entity",
"Sheet Row(s)",
"Spreadsheet",
);
this.agHelper.EnterValue(spreadsheet, {
propFieldName: "",
directInput: false,
inputFieldName: "Spreadsheet Name",
});
this.agHelper.RenameWithInPane("insert_spreadsheet");
this.agHelper.EnterValue(rowData, {
propFieldName: "",
directInput: false,
inputFieldName: "Row object(s)",
});
this.dataSources.RunQuery();
}
public DeleteSpreadsheetQuery(dataSourceName: string, spreadsheet: string) {
this.entityExplorer.CreateNewDsQuery(dataSourceName);
this.dataSources.ValidateNSelectDropdown(
"Operation",
"Fetch Many",
"Delete One",
);
this.dataSources.ValidateNSelectDropdown(
"Entity",
"Sheet Row(s)",
"Spreadsheet",
);
this.dataSources.ValidateNSelectDropdown("Spreadsheet", "", spreadsheet);
this.agHelper.RenameWithInPane("delete_spreadsheet");
this.dataSources.RunQuery();
}
public AddInsertOrUpdateQuery(
operation: operation,
dataSourceName: string,
spreadSheet: string,
rowData: string,
executeQuery = true,
sheetName = "Sheet1",
headRowIndex = "1",
) {
this.EnterBasicQueryValues(
operation,
dataSourceName,
spreadSheet,
true,
"Sheet Row(s)",
sheetName,
headRowIndex,
);
let inputField = "";
if (operation.includes("Insert")) {
inputField = operation == "Insert One" ? "Row object" : "Row object(s)";
} else if (operation.includes("Update")) {
inputField =
operation == "Update One"
? "Update row object"
: "Update row object(s)";
}
this.agHelper.EnterValue(rowData, {
propFieldName: "",
directInput: false,
inputFieldName: inputField,
});
if (executeQuery) this.dataSources.RunQuery();
}
public EnterBasicQueryValues(
operation: operation,
dataSourceName: string,
spreadSheet: string,
renameQuery = true,
entity = "Sheet Row(s)",
sheetName = "Sheet1",
headRowIndex = "1",
) {
this.entityExplorer.CreateNewDsQuery(dataSourceName);
this.dataSources.ValidateNSelectDropdown(
"Operation",
"Fetch Many",
operation,
);
this.dataSources.ValidateNSelectDropdown("Entity", "Sheet Row(s)", entity);
this.agHelper.Sleep(500);
this.dataSources.ValidateNSelectDropdown("Spreadsheet", "", spreadSheet);
if (!entity.includes("Spreadsheet")) {
this.dataSources.ValidateNSelectDropdown("Sheet name", "", sheetName);
this.agHelper.EnterValue(headRowIndex, {
propFieldName: "",
directInput: false,
inputFieldName: "Table heading row index",
});
}
if (renameQuery) {
this.agHelper.RenameWithInPane(
operation.toLowerCase().replace(" ", "_") + "_query",
);
}
}
public SelectMultiDropDownValue(ddName: string, option: string) {
this.agHelper.GetNClick(this.dataSources._multiSelectDropdown(ddName));
this.agHelper.GetNClickByContains(this.dataSources._dropdownOption, option);
}
}

View File

@ -124,7 +124,11 @@ export class HomePage {
this.agHelper.GetNClick(this._homeTab);
}
public CreateNewWorkspace(workspaceNewName: string) {
public CreateNewWorkspace(
workspaceNewName: string,
toNavigateToHome = false,
) {
if (toNavigateToHome) this.NavigateToHome();
let oldName = "";
this.agHelper.GetNClick(this._newWorkSpaceLink);
this.assertHelper.AssertNetworkStatus("createWorkspace", 201);
@ -255,6 +259,7 @@ export class HomePage {
//Maps to CreateAppForWorkspace in command.js
public CreateAppInWorkspace(workspaceName: string, appname = "") {
cy.xpath(this._existingWorkspaceCreateNewApp(workspaceName))
.last()
.scrollIntoView()
.should("be.visible")
.click({ force: true });

View File

@ -88,8 +88,10 @@ export class PropertyPane {
_selectorViewLabel = '[data-testId="selector-view-label"]';
_textView = ".text-view";
_selectorView = ".selector-view";
_dropdownOptions =
"//div[@class='rc-virtual-list']//div[contains(@class, 'rc-select-item-option')]";
_dropDownValue = (dropdownOption: string) =>
`//div[@class='rc-virtual-list']//div[contains(@class, 'rc-select-item-option')]//span[text()='${dropdownOption}']`;
`${this._dropdownOptions}//span[text()='${dropdownOption}']`;
_selectPropDropdown = (ddName: string) =>
"//div[contains(@class, 't--property-control-" +
ddName.replace(/ +/g, "").toLowerCase() +
@ -259,6 +261,33 @@ export class PropertyPane {
});
}
public AssertPropertiesDropDownValues(
endpoint: string,
dropdownExpectedValues: string[],
) {
this.agHelper.GetNClick(this._selectPropDropdown(endpoint));
this.agHelper
.GetElement(this._dropdownOptions)
.then(($ddVisibleTexts: any) => {
let foundAll = true; // Flag to track if all expected selections are found
const foundSelections: string[] = []; // Array to track found selections
$ddVisibleTexts.each((index: any, element: any) => {
const spanText = Cypress.$(element).text().trim();
foundSelections.push(spanText);
});
// Check if all expected selections are found in the dropdown
dropdownExpectedValues.forEach((expectedSelection) => {
if (!foundSelections.includes(expectedSelection)) {
foundAll = false;
cy.log("Expected dropdown text not found: " + expectedSelection);
}
});
expect(foundAll).to.be.true;
cy.log("All dropdown values are present");
});
this.agHelper.GetNClick(this._selectPropDropdown(endpoint)); //Closing dropdown
}
public SelectJSFunctionToExecuteInExistingActionBlock(
jsName: string,
funcName: string,
@ -383,9 +412,14 @@ export class PropertyPane {
toVerifySave && this.agHelper.AssertAutoSave();
}
public TypeTextIntoField(endp: string, value: string, removeText = true) {
public TypeTextIntoField(
endp: string,
value: string,
removeText = true,
toVerifySave = true,
) {
if (removeText) {
this.RemoveText(endp);
this.RemoveText(endp, toVerifySave);
}
this.agHelper
.GetElement(this.locator._propertyInputField(endp))

View File

@ -160,6 +160,7 @@ export class Table {
_columnCheckbox = (columnName: string) =>
"[data-rbd-draggable-id='" + columnName + "']" + " .t--card-checkbox input";
_dateInputPopover = ".bp3-dateinput-popover";
_tableV2Widget = ".t--draggable-tablewidgetv2";
_tableV2Row = ".t--draggable-tablewidgetv2 .tbody";
_weekdayRowDayPicker =
".bp3-datepicker .DayPicker .DayPicker-Months .DayPicker-WeekdaysRow";

View File

@ -864,7 +864,8 @@ Cypress.Commands.add(
(forSuccess, forFailure, actionType, actionValue, idx = 0) => {
propPane.SelectActionByTitleAndValue(actionType, actionValue);
cy.get(propPane._actionCallbacks).last().click();
agHelper.Sleep();
agHelper.GetNClick(propPane._actionCallbacks, 0, true);
// add a success callback
cy.get(propPane._actionAddCallback("success")).click().wait(500);

View File

@ -30,6 +30,9 @@ export default defineConfig({
},
specPattern: "cypress/e2e/**/*.{js,ts}",
testIsolation: false,
excludeSpecPattern: "cypress/e2e/**/spec_utility.ts",
excludeSpecPattern: [
"cypress/e2e/**/spec_utility.ts",
"cypress/e2e/GSheet/**/**/*",
],
},
});

View File

@ -0,0 +1,40 @@
import { defineConfig } from "cypress";
export default defineConfig({
defaultCommandTimeout: 30000,
requestTimeout: 60000,
responseTimeout: 60000,
pageLoadTimeout: 60000,
videoUploadOnPasses: false,
videoCompression: false,
numTestsKeptInMemory: 5,
experimentalMemoryManagement: true,
reporterOptions: {
reportDir: "results",
overwrite: false,
html: true,
json: false,
},
chromeWebSecurity: false,
viewportHeight: 1100,
viewportWidth: 1400,
scrollBehavior: "center",
retries: {
runMode: 1,
openMode: 0,
},
e2e: {
baseUrl: "https://regression.test.appsmith.com",
setupNodeEvents(on, config) {
return require("./cypress/plugins/index.js")(on, config);
},
specPattern: "cypress/e2e/**/*.{js,ts}",
testIsolation: false,
excludeSpecPattern: [
"cypress/e2e/**/spec_utility.ts",
"cypress/e2e/Regression/**/**/*",
"cypress/e2e/Sanity/**/**/*",
"cypress/e2e/Smoke/**/**/*",
],
},
});

View File

@ -31,6 +31,9 @@ module.exports = {
"design-system-old": "<rootDir>/node_modules/design-system-old/build",
"@design-system/widgets-old":
"<rootDir>/node_modules/@design-system/widgets-old",
"@design-system/widgets": "<rootDir>/node_modules/@design-system/widgets",
"@design-system/headless": "<rootDir>/node_modules/@design-system/headless",
"@design-system/theming": "<rootDir>/node_modules/@design-system/theming",
"design-system": "<rootDir>/node_modules/design-system/build",
"^proxy-memoize$": "<rootDir>/node_modules/proxy-memoize/dist/wrapper.cjs",
// @blueprintjs packages need to be resolved to the `esnext` directory. The default `esm` directory

View File

@ -96,6 +96,7 @@
"design-system": "npm:@appsmithorg/design-system@2.1.17",
"design-system-old": "npm:@appsmithorg/design-system-old@1.1.11",
"downloadjs": "^1.4.7",
"echarts": "^5.4.2",
"fast-deep-equal": "^3.1.3",
"fast-xml-parser": "^3.17.5",
"fastdom": "^1.0.11",
@ -278,7 +279,7 @@
"compression-webpack-plugin": "^10.0.0",
"cra-bundle-analyzer": "^0.1.0",
"cy-verify-downloads": "^0.0.5",
"cypress": "^12.16.0",
"cypress": "^12.17.0",
"cypress-file-upload": "^4.1.1",
"cypress-image-snapshot": "^4.0.1",
"cypress-mochawesome-reporter": "^3.5.1",
@ -353,6 +354,7 @@
"@blueprintjs/core@^3.43.0": "patch:@blueprintjs/core@npm%3A3.47.0#./.yarn/patches/@blueprintjs-core-npm-3.47.0-a5bc1ea927.patch",
"@blueprintjs/core@^3.33.0": "patch:@blueprintjs/core@npm%3A3.47.0#./.yarn/patches/@blueprintjs-core-npm-3.47.0-a5bc1ea927.patch",
"@blueprintjs/core@^3.47.0": "patch:@blueprintjs/core@npm%3A3.47.0#./.yarn/patches/@blueprintjs-core-npm-3.47.0-a5bc1ea927.patch",
"@blueprintjs/icons": "3.22.0"
"@blueprintjs/icons": "3.22.0",
"@types/react": "^17.0.2"
}
}

Some files were not shown because too many files have changed in this diff Show More