mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 09:28:31 +00:00
Merge branch 'lts-3.16' of https://github.com/ToolJet/ToolJet into fix/input-component-issues
This commit is contained in:
commit
5c16fa22d7
225 changed files with 9470 additions and 4117 deletions
51
.github/workflows/cloud-frontend-gcp.yml
vendored
51
.github/workflows/cloud-frontend-gcp.yml
vendored
|
|
@ -119,7 +119,7 @@ jobs:
|
|||
TJDB_SQL_MODE_DISABLE: ${{ secrets.CLOUD_TJDB_SQL_MODE_DISABLE }}
|
||||
TOOLJET_SERVER_URL: ${{ secrets.CLOUD_TOOLJET_SERVER_URL }}
|
||||
TOOLJET_EDITION: cloud
|
||||
WEBSITE_SIGNUP_URL: https://website-stage.tooljet.ai/ai-create-account
|
||||
WEBSITE_SIGNUP_URL: https://website-stage.tooljet.ai/signup
|
||||
|
||||
- name: 🚀 Deploy to Netlify
|
||||
run: |
|
||||
|
|
@ -138,5 +138,52 @@ jobs:
|
|||
SERVER_IP: ${{ secrets.CLOUD_SERVER_IP }}
|
||||
TJDB_SQL_MODE_DISABLE: ${{ secrets.CLOUD_TJDB_SQL_MODE_DISABLE }}
|
||||
TOOLJET_SERVER_URL: ${{ secrets.CLOUD_TOOLJET_SERVER_URL }}
|
||||
WEBSITE_SIGNUP_URL: https://website-stage.tooljet.ai/ai-create-account
|
||||
WEBSITE_SIGNUP_URL: https://website-stage.tooljet.ai/signup
|
||||
TOOLJET_EDITION: cloud
|
||||
|
||||
Purge_Cloudflare_Cache:
|
||||
needs: deploy
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: ✅ Check user authorization
|
||||
run: |
|
||||
allowed_user1=${{ secrets.ALLOWED_USER1_USERNAME }}
|
||||
allowed_user2=${{ secrets.ALLOWED_USER2_USERNAME }}
|
||||
allowed_user3=${{ secrets.ALLOWED_USER3_USERNAME }}
|
||||
|
||||
if [[ "${{ github.actor }}" != "$allowed_user1" && \
|
||||
"${{ github.actor }}" != "$allowed_user2" && \
|
||||
"${{ github.actor }}" != "$allowed_user3" ]]; then
|
||||
echo "❌ User '${{ github.actor }}' is not authorized to trigger this workflow."
|
||||
exit 1
|
||||
else
|
||||
echo "✅ User '${{ github.actor }}' is authorized."
|
||||
fi
|
||||
|
||||
- name: 🧹 Purge Cloudflare Cache
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "🔄 Purging Cloudflare cache for specific URLs..."
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
"https://api.cloudflare.com/client/v4/zones/${{ secrets.CLOUDFLARE_ZONE_ID_PROD }}/purge_cache" \
|
||||
-H "Authorization: Bearer ${{ secrets.CLOUDFLARE_API_TOKEN_PROD }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{
|
||||
"files": [
|
||||
"${{ secrets.CLOUDFLARE_CONFIG_URL_STAGE }}",
|
||||
"${{ secrets.CLOUDFLARE_METADATA_URL_STAGE }}"
|
||||
]
|
||||
}')
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
echo "✅ Cloudflare cache purged successfully for specified URLs"
|
||||
echo "$body"
|
||||
else
|
||||
echo "⚠️ Cloudflare cache purge failed with status code: $http_code"
|
||||
echo "$body"
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
51
.github/workflows/cloud-frontend.yml
vendored
51
.github/workflows/cloud-frontend.yml
vendored
|
|
@ -123,7 +123,7 @@ jobs:
|
|||
|
||||
- name: 🚀 Deploy to Netlify
|
||||
run: |
|
||||
npm install -g netlify-cli@17.10.1
|
||||
npm install -g netlify-cli
|
||||
netlify deploy --prod --dir=frontend/build --auth=$NETLIFY_AUTH_TOKEN --site=${{ secrets.CLOUD_PROD_NETLIFY_SITE_ID }}
|
||||
working-directory: repo
|
||||
env:
|
||||
|
|
@ -138,5 +138,52 @@ jobs:
|
|||
SERVER_IP: ${{ secrets.CLOUD_PROD_CLOUD_SERVER_IP }}
|
||||
TJDB_SQL_MODE_DISABLE: ${{ secrets.CLOUD_PROD_TJDB_SQL_MODE_DISABLE }}
|
||||
TOOLJET_SERVER_URL: ${{ secrets.CLOUD_PROD_TOOLJET_SERVER_URL }}
|
||||
WEBSITE_SIGNUP_URL: https://tooljet.ai/ai-create-account
|
||||
WEBSITE_SIGNUP_URL: https://www.tooljet.ai/create-account
|
||||
TOOLJET_EDITION: cloud
|
||||
|
||||
Purge_Cloudflare_Cache:
|
||||
needs: deploy
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: ✅ Check user authorization
|
||||
run: |
|
||||
allowed_user1=${{ secrets.ALLOWED_USER1_USERNAME }}
|
||||
allowed_user2=${{ secrets.ALLOWED_USER2_USERNAME }}
|
||||
allowed_user3=${{ secrets.ALLOWED_USER3_USERNAME }}
|
||||
|
||||
if [[ "${{ github.actor }}" != "$allowed_user1" && \
|
||||
"${{ github.actor }}" != "$allowed_user2" && \
|
||||
"${{ github.actor }}" != "$allowed_user3" ]]; then
|
||||
echo "❌ User '${{ github.actor }}' is not authorized to trigger this workflow."
|
||||
exit 1
|
||||
else
|
||||
echo "✅ User '${{ github.actor }}' is authorized."
|
||||
fi
|
||||
|
||||
- name: 🧹 Purge Cloudflare Cache
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "🔄 Purging Cloudflare cache for specific URLs..."
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
"https://api.cloudflare.com/client/v4/zones/${{ secrets.CLOUDFLARE_ZONE_ID_PROD }}/purge_cache" \
|
||||
-H "Authorization: Bearer ${{ secrets.CLOUDFLARE_API_TOKEN_PROD }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{
|
||||
"files": [
|
||||
"${{ secrets.CLOUDFLARE_CONFIG_URL_PROD }}",
|
||||
"${{ secrets.CLOUDFLARE_METADATA_URL_PROD }}"
|
||||
]
|
||||
}')
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
echo "✅ Cloudflare cache purged successfully for specified URLs"
|
||||
echo "$body"
|
||||
else
|
||||
echo "⚠️ Cloudflare cache purge failed with status code: $http_code"
|
||||
echo "$body"
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
393
.github/workflows/cypress-platform.yml
vendored
393
.github/workflows/cypress-platform.yml
vendored
|
|
@ -37,6 +37,29 @@ jobs:
|
|||
echo "Labels: ${{ toJSON(github.event.pull_request.labels.*.name) }}"
|
||||
echo "Matrix edition: ${{ matrix.edition }}"
|
||||
|
||||
- name: Free up disk space
|
||||
run: |
|
||||
echo "Available disk space before cleanup:"
|
||||
df -h
|
||||
|
||||
# Remove unnecessary packages
|
||||
sudo apt-get remove -y '^aspnetcore-.*' '^dotnet-.*' '^llvm-.*' '^php.*' '^mongodb-.*' '^mysql-.*' azure-cli google-cloud-sdk hhvm google-chrome-stable firefox powershell mono-devel || true
|
||||
sudo apt-get autoremove -y
|
||||
sudo apt-get clean
|
||||
|
||||
# Remove large directories
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /usr/local/lib/android
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf /usr/local/share/boost
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
|
||||
# Clean Docker
|
||||
docker system prune -af --volumes
|
||||
|
||||
echo "Available disk space after cleanup:"
|
||||
df -h
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
|
|
@ -56,32 +79,47 @@ jobs:
|
|||
|
||||
- name: Build CE Docker image
|
||||
if: matrix.edition == 'ce'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: docker/ce-production.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce
|
||||
platforms: linux/amd64
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "Building CE Docker image..."
|
||||
docker buildx build \
|
||||
--platform=linux/amd64 \
|
||||
-f docker/ce-production.Dockerfile \
|
||||
-t tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce \
|
||||
--no-cache \
|
||||
--load \
|
||||
.
|
||||
|
||||
echo "Pushing CE Docker image..."
|
||||
docker push tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce
|
||||
|
||||
echo "Cleaning up build cache..."
|
||||
docker builder prune -af
|
||||
|
||||
echo "Disk space after build:"
|
||||
df -h
|
||||
|
||||
- name: Build EE Docker image
|
||||
if: matrix.edition == 'ee'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
build-args: |
|
||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
BRANCH_NAME=${{ github.event.pull_request.head.ref }}
|
||||
file: cypress-tests/cypress-lts.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee
|
||||
platforms: linux/amd64
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "Building EE Docker image..."
|
||||
docker buildx build \
|
||||
--platform=linux/amd64 \
|
||||
-f cypress-tests/cypress-lts.Dockerfile \
|
||||
--build-arg CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }} \
|
||||
--build-arg BRANCH_NAME=${{ github.event.pull_request.head.ref }} \
|
||||
-t tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee \
|
||||
--no-cache \
|
||||
--load \
|
||||
.
|
||||
|
||||
echo "Pushing EE Docker image..."
|
||||
docker push tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee
|
||||
|
||||
echo "Cleaning up build cache..."
|
||||
docker builder prune -af
|
||||
|
||||
echo "Disk space after build:"
|
||||
df -h
|
||||
|
||||
- name: Set up environment variables
|
||||
run: |
|
||||
|
|
@ -119,7 +157,10 @@ jobs:
|
|||
echo "LICENSE_KEY=${{ secrets.RENDER_LICENSE_KEY }}" >> .env
|
||||
|
||||
- name: clean up old docker containers
|
||||
run: docker system prune -af
|
||||
run: |
|
||||
docker system prune -af --volumes
|
||||
echo "Disk space after Docker cleanup:"
|
||||
df -h
|
||||
|
||||
- name: Pulling the docker-compose file
|
||||
run: curl -LO https://tooljet-test.s3.us-west-1.amazonaws.com/docker-compose.yaml && mkdir postgres_data
|
||||
|
|
@ -182,6 +223,112 @@ jobs:
|
|||
|
||||
echo "✅ Server is ready!"
|
||||
|
||||
- name: Test database connection
|
||||
run: |
|
||||
# Wait for database to be ready
|
||||
echo "Testing database connection..."
|
||||
docker exec Tooljet-postgres psql -U postgres -d tooljet_development -c "SELECT current_database();"
|
||||
|
||||
- name: Create delete_user procedure
|
||||
run: |
|
||||
echo "Creating delete_user stored procedure..."
|
||||
docker exec Tooljet-postgres psql -U postgres -d tooljet_development -c "
|
||||
CREATE OR REPLACE PROCEDURE delete_user(p_email TEXT)
|
||||
LANGUAGE plpgsql
|
||||
AS \$\$
|
||||
DECLARE
|
||||
v_user_id UUID;
|
||||
v_organization_ids UUID[];
|
||||
v_organizations_to_delete UUID[];
|
||||
v_log_message TEXT;
|
||||
BEGIN
|
||||
-- Log start of procedure
|
||||
RAISE NOTICE 'Starting delete_user procedure for email: %', p_email;
|
||||
|
||||
-- Get user_id from email
|
||||
SELECT id INTO v_user_id
|
||||
FROM users
|
||||
WHERE email = p_email;
|
||||
|
||||
IF v_user_id IS NULL THEN
|
||||
RAISE EXCEPTION 'User with email % not found', p_email;
|
||||
END IF;
|
||||
|
||||
RAISE NOTICE 'User found with id: %', v_user_id;
|
||||
|
||||
-- Get organization_ids for the user
|
||||
SELECT ARRAY_AGG(organization_id) INTO v_organization_ids
|
||||
FROM organization_users
|
||||
WHERE user_id = v_user_id;
|
||||
|
||||
RAISE NOTICE 'Found % organizations for user', COALESCE(ARRAY_LENGTH(v_organization_ids, 1), 0);
|
||||
|
||||
-- Get organizations with exactly one user (to be deleted)
|
||||
SELECT ARRAY_AGG(organization_id) INTO v_organizations_to_delete
|
||||
FROM (
|
||||
SELECT organization_id
|
||||
FROM organization_users
|
||||
WHERE organization_id = ANY(v_organization_ids)
|
||||
GROUP BY organization_id
|
||||
HAVING COUNT(*) = 1
|
||||
) subquery;
|
||||
|
||||
-- Add organizations where the user is the owner
|
||||
v_organizations_to_delete := v_organizations_to_delete;
|
||||
|
||||
RAISE NOTICE 'Found % organizations to delete', COALESCE(ARRAY_LENGTH(v_organizations_to_delete, 1), 0);
|
||||
|
||||
-- Delete apps in organizations to be deleted
|
||||
WITH deleted_apps AS (
|
||||
DELETE FROM apps
|
||||
WHERE organization_id = ANY(v_organizations_to_delete)
|
||||
RETURNING id
|
||||
)
|
||||
SELECT 'Deleted ' || COUNT(*) || ' apps' INTO v_log_message FROM deleted_apps;
|
||||
|
||||
RAISE NOTICE '%', v_log_message;
|
||||
|
||||
-- Delete data_sources in organizations to be deleted
|
||||
WITH deleted_data_sources AS (
|
||||
DELETE FROM data_sources
|
||||
WHERE organization_id = ANY(v_organizations_to_delete)
|
||||
RETURNING id
|
||||
)
|
||||
SELECT 'Deleted ' || COUNT(*) || ' data sources' INTO v_log_message FROM deleted_data_sources;
|
||||
|
||||
RAISE NOTICE '%', v_log_message;
|
||||
|
||||
-- Delete audit_logs for organizations to be deleted and for the user
|
||||
WITH deleted_audit_logs AS (
|
||||
DELETE FROM audit_logs
|
||||
WHERE organization_id = ANY(v_organization_ids)
|
||||
OR user_id = v_user_id
|
||||
RETURNING id
|
||||
)
|
||||
SELECT 'Deleted ' || COUNT(*) || ' audit logs' INTO v_log_message FROM deleted_audit_logs;
|
||||
|
||||
RAISE NOTICE '%', v_log_message;
|
||||
|
||||
-- Delete organizations
|
||||
WITH deleted_organizations AS (
|
||||
DELETE FROM organizations
|
||||
WHERE id = ANY(v_organizations_to_delete)
|
||||
RETURNING id
|
||||
)
|
||||
SELECT 'Deleted ' || COUNT(*) || ' organizations' INTO v_log_message FROM deleted_organizations;
|
||||
|
||||
RAISE NOTICE '%', v_log_message;
|
||||
|
||||
-- Finally, delete the user
|
||||
DELETE FROM users
|
||||
WHERE id = v_user_id;
|
||||
|
||||
RAISE NOTICE 'Deleted user with id: %', v_user_id;
|
||||
RAISE NOTICE 'User deletion procedure completed successfully for email: %', p_email;
|
||||
END;
|
||||
\$\$;"
|
||||
echo "✅ delete_user procedure created successfully"
|
||||
|
||||
- name: Create Cypress environment file
|
||||
id: create-json-tj
|
||||
uses: jsdaniell/create-json@1.1.2
|
||||
|
|
@ -250,32 +397,47 @@ jobs:
|
|||
|
||||
- name: Build CE Docker image
|
||||
if: matrix.edition == 'ce'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: docker/ce-production.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce
|
||||
platforms: linux/amd64
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "Building CE Docker image..."
|
||||
docker buildx build \
|
||||
--platform=linux/amd64 \
|
||||
-f docker/ce-production.Dockerfile \
|
||||
-t tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce \
|
||||
--no-cache \
|
||||
--load \
|
||||
.
|
||||
|
||||
echo "Pushing CE Docker image..."
|
||||
docker push tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce
|
||||
|
||||
echo "Cleaning up build cache..."
|
||||
docker builder prune -af
|
||||
|
||||
echo "Disk space after build:"
|
||||
df -h
|
||||
|
||||
- name: Build EE Docker image
|
||||
if: matrix.edition == 'ee'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
build-args: |
|
||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
BRANCH_NAME=${{ github.event.pull_request.head.ref }}
|
||||
file: cypress-tests/cypress-lts.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee
|
||||
platforms: linux/amd64
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "Building EE Docker image..."
|
||||
docker buildx build \
|
||||
--platform=linux/amd64 \
|
||||
-f cypress-tests/cypress-lts.Dockerfile \
|
||||
--build-arg CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }} \
|
||||
--build-arg BRANCH_NAME=${{ github.event.pull_request.head.ref }} \
|
||||
-t tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee \
|
||||
--no-cache \
|
||||
--load \
|
||||
.
|
||||
|
||||
echo "Pushing EE Docker image..."
|
||||
docker push tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee
|
||||
|
||||
echo "Cleaning up build cache..."
|
||||
docker builder prune -af
|
||||
|
||||
echo "Disk space after build:"
|
||||
df -h
|
||||
|
||||
- name: Set up environment variables
|
||||
run: |
|
||||
|
|
@ -315,7 +477,10 @@ jobs:
|
|||
echo "LICENSE_KEY=${{ secrets.RENDER_LICENSE_KEY }}" >> .env
|
||||
|
||||
- name: clean up old docker containers
|
||||
run: docker system prune -af
|
||||
run: |
|
||||
docker system prune -af --volumes
|
||||
echo "Disk space after Docker cleanup:"
|
||||
df -h
|
||||
|
||||
- name: Pulling the docker-compose file
|
||||
run: curl -LO https://tooljet-test.s3.us-west-1.amazonaws.com/docker-compose.yaml && mkdir postgres_data
|
||||
|
|
@ -436,32 +601,47 @@ jobs:
|
|||
|
||||
- name: Build CE Docker image
|
||||
if: matrix.edition == 'ce'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: docker/ce-production.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce
|
||||
platforms: linux/amd64
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "Building CE Docker image..."
|
||||
docker buildx build \
|
||||
--platform=linux/amd64 \
|
||||
-f docker/ce-production.Dockerfile \
|
||||
-t tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce \
|
||||
--no-cache \
|
||||
--load \
|
||||
.
|
||||
|
||||
echo "Pushing CE Docker image..."
|
||||
docker push tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce
|
||||
|
||||
echo "Cleaning up build cache..."
|
||||
docker builder prune -af
|
||||
|
||||
echo "Disk space after build:"
|
||||
df -h
|
||||
|
||||
- name: Build EE Docker image
|
||||
if: matrix.edition == 'ee'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
build-args: |
|
||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
BRANCH_NAME=${{ github.event.pull_request.head.ref }}
|
||||
file: cypress-tests/cypress-lts.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee
|
||||
platforms: linux/amd64
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "Building EE Docker image..."
|
||||
docker buildx build \
|
||||
--platform=linux/amd64 \
|
||||
-f cypress-tests/cypress-lts.Dockerfile \
|
||||
--build-arg CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }} \
|
||||
--build-arg BRANCH_NAME=${{ github.event.pull_request.head.ref }} \
|
||||
-t tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee \
|
||||
--no-cache \
|
||||
--load \
|
||||
.
|
||||
|
||||
echo "Pushing EE Docker image..."
|
||||
docker push tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee
|
||||
|
||||
echo "Cleaning up build cache..."
|
||||
docker builder prune -af
|
||||
|
||||
echo "Disk space after build:"
|
||||
df -h
|
||||
|
||||
- name: Set up environment variables
|
||||
run: |
|
||||
|
|
@ -501,7 +681,10 @@ jobs:
|
|||
echo "LICENSE_KEY=${{ secrets.RENDER_LICENSE_KEY }}" >> .env
|
||||
|
||||
- name: clean up old docker containers
|
||||
run: docker system prune -af
|
||||
run: |
|
||||
docker system prune -af --volumes
|
||||
echo "Disk space after Docker cleanup:"
|
||||
df -h
|
||||
|
||||
- name: Pulling the docker-compose file
|
||||
run: curl -LO https://tooljet-test.s3.us-west-1.amazonaws.com/docker-compose.yaml && mkdir postgres_data
|
||||
|
|
@ -635,32 +818,47 @@ jobs:
|
|||
|
||||
- name: Build CE Docker image
|
||||
if: matrix.edition == 'ce'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: docker/ce-production.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce
|
||||
platforms: linux/amd64
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "Building CE Docker image..."
|
||||
docker buildx build \
|
||||
--platform=linux/amd64 \
|
||||
-f docker/ce-production.Dockerfile \
|
||||
-t tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce \
|
||||
--no-cache \
|
||||
--load \
|
||||
.
|
||||
|
||||
echo "Pushing CE Docker image..."
|
||||
docker push tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ce
|
||||
|
||||
echo "Cleaning up build cache..."
|
||||
docker builder prune -af
|
||||
|
||||
echo "Disk space after build:"
|
||||
df -h
|
||||
|
||||
- name: Build EE Docker image
|
||||
if: matrix.edition == 'ee'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
build-args: |
|
||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
BRANCH_NAME=${{ github.event.pull_request.head.ref }}
|
||||
file: cypress-tests/cypress-lts.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee
|
||||
platforms: linux/amd64
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "Building EE Docker image..."
|
||||
docker buildx build \
|
||||
--platform=linux/amd64 \
|
||||
-f cypress-tests/cypress-lts.Dockerfile \
|
||||
--build-arg CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }} \
|
||||
--build-arg BRANCH_NAME=${{ github.event.pull_request.head.ref }} \
|
||||
-t tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee \
|
||||
--no-cache \
|
||||
--load \
|
||||
.
|
||||
|
||||
echo "Pushing EE Docker image..."
|
||||
docker push tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}-ee
|
||||
|
||||
echo "Cleaning up build cache..."
|
||||
docker builder prune -af
|
||||
|
||||
echo "Disk space after build:"
|
||||
df -h
|
||||
|
||||
- name: Set up environment variables
|
||||
run: |
|
||||
|
|
@ -699,7 +897,10 @@ jobs:
|
|||
echo "LICENSE_KEY=${{ secrets.RENDER_LICENSE_KEY }}" >> .env
|
||||
|
||||
- name: clean up old docker containers
|
||||
run: docker system prune -af
|
||||
run: |
|
||||
docker system prune -af --volumes
|
||||
echo "Disk space after Docker cleanup:"
|
||||
df -h
|
||||
|
||||
- name: Pulling the docker-compose file
|
||||
run: curl -LO https://tooljet-test.s3.us-west-1.amazonaws.com/docker-compose.yaml && mkdir postgres_data
|
||||
|
|
|
|||
61
.github/workflows/deploy-to-stage.yml
vendored
61
.github/workflows/deploy-to-stage.yml
vendored
|
|
@ -4,18 +4,18 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
branch_name:
|
||||
description: 'Git branch to build from'
|
||||
description: "Git branch to build from"
|
||||
required: true
|
||||
default: 'main'
|
||||
default: "lts-3.16"
|
||||
dockerfile_path:
|
||||
description: 'Path to Dockerfile'
|
||||
description: "Path to Dockerfile"
|
||||
required: true
|
||||
default: './docker/LTS/cloud/cloud-server.Dockerfile'
|
||||
default: "./docker/LTS/cloud/cloud-server.Dockerfile"
|
||||
type: choice
|
||||
options:
|
||||
- ./docker/LTS/cloud/cloud-server.Dockerfile
|
||||
docker_tag:
|
||||
description: 'Docker tag suffix (e.g., cloud-staging-v14)'
|
||||
description: "Docker tag suffix (e.g., cloud-staging-v14)"
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
|
|
@ -82,7 +82,7 @@ jobs:
|
|||
BRANCH_NAME=${{ github.event.inputs.branch_name }}
|
||||
|
||||
- name: Show the full Docker tag
|
||||
run: |
|
||||
run: |
|
||||
echo "✅ Docker image tagged as: $IMAGE_TAG"
|
||||
|
||||
# Deploy to AKS
|
||||
|
|
@ -144,6 +144,7 @@ jobs:
|
|||
- name: 📥 Manual Git checkout with submodules
|
||||
run: |
|
||||
set -e
|
||||
|
||||
BRANCH="${{ github.event.inputs.branch_name }}"
|
||||
REPO="https://x-access-token:${{ secrets.CUSTOM_GITHUB_TOKEN }}@github.com/${{ github.repository }}"
|
||||
|
||||
|
|
@ -155,6 +156,11 @@ jobs:
|
|||
git clone --recurse-submodules --depth=1 --branch "$BRANCH" "$REPO" repo
|
||||
cd repo
|
||||
|
||||
echo "🔎 Main repo: verifying checkout"
|
||||
MAIN_CURRENT=$(git rev-parse --abbrev-ref HEAD)
|
||||
echo "✅ Main repo: successfully checked out branch $MAIN_CURRENT"
|
||||
echo "📍 Main repo: current commit $(git rev-parse --short HEAD): $(git log -1 --pretty=%s)"
|
||||
|
||||
echo "🔁 Updating submodules"
|
||||
git submodule update --init --recursive
|
||||
|
||||
|
|
@ -162,13 +168,44 @@ jobs:
|
|||
|
||||
BRANCH="$BRANCH" git submodule foreach --recursive bash -c '
|
||||
name="$sm_path"
|
||||
echo "↪ $name: checking out branch $BRANCH"
|
||||
echo ""
|
||||
echo "Entering '\''$name'\''"
|
||||
echo "↪ $name: trying to checkout branch '\''$BRANCH'\''"
|
||||
|
||||
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null; then
|
||||
git fetch origin "$BRANCH:$BRANCH"
|
||||
git checkout "$BRANCH"
|
||||
git fetch origin "$BRANCH:$BRANCH" || {
|
||||
echo "❌ $name: fetch failed for $BRANCH"
|
||||
exit 1
|
||||
}
|
||||
|
||||
PREV=$(git rev-parse --short HEAD || echo "unknown")
|
||||
git checkout "$BRANCH" || {
|
||||
echo "❌ $name: checkout failed for $BRANCH"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Previous HEAD position was $PREV: $(git log -1 --pretty=%s || echo 'unknown')"
|
||||
echo "✅ $name: checked out branch $BRANCH"
|
||||
else
|
||||
echo "⚠️ Branch not found, falling back to main"
|
||||
git checkout main && git pull origin main
|
||||
echo "⚠️ $name: branch '$BRANCH' not found on origin. Falling back to 'lts-3.16'"
|
||||
PREV=$(git rev-parse --short HEAD || echo "unknown")
|
||||
git fetch origin lts-3.16:lts-3.16 || {
|
||||
echo "❌ $name: fetch failed for lts-3.16"
|
||||
exit 1
|
||||
}
|
||||
git checkout lts-3.16 || {
|
||||
echo "❌ $name: fallback to lts-3.16 failed"
|
||||
exit 1
|
||||
}
|
||||
echo "Previous HEAD position was $PREV: $(git log -1 --pretty=%s || echo 'unknown')"
|
||||
echo "✅ $name: now on branch lts-3.16"
|
||||
fi
|
||||
|
||||
CURRENT=$(git rev-parse --abbrev-ref HEAD)
|
||||
echo "🔎 $name: current branch = $CURRENT"
|
||||
if [ "$CURRENT" != "$BRANCH" ] && [ "$CURRENT" != "lts-3.16" ]; then
|
||||
echo "❌ $name: unexpected branch state — wanted '$BRANCH' or fallback 'lts-3.16', got '$CURRENT'"
|
||||
exit 1
|
||||
fi
|
||||
'
|
||||
|
||||
|
|
@ -181,7 +218,7 @@ jobs:
|
|||
run: npm install
|
||||
working-directory: repo
|
||||
|
||||
- name: 🛠️ Build project
|
||||
- name: 🛠️ Build the project
|
||||
run: npm run build:plugins:prod && npm run build:frontend
|
||||
working-directory: repo
|
||||
env:
|
||||
|
|
|
|||
9
.github/workflows/manual-docker-build.yml
vendored
9
.github/workflows/manual-docker-build.yml
vendored
|
|
@ -4,18 +4,19 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
branch_name:
|
||||
description: 'Git branch to build from'
|
||||
description: "Git branch to build from"
|
||||
required: true
|
||||
default: 'main'
|
||||
default: "main"
|
||||
dockerfile_path:
|
||||
description: 'Path to Dockerfile'
|
||||
description: "Path to Dockerfile"
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- ./docker/LTS/ee/ee-production.Dockerfile
|
||||
- ./docker/pre-release/ee/ee-production.Dockerfile
|
||||
- ./docker/LTS/cloud/cloud-server.Dockerfile
|
||||
docker_tag:
|
||||
description: 'Docker tag suffix (e.g., pre-release-14)'
|
||||
description: "Docker tag suffix (e.g., pre-release-14)"
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
69
.github/workflows/update-test-system.yml
vendored
69
.github/workflows/update-test-system.yml
vendored
|
|
@ -4,21 +4,21 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
branch_name:
|
||||
description: 'Git branch to build from'
|
||||
description: "Git branch to build from"
|
||||
required: true
|
||||
default: 'main'
|
||||
default: "main"
|
||||
dockerfile_path:
|
||||
description: 'Select Dockerfile'
|
||||
description: "Select Dockerfile"
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- ./docker/LTS/ee/ee-production.Dockerfile
|
||||
- ./docker/pre-release/ee/ee-production.Dockerfile
|
||||
docker_tag:
|
||||
description: 'Docker tag suffix (e.g., pre-release-14, 3.16-lts, etc.)'
|
||||
description: "Docker tag suffix (e.g., pre-release-14, 3.16-lts, etc.)"
|
||||
required: true
|
||||
test_system:
|
||||
description: 'Select test system'
|
||||
description: "Select test system"
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
|
|
@ -52,24 +52,27 @@ jobs:
|
|||
"${{ secrets.ALLOWED_USER10_TEST_SYSTEM }}"
|
||||
"${{ secrets.ALLOWED_USER11_TEST_SYSTEM }}"
|
||||
"${{ secrets.ALLOWED_USER12_TEST_SYSTEM }}"
|
||||
"${{ secrets.ALLOWED_USER13_TEST_SYSTEM }}"
|
||||
"${{ secrets.ALLOWED_USER14_TEST_SYSTEM }}"
|
||||
)
|
||||
|
||||
|
||||
current_user="${{ github.actor }}"
|
||||
authorized=false
|
||||
|
||||
|
||||
for user in "${allowed_users[@]}"; do
|
||||
if [[ "$current_user" == "$user" ]]; then
|
||||
authorized=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
if [[ "$authorized" == "false" ]]; then
|
||||
echo "❌ User '$current_user' is not authorized to trigger this workflow."
|
||||
exit 1
|
||||
else
|
||||
echo "✅ User '$current_user' is authorized."
|
||||
fi
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -94,6 +97,7 @@ jobs:
|
|||
else
|
||||
echo "tag=tooljet/tj-osv:$input_tag" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build and Push Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
|
|
@ -105,6 +109,7 @@ jobs:
|
|||
build-args: |
|
||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
BRANCH_NAME=${{ github.event.inputs.branch_name }}
|
||||
|
||||
- name: Show the full Docker tag
|
||||
run: echo "✅ Docker image built and pushed:${{ steps.taggen.outputs.tag }}"
|
||||
|
||||
|
|
@ -116,38 +121,39 @@ jobs:
|
|||
run: |
|
||||
test_system="${{ github.event.inputs.test_system }}"
|
||||
vm_host=$(echo '${{ secrets.VM_HOST_MAP_JSON }}' | jq -r --arg sys "$test_system" '.[$sys]')
|
||||
|
||||
|
||||
if [[ -z "$vm_host" || "$vm_host" == "null" ]]; then
|
||||
echo "VM mapping not found for $test_system"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "host=$vm_host" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Deploy to target environment
|
||||
run: |
|
||||
echo "$SSH_KEY" > key.pem
|
||||
chmod 600 key.pem
|
||||
|
||||
|
||||
IMAGE_TAG="${{ steps.taggen.outputs.tag }}"
|
||||
TARGET_SYSTEM="${{ github.event.inputs.test_system }}"
|
||||
|
||||
|
||||
# Debug: Show what we're deploying
|
||||
echo "DEBUG: IMAGE_TAG=$IMAGE_TAG"
|
||||
echo "DEBUG: TARGET_SYSTEM=$TARGET_SYSTEM"
|
||||
|
||||
|
||||
ssh -o StrictHostKeyChecking=no -i key.pem $SSH_USER@${{ steps.vmhost.outputs.host }} << EOF
|
||||
set -e
|
||||
|
||||
|
||||
IMAGE_TAG="$IMAGE_TAG"
|
||||
TARGET_SYSTEM="$TARGET_SYSTEM"
|
||||
|
||||
|
||||
cd ~
|
||||
echo "📁 Finding correct deployment directory"
|
||||
|
||||
|
||||
# Debug: Show variables on remote host
|
||||
echo "Debug on remote: IMAGE_TAG=\$IMAGE_TAG"
|
||||
echo "Debug on remote: TARGET_SYSTEM=\$TARGET_SYSTEM"
|
||||
|
||||
|
||||
if [[ "\$TARGET_SYSTEM" == *-3.16-lts ]]; then
|
||||
echo "Detected LTS system: \$TARGET_SYSTEM"
|
||||
echo "🔍 Searching for LTS directories..."
|
||||
|
|
@ -178,39 +184,39 @@ jobs:
|
|||
cd ~
|
||||
echo "✅ Now in directory: \$(pwd)"
|
||||
fi
|
||||
|
||||
|
||||
echo "🔐 Docker login"
|
||||
echo "${{ secrets.DOCKER_PASSWORD }}" | sudo docker login --username "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||
|
||||
|
||||
echo "current image"
|
||||
cat .env | grep TOOLJET_IMAGE
|
||||
|
||||
|
||||
echo "📦 Reading current TOOLJET_IMAGE from .env"
|
||||
CURRENT_IMAGE=\$(grep '^TOOLJET_IMAGE=' .env | cut -d '=' -f2- | tr -d '"' | tr -d "'")
|
||||
echo "Found CURRENT_IMAGE: \$CURRENT_IMAGE"
|
||||
|
||||
|
||||
echo "🛑 Stopping containers"
|
||||
sudo docker-compose down
|
||||
|
||||
|
||||
echo "📝 Updating .env with new image"
|
||||
sudo sed -i "s|^TOOLJET_IMAGE=.*|TOOLJET_IMAGE=\$IMAGE_TAG|" .env
|
||||
|
||||
|
||||
echo "📥 Pulling new image: \$IMAGE_TAG"
|
||||
if [ -z "\$IMAGE_TAG" ]; then
|
||||
echo "❌ IMAGE_TAG is empty!"
|
||||
exit 1
|
||||
fi
|
||||
sudo docker pull "\$IMAGE_TAG"
|
||||
|
||||
|
||||
echo "🚀 Starting container in background"
|
||||
sudo docker-compose up -d
|
||||
|
||||
|
||||
# Wait for ToolJet to start and show success message
|
||||
echo "⏳ Waiting for ToolJet to start (timeout: 300 seconds)..."
|
||||
SUCCESS_FOUND=false
|
||||
TIMEOUT=300
|
||||
ELAPSED=0
|
||||
|
||||
|
||||
while [ \$ELAPSED -lt \$TIMEOUT ]; do
|
||||
# Check for success message in logs
|
||||
if sudo docker-compose logs 2>/dev/null | grep -qE "🚀 TOOLJET APPLICATION STARTED SUCCESSFULLY|Ready to use at http://localhost:82 🚀|Ready to use at http://localhost:80"; then
|
||||
|
|
@ -223,7 +229,7 @@ jobs:
|
|||
sleep 10
|
||||
ELAPSED=\$((ELAPSED + 10))
|
||||
done
|
||||
|
||||
|
||||
if [ "\$SUCCESS_FOUND" = false ]; then
|
||||
echo "❌ Timeout reached without finding success logs"
|
||||
echo "📄 Showing current logs for troubleshooting..."
|
||||
|
|
@ -244,20 +250,19 @@ jobs:
|
|||
echo "✅ Rollback completed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "✅ Deployment successful!"
|
||||
|
||||
|
||||
echo "📌 Storing successful deployment info in .env"
|
||||
sudo sed -i "/^OLD_IMAGE=/d" .env
|
||||
echo "OLD_IMAGE=\$CURRENT_IMAGE" | sudo tee -a .env
|
||||
echo "📄 Final application logs:"
|
||||
sudo docker-compose logs --tail=50
|
||||
|
||||
|
||||
echo "🧹 Pruning old Docker images"
|
||||
sudo docker image prune -a -f
|
||||
|
||||
|
||||
EOF
|
||||
env:
|
||||
SSH_USER: ${{ secrets.AZURE_VM_USER }}
|
||||
SSH_KEY: ${{ secrets.AZURE_VM_KEY }}
|
||||
|
||||
|
|
|
|||
2
.version
2
.version
|
|
@ -1 +1 @@
|
|||
3.20.21-lts
|
||||
3.20.30-lts
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ module.exports = defineConfig({
|
|||
trashAssetsBeforeRuns: true,
|
||||
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
setupNodeEvents (on, config) {
|
||||
on("task", {
|
||||
readPdf(pathToPdf) {
|
||||
readPdf (pathToPdf) {
|
||||
return new Promise((resolve) => {
|
||||
const pdfPath = path.resolve(pathToPdf);
|
||||
let dataBuffer = fs.readFileSync(pdfPath);
|
||||
|
|
@ -33,7 +33,7 @@ module.exports = defineConfig({
|
|||
});
|
||||
|
||||
on("task", {
|
||||
readXlsx(filePath) {
|
||||
readXlsx (filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
let dataBuffer = fs.readFileSync(filePath);
|
||||
|
|
@ -48,7 +48,7 @@ module.exports = defineConfig({
|
|||
});
|
||||
|
||||
on("task", {
|
||||
deleteFile(filePath) {
|
||||
deleteFile (filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fullPath = path.resolve(filePath);
|
||||
if (fs.existsSync(fullPath)) {
|
||||
|
|
@ -69,7 +69,7 @@ module.exports = defineConfig({
|
|||
});
|
||||
|
||||
on("task", {
|
||||
deleteFolder(folderName) {
|
||||
deleteFolder (folderName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (fs.existsSync(folderName)) {
|
||||
rmdir(folderName, { maxRetries: 10, recursive: true }, (err) => {
|
||||
|
|
@ -87,7 +87,7 @@ module.exports = defineConfig({
|
|||
});
|
||||
|
||||
on("task", {
|
||||
dbConnection({ dbconfig, sql }) {
|
||||
dbConnection ({ dbconfig, sql }) {
|
||||
const client = new pg.Pool(dbconfig);
|
||||
return client.query(sql);
|
||||
},
|
||||
|
|
@ -110,18 +110,20 @@ module.exports = defineConfig({
|
|||
return config;
|
||||
},
|
||||
experimentalRunAllSpecs: true,
|
||||
experimentalModfyObstructiveThirdPartyCode: true,
|
||||
baseUrl: "http://localhost:8082",
|
||||
specPattern: "cypress/e2e/happyPath/**/*.cy.js",
|
||||
downloadsFolder: "cypress/downloads",
|
||||
numTestsKeptInMemory: 0,
|
||||
redirectionLimit: 5,
|
||||
redirectionLimit: 3,
|
||||
trashAssetsBeforeRuns: true,
|
||||
experimentalMemoryManagement: true,
|
||||
coverage: true,
|
||||
codeCoverageTasksRegistered: true,
|
||||
coverage: false,
|
||||
codeCoverageTasksRegistered: false,
|
||||
video: false,
|
||||
videoUploadOnPasses: false,
|
||||
experimentalStudio: true,
|
||||
// experimentalStudio: true,
|
||||
// experimentalPromptCommand: true,
|
||||
|
||||
// projectId: "ca6324a0-4210-4f7e-846a-71ca2766ca4",
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,35 +1,5 @@
|
|||
const envVar = Cypress.env("environment");
|
||||
|
||||
Cypress.Commands.add(
|
||||
"apiLogin",
|
||||
(
|
||||
userEmail = "dev@tooljet.io",
|
||||
userPassword = "password",
|
||||
workspaceId = "",
|
||||
redirection = "/"
|
||||
) => {
|
||||
cy.request({
|
||||
url: `${Cypress.env("server_host")}/api/authenticate/${workspaceId}`,
|
||||
method: "POST",
|
||||
body: {
|
||||
email: userEmail,
|
||||
password: userPassword,
|
||||
redirectTo: redirection,
|
||||
},
|
||||
})
|
||||
.its("body")
|
||||
.then((res) => {
|
||||
Cypress.env("workspaceId", res.current_organization_id);
|
||||
|
||||
Cypress.log({
|
||||
name: "Api login",
|
||||
displayName: "LOGIN: ",
|
||||
message: `: Success`,
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("apiCreateGDS", (url, name, kind, options) => {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
cy.request(
|
||||
|
|
@ -64,7 +34,7 @@ Cypress.Commands.add("apiCreateGDS", (url, name, kind, options) => {
|
|||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("apiFetchDataSourcesId", () => {
|
||||
Cypress.Commands.add("apiFetchDataSourcesIdFromApp", () => {
|
||||
cy.getAuthHeaders().then((headers) => {
|
||||
cy.request({
|
||||
method: "GET",
|
||||
|
|
@ -183,23 +153,6 @@ Cypress.Commands.add(
|
|||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("apiLogout", () => {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
cy.request(
|
||||
{
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/session/logout`,
|
||||
headers: {
|
||||
"Tj-Workspace-Id": Cypress.env("workspaceId"),
|
||||
Cookie: `tj_auth_token=${cookie.value}`,
|
||||
},
|
||||
},
|
||||
{ log: false }
|
||||
).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("apiAddQuery", (queryName, query, dataQueryId) => {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
|
|
@ -233,7 +186,7 @@ Cypress.Commands.add("apiAddQuery", (queryName, query, dataQueryId) => {
|
|||
|
||||
Cypress.Commands.add(
|
||||
"apiAddQueryToApp",
|
||||
({ queryName, options, dsName, dsKind }) => {
|
||||
({ queryName, options, dataSourceName, dsKind }) => {
|
||||
cy.getCookie("tj_auth_token", { log: false }).then((cookie) => {
|
||||
const authToken = cookie?.value;
|
||||
const workspaceId = Cypress.env("workspaceId");
|
||||
|
|
@ -259,10 +212,10 @@ Cypress.Commands.add(
|
|||
headers: commonHeaders,
|
||||
}).then((dsResponse) => {
|
||||
const dataSource = dsResponse.body.data_sources.find(
|
||||
(ds) => ds.name === dsName
|
||||
(ds) => ds.name === dataSourceName
|
||||
);
|
||||
const dataSourceID = dataSource.id;
|
||||
Cypress.env(`${dsName}`, dataSourceID);
|
||||
Cypress.env(`${dataSourceName}`, dataSourceID);
|
||||
|
||||
cy.request({
|
||||
method: "POST",
|
||||
|
|
@ -378,19 +331,6 @@ Cypress.Commands.add(
|
|||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("apiGetEnvironments", () => {
|
||||
cy.getAuthHeaders().then((headers) => {
|
||||
cy.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/app-environments`,
|
||||
headers: headers,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return response.body.environments;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("apiMakeAppPublic", (appId = Cypress.env("appId")) => {
|
||||
cy.getAuthHeaders().then((headers) => {
|
||||
cy.request({
|
||||
|
|
@ -495,30 +435,17 @@ Cypress.Commands.add("apiGetDataSourceIdByName", (dataSourceName) => {
|
|||
headers: headers,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
const dataSource = response.body.data_sources.find(
|
||||
(ds) => ds.name === dataSourceName
|
||||
);
|
||||
return dataSource.id;
|
||||
const id = response.body.data_sources.find(ds => ds.name === dataSourceName)?.id;
|
||||
Cypress.log({
|
||||
name: "apiGetDataSourceIdByName",
|
||||
displayName: "Data Source ID",
|
||||
message: `Data source ID for '${dataSourceName}': ${id}`,
|
||||
});
|
||||
return id;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("getAuthHeaders", (returnCached = false) => {
|
||||
let headers = {};
|
||||
if (returnCached) {
|
||||
return returnCached;
|
||||
} else {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
headers = {
|
||||
"Tj-Workspace-Id": Cypress.env("workspaceId"),
|
||||
Cookie: `tj_auth_token=${cookie.value}`,
|
||||
};
|
||||
Cypress.env("authHeaders", headers);
|
||||
return headers;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
"apiUpdateDataSource",
|
||||
(dataSourceName, envName, updateData) => {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const API_ENDPOINT =
|
|||
|
||||
Cypress.Commands.add(
|
||||
"appUILogin",
|
||||
(email = "dev@tooljet.io", password = "password") => {
|
||||
(email = "dev@tooljet.io", password = "password", status = 'success', toast = '') => {
|
||||
cy.clearAndType(onboardingSelectors.loginEmailInput, email);
|
||||
cy.clearAndType(onboardingSelectors.loginPasswordInput, password);
|
||||
cy.get(onboardingSelectors.signInButton).click();
|
||||
|
|
@ -27,7 +27,7 @@ Cypress.Commands.add(
|
|||
);
|
||||
|
||||
Cypress.Commands.add("clearAndType", (selector, text) => {
|
||||
cy.get(selector).type(`{selectall}{backspace}${text}`);
|
||||
cy.get(selector).should("be.visible", { timeout: 10000 }).click({ force: true }).type(`{selectall}{backspace}${text}`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("forceClickOnCanvas", () => {
|
||||
|
|
@ -53,9 +53,10 @@ Cypress.Commands.add("waitForAutoSave", () => {
|
|||
cy.wait(200);
|
||||
cy.get(commonSelectors.autoSave, { timeout: 20000 }).should(
|
||||
"have.text",
|
||||
commonText.autoSave,
|
||||
'',
|
||||
{ timeout: 20000 }
|
||||
);
|
||||
).find('svg')
|
||||
.should('be.visible', { timeout: 20000 });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("createApp", (appName) => {
|
||||
|
|
@ -65,7 +66,7 @@ Cypress.Commands.add("createApp", (appName) => {
|
|||
: commonSelectors.appCreateButton;
|
||||
|
||||
cy.get("body").then(($title) => {
|
||||
cy.get(getAppButtonSelector($title)).click();
|
||||
cy.get(getAppButtonSelector($title)).scrollIntoView().click({ force: true });//workaround for cypress dashboard click issue
|
||||
cy.clearAndType('[data-cy="app-name-input"]', appName);
|
||||
cy.get('[data-cy="create-app"]').click();
|
||||
});
|
||||
|
|
@ -77,37 +78,47 @@ Cypress.Commands.add(
|
|||
"dragAndDropWidget",
|
||||
(
|
||||
widgetName,
|
||||
positionX = 80,
|
||||
positionY = 80,
|
||||
positionX = 100,
|
||||
positionY = 100,
|
||||
widgetName2 = widgetName,
|
||||
canvas = commonSelectors.canvas
|
||||
) => {
|
||||
const dataTransfer = new DataTransfer();
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
cy.get("body")
|
||||
.then(($body) => {
|
||||
const isSearchVisible = $body
|
||||
.find(commonSelectors.searchField)
|
||||
.is(":visible");
|
||||
cy.get('[data-cy="right-sidebar-plus-button"]').click();
|
||||
cy.get(commonSelectors.searchField).should('be.visible');
|
||||
|
||||
if (!isSearchVisible) {
|
||||
cy.get('[data-cy="right-sidebar-plus-button"]').click();
|
||||
}
|
||||
cy.get(commonSelectors.searchField).first().clear().type(widgetName);
|
||||
cy.get(commonWidgetSelector.widgetBox(widgetName2)).should('be.visible');
|
||||
|
||||
cy.get(commonWidgetSelector.widgetBox(widgetName2))
|
||||
.trigger('mousedown', { which: 1, button: 0, force: true })
|
||||
.trigger('dragstart', { dataTransfer, force: true });
|
||||
|
||||
cy.get(canvas)
|
||||
.trigger('dragenter', {
|
||||
dataTransfer,
|
||||
clientX: positionX,
|
||||
clientY: positionY,
|
||||
force: true
|
||||
})
|
||||
.then(() => {
|
||||
cy.clearAndType(commonSelectors.searchField, widgetName);
|
||||
.trigger('dragover', {
|
||||
dataTransfer,
|
||||
clientX: positionX,
|
||||
clientY: positionY,
|
||||
force: true
|
||||
});
|
||||
|
||||
cy.get(commonWidgetSelector.widgetBox(widgetName2)).trigger(
|
||||
"dragstart",
|
||||
{ dataTransfer },
|
||||
{ force: true }
|
||||
);
|
||||
cy.get(canvas).trigger("drop", positionX, positionY, {
|
||||
dataTransfer,
|
||||
force: true,
|
||||
});
|
||||
|
||||
cy.get(canvas)
|
||||
.trigger('drop', {
|
||||
dataTransfer,
|
||||
clientX: positionX,
|
||||
clientY: positionY,
|
||||
force: true
|
||||
})
|
||||
.trigger('mouseup', { force: true });
|
||||
|
||||
cy.waitForAutoSave();
|
||||
}
|
||||
);
|
||||
|
|
@ -224,8 +235,11 @@ Cypress.Commands.add("renameApp", (appName) => {
|
|||
`{selectAll}{backspace}${appName}`,
|
||||
{ force: true }
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.waitForAutoSave();
|
||||
cy.get(commonSelectors.renameAppButton).should("be.enabled").click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.appRenamedToast
|
||||
);
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
|
|
@ -410,7 +424,7 @@ Cypress.Commands.add("getPosition", (componentName) => {
|
|||
);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("defaultWorkspaceLogin", () => {
|
||||
Cypress.Commands.add("defaultWorkspaceLogin", (workspaceSlug = 'my-workspace',) => {
|
||||
// cy.task("dbConnection", {
|
||||
// dbconfig: Cypress.env("app_db"),
|
||||
// sql: `
|
||||
|
|
@ -421,10 +435,9 @@ Cypress.Commands.add("defaultWorkspaceLogin", () => {
|
|||
cy.apiLogin(
|
||||
"dev@tooljet.io",
|
||||
"password",
|
||||
// workspaceId,
|
||||
// "/my-workspace"
|
||||
).then(() => {
|
||||
cy.visit("/");
|
||||
cy.visit(`/${workspaceSlug}`);
|
||||
cy.wait(2000);
|
||||
cy.get(commonSelectors.homePageLogo, { timeout: 10000 });
|
||||
});
|
||||
|
|
@ -557,7 +570,7 @@ Cypress.Commands.add("installMarketplacePlugin", (pluginName) => {
|
|||
}
|
||||
});
|
||||
|
||||
function installPlugin (pluginName) {
|
||||
function installPlugin(pluginName) {
|
||||
cy.get('[data-cy="-list-item"]').eq(1).click();
|
||||
cy.wait(1000);
|
||||
|
||||
|
|
@ -659,4 +672,27 @@ Cypress.Commands.add("runSqlQuery", (query, db = Cypress.env("app_db")) => {
|
|||
dbconfig: db,
|
||||
sql: query,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
"openWorkflow",
|
||||
(
|
||||
slug = "",
|
||||
workspaceId = Cypress.env("workspaceId"),
|
||||
workflowId = Cypress.env("workflowId"),
|
||||
) => {
|
||||
cy.intercept("GET", "/api/apps/*").as("getWorkflowData");
|
||||
cy.window({ log: false }).then((win) => {
|
||||
win.localStorage.setItem("walkthroughCompleted", "true");
|
||||
});
|
||||
cy.visit(`/${workspaceId}/apps/${workflowId}/${slug}`);
|
||||
|
||||
cy.wait("@getWorkflowData").then((interception) => {
|
||||
const responseData = interception.response.body;
|
||||
|
||||
Cypress.env("editingVersionId", responseData.editing_version.id);
|
||||
Cypress.env("environmentId", responseData.editorEnvironment.id);
|
||||
Cypress.env("workflowId", responseData.id);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,66 @@
|
|||
const envVar = Cypress.env("environment");
|
||||
|
||||
Cypress.Commands.add(
|
||||
"apiLogin",
|
||||
(
|
||||
userEmail = "dev@tooljet.io",
|
||||
userPassword = "password",
|
||||
workspaceId = "",
|
||||
redirection = "/"
|
||||
) => {
|
||||
cy.request({
|
||||
url: `${Cypress.env("server_host")}/api/authenticate/${workspaceId}`,
|
||||
method: "POST",
|
||||
body: {
|
||||
email: userEmail,
|
||||
password: userPassword,
|
||||
redirectTo: redirection,
|
||||
},
|
||||
})
|
||||
.its("body")
|
||||
.then((res) => {
|
||||
Cypress.env("workspaceId", res.current_organization_id);
|
||||
|
||||
Cypress.log({
|
||||
name: "Api login",
|
||||
displayName: "LOGIN: ",
|
||||
message: `: Success`,
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("apiLogout", () => {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
cy.request(
|
||||
{
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/session/logout`,
|
||||
headers: {
|
||||
"Tj-Workspace-Id": Cypress.env("workspaceId"),
|
||||
Cookie: `tj_auth_token=${cookie.value}`,
|
||||
},
|
||||
},
|
||||
{ log: false }
|
||||
).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("apiGetEnvironments", () => {
|
||||
cy.getAuthHeaders().then((headers) => {
|
||||
cy.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/app-environments`,
|
||||
headers: headers,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return response.body.environments;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("apiCreateWorkspace", (workspaceName, workspaceSlug) => {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
cy.request(
|
||||
|
|
@ -100,12 +161,61 @@ Cypress.Commands.add("apiUpdateWsConstant", (id, updateValue, envName) => {
|
|||
url: `${Cypress.env("server_host")}/api/organization-constants/${id}`,
|
||||
headers: headers,
|
||||
body: {
|
||||
value: String(updateValue), // ensure it's a string
|
||||
value: String(updateValue),
|
||||
environment_id: envId,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
response.body; // returns the PATCH response body
|
||||
response.body;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("apiGetGroupId", (groupName) => {
|
||||
return cy.getAuthHeaders().then((headers) => {
|
||||
return cy
|
||||
.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions`,
|
||||
headers: headers,
|
||||
log: false,
|
||||
})
|
||||
.then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
const group = response.body.groupPermissions.find(
|
||||
(g) => g.name === groupName
|
||||
);
|
||||
if (!group) throw new Error(`Group with name ${groupName} not found`);
|
||||
return group.id;
|
||||
});
|
||||
});
|
||||
});
|
||||
Cypress.Commands.add("apiUpdateUserRole", (email, role) => {
|
||||
return cy.task("dbConnection", {
|
||||
dbconfig: Cypress.env("app_db"),
|
||||
sql: `
|
||||
SELECT id
|
||||
FROM users
|
||||
WHERE email='${email}'
|
||||
LIMIT 1;
|
||||
`,
|
||||
}).then((resp) => {
|
||||
const userId = resp.rows[0]?.id;
|
||||
if (!userId) throw new Error(`User with email ${email} not found`);
|
||||
return userId;
|
||||
}).then((userId) => {
|
||||
return cy.getAuthHeaders().then((headers) => {
|
||||
return cy.request({
|
||||
method: "PUT",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/role/user`,
|
||||
headers: headers,
|
||||
body: {
|
||||
newRole: role,
|
||||
userId: userId,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -116,47 +226,71 @@ Cypress.Commands.add(
|
|||
(
|
||||
groupName,
|
||||
name,
|
||||
canEdit = false,
|
||||
canView = true,
|
||||
hideFromDashboard = false,
|
||||
resourceType = "app",
|
||||
permissions = {},
|
||||
resourcesToAdd = []
|
||||
) => {
|
||||
cy.getAuthHeaders().then((headers) => {
|
||||
// Fetch group permissions
|
||||
cy.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions`,
|
||||
headers: headers,
|
||||
log: false,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
const group = response.body.groupPermissions.find(
|
||||
(g) => g.name === groupName
|
||||
);
|
||||
if (!group) throw new Error(`Group with name ${groupName} not found`);
|
||||
cy.apiGetGroupId(groupName).then((groupId) => {
|
||||
const environment = Cypress.env("environment");
|
||||
const isEnterprise = environment === "Enterprise";
|
||||
|
||||
const groupId = group.id;
|
||||
const resourceTypeMap = {
|
||||
app: { type: "app", endpoint: "app" },
|
||||
workflow: { type: "workflow", endpoint: "data-source" },
|
||||
datasource: { type: "data_source", endpoint: "data-source" },
|
||||
};
|
||||
|
||||
// Create granular permission
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/granular-permissions`,
|
||||
headers: headers,
|
||||
body: {
|
||||
const { type, endpoint } =
|
||||
resourceTypeMap[resourceType] || resourceTypeMap.app;
|
||||
|
||||
const url = isEnterprise
|
||||
? `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/granular-permissions/${endpoint}`
|
||||
: `${Cypress.env("server_host")}/api/v2/group-permissions/granular-permissions`;
|
||||
let permissionObject;
|
||||
|
||||
if (resourceType === "datasource") {
|
||||
permissionObject = {
|
||||
action: {
|
||||
canUse: permissions.canUse ?? true,
|
||||
canConfigure: permissions.canConfigure ?? false,
|
||||
},
|
||||
resourcesToAdd,
|
||||
};
|
||||
} else {
|
||||
permissionObject = {
|
||||
canEdit: permissions.canEdit ?? false,
|
||||
canView: permissions.canView ?? true,
|
||||
hideFromDashboard: permissions.hideFromDashboard ?? false,
|
||||
resourcesToAdd,
|
||||
};
|
||||
}
|
||||
|
||||
const body = isEnterprise
|
||||
? {
|
||||
name,
|
||||
type,
|
||||
groupId,
|
||||
isAll: true,
|
||||
createResourcePermissionObject: permissionObject,
|
||||
}
|
||||
: {
|
||||
name,
|
||||
type: "app",
|
||||
groupId,
|
||||
isAll: true,
|
||||
createAppsPermissionsObject: {
|
||||
canEdit,
|
||||
canView,
|
||||
hideFromDashboard,
|
||||
resourcesToAdd,
|
||||
},
|
||||
},
|
||||
createAppsPermissionsObject: permissionObject,
|
||||
};
|
||||
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: url,
|
||||
headers: headers,
|
||||
body: body,
|
||||
log: false,
|
||||
}).then((res) => {
|
||||
expect(res.status).to.equal(201);
|
||||
cy.log(`Created ${resourceType} granular permission: ${name}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -167,22 +301,7 @@ Cypress.Commands.add(
|
|||
"apiDeleteGranularPermission",
|
||||
(groupName, typesToDelete = []) => {
|
||||
cy.getAuthHeaders().then((headers) => {
|
||||
// Step 1: Get the group by name
|
||||
cy.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions`,
|
||||
headers,
|
||||
log: false,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
const group = response.body.groupPermissions.find(
|
||||
(g) => g.name === groupName
|
||||
);
|
||||
if (!group) throw new Error(`Group with name ${groupName} not found`);
|
||||
|
||||
const groupId = group.id;
|
||||
|
||||
// Step 2: Get all granular permissions for the group
|
||||
cy.apiGetGroupId(groupName).then((groupId) => {
|
||||
cy.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/granular-permissions`,
|
||||
|
|
@ -192,23 +311,30 @@ Cypress.Commands.add(
|
|||
expect(granularResponse.status).to.equal(200);
|
||||
const granularPermissions = granularResponse.body;
|
||||
|
||||
// Step 3: Filter if typesToDelete is specified
|
||||
const permissionsToDelete = typesToDelete.length
|
||||
? granularPermissions.filter((perm) =>
|
||||
typesToDelete.includes(perm.type)
|
||||
)
|
||||
: granularPermissions;
|
||||
|
||||
// Step 4: Delete each granular permission
|
||||
permissionsToDelete.forEach((permission) => {
|
||||
const typeEndpointMap = {
|
||||
app: "app",
|
||||
workflow: "app",
|
||||
data_source: "data-source",
|
||||
};
|
||||
const endpoint = typeEndpointMap[permission.type] || "app";
|
||||
|
||||
cy.request({
|
||||
method: "DELETE",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/granular-permissions/app/${permission.id}`,
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/granular-permissions/${endpoint}/${permission.id}`,
|
||||
headers,
|
||||
log: false,
|
||||
}).then((deleteResponse) => {
|
||||
expect(deleteResponse.status).to.equal(200);
|
||||
cy.log(`Deleted granular permission: ${permission.name}`);
|
||||
cy.log(
|
||||
`Deleted ${permission.type} granular permission: ${permission.name}`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -310,14 +436,13 @@ Cypress.Commands.add(
|
|||
redirectTo = "/",
|
||||
}) => {
|
||||
cy.intercept("POST", "/api/oauth/sign-in/*", (req) => {
|
||||
// Inject missing params if not present
|
||||
if (!req.body.organizationId) {
|
||||
req.body.organizationId = organizationId;
|
||||
}
|
||||
if (!req.body.redirectTo) {
|
||||
req.body.redirectTo = redirectTo;
|
||||
}
|
||||
req.continue(); // Send the modified request
|
||||
req.continue();
|
||||
}).as("oidcSignIn");
|
||||
cy.getSsoConfigId("openid").then((ssoConfigId) => {
|
||||
const configIdToUse = ssoConfigId;
|
||||
|
|
@ -375,7 +500,6 @@ Cypress.Commands.add(
|
|||
const redirectUrl = authResp.headers["location"];
|
||||
const params = new URL(redirectUrl).searchParams;
|
||||
const code = params.get("code");
|
||||
// 6. Exchange code for tokens
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: `https://${oktaDomain}/oauth2/v1/token`,
|
||||
|
|
@ -567,3 +691,45 @@ Cypress.Commands.add(
|
|||
});
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add(
|
||||
"apiUpdateGroupPermission",
|
||||
(groupName, permissionPayload) => {
|
||||
return cy.apiGetGroupId(groupName).then((groupId) => {
|
||||
return cy.getAuthHeaders().then((headers) => {
|
||||
return cy
|
||||
.request({
|
||||
method: "PUT",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}`,
|
||||
headers: headers,
|
||||
body: permissionPayload,
|
||||
log: false,
|
||||
})
|
||||
.then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return response.body;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("getAuthHeaders", (returnCached = false) => {
|
||||
let headers = {};
|
||||
if (returnCached) {
|
||||
return returnCached;
|
||||
} else {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
headers = {
|
||||
"Tj-Workspace-Id": Cypress.env("workspaceId"),
|
||||
Cookie: `tj_auth_token=${cookie.value}`,
|
||||
};
|
||||
Cypress.env("authHeaders", headers);
|
||||
Cypress.log({
|
||||
name: "getAuthHeaders",
|
||||
message: `Auth headers: ${JSON.stringify(headers)}`,
|
||||
});
|
||||
return headers;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import { commonText } from "Texts/common";
|
|||
import { selectAppCardOption } from "Support/utils/common";
|
||||
import { navigateBackToWorkflowsDashboard } from "Support/utils/workFlows";
|
||||
|
||||
Cypress.Commands.add("createWorkflowApp", (wfName) => {
|
||||
Cypress.Commands.add("createWorkflowApp", (workflowName) => {
|
||||
cy.get(workflowSelector.globalWorkFlowsIcon).click();
|
||||
cy.get(workflowSelector.workflowsCreateButton).click();
|
||||
cy.get(workflowSelector.workFlowNameInputField).type(wfName);
|
||||
cy.get(workflowSelector.workFlowNameInputField).type(workflowName);
|
||||
cy.get(workflowSelector.createWorkFlowsButton).click();
|
||||
});
|
||||
|
||||
|
|
@ -115,10 +115,10 @@ Cypress.Commands.add(
|
|||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("deleteWorkflow", (wfName) => {
|
||||
Cypress.Commands.add("deleteWorkflow", (workflowName) => {
|
||||
cy.intercept("DELETE", "/api/apps/*").as("appDeleted");
|
||||
navigateBackToWorkflowsDashboard();
|
||||
cy.get(commonSelectors.appCard(wfName))
|
||||
cy.get(commonSelectors.appCard(workflowName))
|
||||
.realHover()
|
||||
.find(commonSelectors.appCardOptionsButton)
|
||||
.realHover()
|
||||
|
|
@ -128,9 +128,9 @@ Cypress.Commands.add("deleteWorkflow", (wfName) => {
|
|||
cy.wait("@appDeleted");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("deleteWorkflowfromDashboard", (wfName) => {
|
||||
Cypress.Commands.add("deleteWorkflowfromDashboard", (workflowName) => {
|
||||
cy.intercept("DELETE", "/api/apps/*").as("appDeleted");
|
||||
cy.get(commonSelectors.appCard(wfName))
|
||||
cy.get(commonSelectors.appCard(workflowName))
|
||||
.realHover()
|
||||
.find(commonSelectors.appCardOptionsButton)
|
||||
.realHover()
|
||||
|
|
@ -142,13 +142,17 @@ Cypress.Commands.add("deleteWorkflowfromDashboard", (wfName) => {
|
|||
|
||||
Cypress.Commands.add(
|
||||
"exportWorkflowApp",
|
||||
(wfName, fixtureFile = "cypress/fixtures/exportedApp.json") => {
|
||||
(workflowName, fixtureFile = "cypress/fixtures/exportedApp.json") => {
|
||||
navigateBackToWorkflowsDashboard();
|
||||
|
||||
selectAppCardOption(
|
||||
wfName,
|
||||
commonSelectors.appCardOptions(workflowsText.exportWFOption)
|
||||
);
|
||||
cy.get(`[data-cy="${workflowName}-card"]`)
|
||||
.trigger('mouseover')
|
||||
.find('[data-cy="app-card-menu-icon"]')
|
||||
.click({ force: true });
|
||||
|
||||
cy.get(commonSelectors.appCardOptions(workflowsText.exportWFOption))
|
||||
.click();
|
||||
|
||||
cy.wait(2000);
|
||||
|
||||
cy.exec("ls -t ./cypress/downloads/ | head -1").then((result) => {
|
||||
|
|
@ -158,20 +162,21 @@ Cypress.Commands.add(
|
|||
cy.writeFile(fixtureFile, json);
|
||||
});
|
||||
});
|
||||
cy.deleteWorkflowfromDashboard(wfName);
|
||||
|
||||
cy.deleteWorkflowfromDashboard(workflowName);
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("addWorkflowInApp", (wfName) => {
|
||||
Cypress.Commands.add("addWorkflowInApp", (workflowName) => {
|
||||
cy.get(workflowSelector.showDSPopoverButton).click();
|
||||
cy.get(workflowSelector.workflowSearchInput).type(
|
||||
workflowsText.workflowLabel
|
||||
);
|
||||
cy.contains(`[id*="react-select-"]`, workflowsText.workflowLabel).click();
|
||||
cy.get(workflowSelector.queryRenameInput).clear().type(wfName);
|
||||
cy.get(workflowSelector.queryRenameInput).clear().type(workflowName);
|
||||
cy.get(workflowSelector.workflowDropdown).parent()
|
||||
.find('.react-select__control')
|
||||
.click();
|
||||
cy.get(workflowSelector.workflowSelectInput).realType(wfName);
|
||||
cy.get(workflowSelector.workflowSelectOption).contains(wfName).click();
|
||||
cy.get(workflowSelector.workflowSelectInput).realType(workflowName);
|
||||
cy.get(workflowSelector.workflowSelectOption).contains(workflowName).click();
|
||||
});
|
||||
|
|
|
|||
102
cypress-tests/cypress/commands/workflowsApiCommands.js
Normal file
102
cypress-tests/cypress/commands/workflowsApiCommands.js
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
const envVar = Cypress.env("environment");
|
||||
|
||||
Cypress.Commands.add("apiCreateWorkflow", (workflowName, reuseSession = false) => {
|
||||
cy.getAuthHeaders(reuseSession).then((headers) => {
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: `${Cypress.env("server_host")}/api/apps`,
|
||||
headers: headers,
|
||||
body: {
|
||||
icon: "sentfast",
|
||||
name: workflowName,
|
||||
type: "workflow",
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
|
||||
const workflowId = response.body?.id || response.allRequestResponses?.[0]?.["Response Body"]?.id;
|
||||
const userId = response.body?.user_id || response.allRequestResponses?.[0]?.["Response Body"]?.user_id;
|
||||
|
||||
Cypress.env("workflowId", workflowId);
|
||||
Cypress.env("user_id", userId);
|
||||
|
||||
Cypress.log({
|
||||
name: "Workflow create",
|
||||
displayName: "WORKFLOW CREATED",
|
||||
message: `: ${response.body.name}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Cypress.Commands.add(
|
||||
"openWorkflowByName",
|
||||
(
|
||||
workflowName,
|
||||
workspaceId = Cypress.env("workspaceId"),
|
||||
componentSelector = "[data-cy='workflow-canvas']",
|
||||
reuseSession = false
|
||||
) => {
|
||||
cy.getAuthHeaders(reuseSession).then((headers) => {
|
||||
cy.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/apps?page=1&type=workflow&searchKey=${workflowName}`,
|
||||
headers: headers,
|
||||
}, { log: false }).then((response) => {
|
||||
const workflow = response.body.apps?.find(
|
||||
app => app.name === workflowName || app.slug === workflowName
|
||||
);
|
||||
|
||||
if (workflow) {
|
||||
Cypress.env("workflowId", workflow.id);
|
||||
cy.openWorkflow(workflow.slug, workspaceId, workflow.id, componentSelector);
|
||||
|
||||
Cypress.log({
|
||||
name: "Workflow Open",
|
||||
displayName: "WORKFLOW OPENED",
|
||||
message: `: ${workflowName}`,
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Workflow "${workflowName}" not found`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("apiDeleteWorkflow", (workflowName, reuseSession = false) => {
|
||||
cy.getAuthHeaders(reuseSession).then((headers) => {
|
||||
cy.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/apps?page=1&type=workflow&searchKey=${workflowName}`,
|
||||
headers: headers,
|
||||
}, { log: false }).then((response) => {
|
||||
const workflow = response.body.apps?.find(
|
||||
app => app.name === workflowName || app.slug === workflowName
|
||||
);
|
||||
|
||||
if (workflow) {
|
||||
cy.request({
|
||||
method: "DELETE",
|
||||
url: `${Cypress.env("server_host")}/api/apps/${workflow.id}`,
|
||||
headers: headers,
|
||||
}, { log: false }).then((deleteResponse) => {
|
||||
expect(deleteResponse.status).to.equal(200);
|
||||
Cypress.log({
|
||||
name: "Workflow Delete",
|
||||
displayName: "WORKFLOW DELETED",
|
||||
message: `: ${workflowName}`,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Cypress.log({
|
||||
name: "Workflow Not Found",
|
||||
displayName: "WORKFLOW NOT FOUND",
|
||||
message: `: ${workflowName}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -89,6 +89,7 @@ export const commonSelectors = {
|
|||
invitedUserEmail: '[data-cy="email-input-value"]',
|
||||
invitedUseremail: '[data-cy="email-input-input-value"]',
|
||||
acceptInviteButton: '[data-cy="accept-invite-button"]',
|
||||
homePageIcon: '[data-cy="icon-home"]',
|
||||
databaseIcon: '[data-cy="icon-database"]',
|
||||
profileSettings: '[data-cy="profile-settings"]',
|
||||
workspaceSettings: '[data-cy="workspace-settings"]',
|
||||
|
|
@ -176,7 +177,9 @@ export const commonSelectors = {
|
|||
resetPasswordButton: '[data-cy="reset-password-button"]',
|
||||
resetPasswordPageDescription: '[data-cy="reset-password-page-description"]',
|
||||
backToLoginButton: '[data-cy="back-to-login"]',
|
||||
breadcrumbTitle: '[data-cy="app-header-label"]>>',
|
||||
// breadcrumbTitle: '[data-cy="app-header-label"]>>',
|
||||
breadcrumbHeaderTitle: '[data-cy="breadcrumb-header-applications"]>>',
|
||||
breadcrumbTitle: '[data-cy="breadcrumb-header-workspace-settings"]>>',
|
||||
// breadcrumbPageTitle: '[data-cy="app-header-label"]',
|
||||
breadcrumbPageTitle: '[data-cy="breadcrumb-page-title"]',
|
||||
labelFullNameInput: '[data-cy="name-label"]',
|
||||
|
|
@ -190,6 +193,7 @@ export const commonSelectors = {
|
|||
addNewDataSourceButton: '[data-cy="add-new-data-source-button"]',
|
||||
saveButton: '[data-cy="save-button"]',
|
||||
appEditButton: '[data-cy="edit-button"]',
|
||||
editorAppNameInput: '[data-cy="editor-app-name-input"]',
|
||||
onboardingRadioButton: (radioButtonText) => {
|
||||
return `[data-cy="${cyParamName(radioButtonText)}-radio-button"]`;
|
||||
},
|
||||
|
|
@ -268,6 +272,7 @@ export const commonSelectors = {
|
|||
chooseFromTemplateButton: '[data-cy="choose-from-template-button"]',
|
||||
CreateAppFromTemplateButton: '[data-cy="create-new-app-from-template-title"]',
|
||||
settingsIcon: '[data-cy="settings-icon"]',
|
||||
previewSettings: '[data-cy="preview-settings"]',
|
||||
marketplaceOption: '[data-cy="marketplace-option"]',
|
||||
backToAppOption: '[data-cy="back-to-app-option"]',
|
||||
databaseOption: '[data-cy="database-option"]',
|
||||
|
|
@ -281,6 +286,8 @@ export const commonSelectors = {
|
|||
defaultModalTitle: '[data-cy="modal-title"]',
|
||||
workspaceConstantsIcon: '[data-cy="icon-workspace-constants"]',
|
||||
confirmationButton: '[data-cy="confirmation-button"]',
|
||||
modalConfirmButton: '[data-cy="modal-confirm-button"]',
|
||||
rightSidebarPlusButton: '[data-cy="right-sidebar-plus-button"]',
|
||||
|
||||
textField: (fieldName) => {
|
||||
return `[data-cy="${cyParamName(fieldName)}-text-field"]`;
|
||||
|
|
|
|||
|
|
@ -55,4 +55,24 @@ export const dashboardSelector = {
|
|||
slugSuccessLabel: '[data-cy="slug-sucess-label"]',
|
||||
slugErrorLabel: '[data-cy="slug-error-label"]',
|
||||
editWorkspaceTitle: '[data-cy="edit-workspace-title"]',
|
||||
|
||||
homePagePromptHeader: '[data-cy="home-page-prompt-header"]',
|
||||
promptInput: '[data-cy="prompt-input"]',
|
||||
homePageDividerText: '[data-cy="divider-text"]',
|
||||
appCardWidget: '[data-cy="getstarted-app-widget"]',
|
||||
templateCardWidget: '[data-cy="getstarted-templates-widget"]',
|
||||
databaseCardWidget: '[data-cy="getstarted-datasource-widget"]',
|
||||
workflowCardWidget: '[data-cy="getstarted-workflow-widget"]',
|
||||
|
||||
widgetCardName: (cardType) => {
|
||||
return `[data-cy="getstarted-${cardType}-widget"]`;
|
||||
},
|
||||
widgetCardTitle: '[data-cy="widget-card-title"]',
|
||||
widgetCardDescription: '[data-cy="widget-card-description"]',
|
||||
homePagePromptTextArea: '[data-cy="prompt-textarea"]',
|
||||
promptEnterButton: '[data-cy="prompt-enter-button"]',
|
||||
aiIcon: '[data-cy="ai-icon"]',
|
||||
homePageIcon: (iconName) => {
|
||||
return `[data-cy="${iconName}s-icon"]`;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export const appVersionSelectors = {
|
|||
versionNameInputField: '[data-cy="version-name-input-field"]',
|
||||
createVersionFromLabel: '[data-cy="create-version-from-label"]',
|
||||
createVersionInputField: '[data-cy="create-version-from-input-field"]',
|
||||
createNewVersionButton: '[data-cy="create-new-version-button"]',
|
||||
createNewVersionButton: '[data-cy="create-new-version-button"]:last',
|
||||
appVersionContentList: ".react-select__menu-list",
|
||||
};
|
||||
export const exportAppModalSelectors = {
|
||||
|
|
|
|||
82
cypress-tests/cypress/constants/selectors/license.js
Normal file
82
cypress-tests/cypress/constants/selectors/license.js
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
export const cyParamName = (paramName = "") => {
|
||||
return String(paramName).toLowerCase().replace(/\s+/g, "-");
|
||||
};
|
||||
|
||||
export const cyParamNameCamelCase = (paramName = "") => {
|
||||
return paramName
|
||||
.replace(/[^a-zA-Z0-9\s]/g, "")
|
||||
.toLowerCase()
|
||||
.replace(/\s+(\w)/g, (_, c) => c.toUpperCase());
|
||||
};
|
||||
|
||||
export const licenseSelectors = {
|
||||
comparePlansText: '[data-cy="compare-plans-button"]',
|
||||
listOfItems: (itemName) => {
|
||||
return `[data-cy="${cyParamName(itemName)}-list-item"]`;
|
||||
},
|
||||
tabTitle: (tabName) => {
|
||||
return `[data-cy="${cyParamNameCamelCase(tabName)}-tab-title"]`;
|
||||
},
|
||||
subTab: (subTabName) => {
|
||||
return `[data-cy="${cyParamName(subTabName)}-sub-tab"]`;
|
||||
},
|
||||
numberOfTextLabel: (label) => {
|
||||
return `[data-cy="number-of-${cyParamName(label)}-label"]`;
|
||||
},
|
||||
inputField: (fieldName) => {
|
||||
return `[data-cy="${cyParamName(fieldName)}-field"]`;
|
||||
},
|
||||
label: (labelName) => {
|
||||
return `[data-cy="${cyParamName(labelName)}-label"]`;
|
||||
},
|
||||
|
||||
limitInfo: (type) => {
|
||||
return `[data-cy="${cyParamName(type)}-limit-info"]`;
|
||||
},
|
||||
limitHeading: (type) => {
|
||||
return `[data-cy="${cyParamName(type)}-limit-heading"]`;
|
||||
},
|
||||
|
||||
circularToggleDisabledIcon: '[data-cy="circular-toggle-disabled-icon"]',
|
||||
licenseBannerHeading: '[data-cy="license-banner-heading"]',
|
||||
licenseBannerInfo: '[data-cy="license-banner-info"]',
|
||||
paidFeatureButton: '[data-cy="paid-feature-button"]',
|
||||
warningIcon: '[data-cy="warning-icon"]',
|
||||
noDomainLinkedLabel: '[data-cy="no-domain-header"]',
|
||||
noDomainInfoText: '[data-cy="no-domain-info-text"]',
|
||||
licenseOption: '[data-cy="license-list-item"]',
|
||||
licenseKeyOption: '[data-cy="license-key-list-item"]',
|
||||
limitOption: '[data-cy="limits-list-item"]',
|
||||
accessOption: '[data-cy="access-list-item"]',
|
||||
domainOption: '[data-cy="domain-list-item"]',
|
||||
licenseKeyTitle: '[data-cy="licenseKey-tab-title"]',
|
||||
licenseLabel: '[data-cy="license-label"]',
|
||||
updateButton: '[data-cy="update-button"]',
|
||||
limitsTabTitle: '[data-cy="limits-tab-title"]',
|
||||
appsTab: '[data-cy="apps-sub-tab"]',
|
||||
noOfAppsLabel: '[data-cy="number-of-apps-label"]',
|
||||
noOfAppsfield: '[data-cy="apps-field"]',
|
||||
workspaceTab: '[data-cy="workspaces-sub-tab"]',
|
||||
noOfworkspaceLabel: '[data-cy="number-of-workspaces-label"]',
|
||||
noOfWorkspacefield: '[data-cy="workspaces-field"]',
|
||||
usersTab: '[data-cy="users-sub-tab"]',
|
||||
noOfTotalUsersLabel: '[data-cy="number-of-total-users-label"]',
|
||||
noOfTotalUsersfield: '[data-cy="total-users-field"]',
|
||||
noOfBuildersLabel: '[data-cy="number-of-builders-label"]',
|
||||
noOfBuildersfield: '[data-cy="builders-field"]',
|
||||
noOfEndUsersLabel: '[data-cy="number-of-end-users-label"]',
|
||||
noOfEndUsersfield: '[data-cy="end-users-field"]',
|
||||
noOfSuperAdminLabel: '[data-cy="number-of-super-admins-label"]',
|
||||
noOfSuperAdminfield: '[data-cy="super-admins-field"]',
|
||||
tablesTab: '[data-cy="tables-sub-tab"]',
|
||||
noOfTablesLabel: '[data-cy="number-of-tables-label"]',
|
||||
noOfTablesfield: '[data-cy="tables-field"]',
|
||||
expiryStatus: '[data-cy="license-expiry-status"]',
|
||||
licenseTextArea: '[data-cy="license-text-area"]',
|
||||
paidFeatureButton: '[data-cy="paid-feature-button"]',
|
||||
accessTabTitle: '[data-cy="access-tab-title"]',
|
||||
enterpriseGradientIcon: '[data-cy="enterprise-gradient-icon"]',
|
||||
warningInfoText: '[data-cy="warning-info-text"]',
|
||||
lockGradientIcon: '[data-cy="lock-gradient"]',
|
||||
dsGradientIcon: '[data-cy="datasource-gradient"]',
|
||||
};
|
||||
|
|
@ -4,6 +4,7 @@ export const cyParamName = (paramName = "") => {
|
|||
export const groupsSelector = {
|
||||
pageTitle: "[data-cy=user-groups-title]",
|
||||
createNewGroupButton: "[data-cy=create-new-group-button]",
|
||||
createGroupButton: "[data-cy=create-group-button]",
|
||||
tableHeader: "[data-cy=table-header]",
|
||||
groupName: "[data-cy=group-name]",
|
||||
addNewGroupModalTitle: '[data-cy="add-new-group-title"]',
|
||||
|
|
@ -41,7 +42,7 @@ export const groupsSelector = {
|
|||
allAppsRadio: '[data-cy="all-apps-radio"]',
|
||||
allAppsLabel: '[data-cy="all-apps-label"]',
|
||||
allAppsHelperText: '[data-cy="this-will-select-all-apps-in-the-workspace-including-any-new-apps-created-info-text"]',
|
||||
customradio: '[data-cy="custom-radio"]',
|
||||
customRadio: '[data-cy="custom-radio"]',
|
||||
customLabel: '[data-cy="custom-label"]',
|
||||
customHelperText: '[data-cy="custom-info-text"]',
|
||||
resourcesFolders: "[data-cy=resource-folders]",
|
||||
|
|
@ -50,10 +51,29 @@ export const groupsSelector = {
|
|||
appsCreateLabel: "[data-cy=app-create-label]",
|
||||
appsDeleteCheck: "[data-cy=app-delete-checkbox]",
|
||||
appsDeleteLabel: "[data-cy=app-delete-label]",
|
||||
appPromoteCheck: '[data-cy="app-promote-checkbox"]',
|
||||
appPromoteLabel: '[data-cy="app-promote-label"]',
|
||||
appPromoteHelperText: '[data-cy="app-promote-helper-text"]',
|
||||
appReleaseCheck: '[data-cy="app-release-checkbox"]',
|
||||
appReleaseLabel: '[data-cy="app-release-label"]',
|
||||
appReleaseHelperText: '[data-cy="app-release-helper-text"]',
|
||||
workflowsCreateCheck: "[data-cy=workflow-create-checkbox]",
|
||||
workflowsCreateLabel: "[data-cy=workflow-create-label]",
|
||||
workflowsCreateHelperText: '[data-cy="workflow-create-helper-text"]',
|
||||
workflowsDeleteCheck: '[data-cy="workflow-delete-checkbox"]',
|
||||
workflowsDeleteLabel: '[data-cy="workflow-delete-label"]',
|
||||
workflowsDeleteHelperText: '[data-cy="workflow-delete-helper-text"]',
|
||||
datasourcesCreateCheck: "[data-cy=datasource-create-checkbox]",
|
||||
datasourcesCreateLabel: "[data-cy=datasource-create-label]",
|
||||
datasourcesCreateHelperText: '[data-cy="datasource-create-helper-text"]',
|
||||
datasourcesDeleteCheck: '[data-cy="datasource-delete-checkbox"]',
|
||||
datasourcesDeleteLabel: '[data-cy="datasource-delete-label"]',
|
||||
datasourcesDeleteHelperText: '[data-cy="datasource-delete-helper-text"]',
|
||||
foldersCreateCheck: "[data-cy=folder-create-checkbox]",
|
||||
foldersCreateLabel: "[data-cy=folder-create-label]",
|
||||
foldersHelperText: '[data-cy="folder-helper-text"]',
|
||||
workspaceVarCheckbox: '[data-cy="env-variable-checkbox"]',
|
||||
|
||||
appsText: '[data-cy="apps-text"]',
|
||||
appEditRadio: '[data-cy="app-edit-radio"]',
|
||||
appEditLabel: '[data-cy="app-edit-label"]',
|
||||
|
|
@ -64,9 +84,26 @@ export const groupsSelector = {
|
|||
appHideCheckbox: '[data-cy="app-hide-from-dashboard-radio"]',
|
||||
appHideHelperText: '[data-cy="app-hide-from-dashboard-helper-text"]',
|
||||
appHideLabel: '[data-cy="app-hide-from-dashboard-label"]',
|
||||
|
||||
workflowsText: '[data-cy="workflows-text"]',
|
||||
workflowsBuildRadio: '[data-cy="workflow-build-radio"]',
|
||||
workflowsBuildLabel: '[data-cy="workflow-build-label"]',
|
||||
workflowsBuildHelperText: '[data-cy="workflow-build-helper-text"]',
|
||||
workflowsExecuteRadio: '[data-cy="workflow-execute-radio"]',
|
||||
workflowsExecuteLabel: '[data-cy="workflow-execute-label"]',
|
||||
workflowsExecuteHelperText: '[data-cy="workflow-execute-helper-text"]',
|
||||
|
||||
datasourcesText: '[data-cy="datasources-text"]',
|
||||
datasourcesConfigureRadio: '[data-cy="datasource-configure-radio"]',
|
||||
datasourcesConfigureLabel: '[data-cy="datasource-configure-label"]',
|
||||
datasourcesConfigureHelperText: '[data-cy="datasource-configure-helper-text"]',
|
||||
datasourcesBuildWithRadio: '[data-cy="datasource-build-with-radio"]',
|
||||
datasourcesBuildWithLabel: '[data-cy="datasource-build-with-label"]',
|
||||
datasourcesBuildWithHelperText: '[data-cy="datasource-build-with-helper-text"]',
|
||||
|
||||
appHidePermissionModalLabel: '[data-cy="hide-from-dashboard-permission-label"]',
|
||||
appHidePermissionModalHelperText: '[data-cy="hide-from-dashboard-permission-info-text"]',
|
||||
addAppButton: '[data-cy="add-apps-buton"]',
|
||||
addAppsButton: '[data-cy="add-apps-buton"]',
|
||||
addEditPermissionModal: '[data-cy = "add-edit-permission-modal"]',
|
||||
addEditPermissionModalTitle: '[data-cy="modal-title"]',
|
||||
permissionNameLabel: '[data-cy="permission-name-label"]',
|
||||
|
|
@ -103,8 +140,8 @@ export const groupsSelector = {
|
|||
helperTextAdminPermissions: '[data-cy="helper-text-admin-permissions"]',
|
||||
updateGroupNameModalTitle: '[data-cy="update-group-title"]',
|
||||
editGranularPermissionIcon: '[data-cy="edit-permission-button"]',
|
||||
granularAccessPermission: '[data-cy="granular-access-permission"]',
|
||||
groupChip: '[data-cy="group-chip"]',
|
||||
granularAccessPermission: '[data-cy="apps-granular-access"]',
|
||||
groupChip: (accessType) => `[data-cy="${cyParamName(accessType)}-group-chip"]`,
|
||||
resourceContainer: '[data-cy="resources-container"]',
|
||||
groupLink: (groupname) => {
|
||||
return `[data-cy="${cyParamName(groupname)}-list-item"]`;
|
||||
|
|
@ -150,7 +187,13 @@ export const groupsSelector = {
|
|||
granularEmptyPageIcon: '[data-cy="empty-page-svg"]',
|
||||
emptyPagePermissionHelperText: '[data-cy="empty-page-info-text"]',
|
||||
groupNameUpdateLink: '[data-cy="group-name-update-link"]',
|
||||
|
||||
|
||||
addPermissionButton: '[data-cy="add-permission-button"]',
|
||||
addAppButton: '[data-cy="add-app-button"]',
|
||||
addWorkflowButton: '[data-cy="add-workflow-button"]',
|
||||
addDatasourceButton: '[data-cy="add-data_source-button"]',
|
||||
buildWorkflowradio: '[data-cy="build-permission-radio"]',
|
||||
executeWorkflowradio: '[data-cy="execute-permission-radio"]',
|
||||
configureDatasourceradio: '[data-cy="configure-permission-radio"]',
|
||||
buildWithDatasourceRadio: '[data-cy="build-with-datasource-radio"]',
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export const commonText = {
|
|||
createFolder: "Create folder",
|
||||
AddedToFolderToast: "Added to folder.",
|
||||
appCreatedToast: "App created successfully!",
|
||||
appRenamedToast: "App name has been updated!",
|
||||
appRemovedFromFolderMessage:
|
||||
"The app will be removed from this folder, do you want to continue?",
|
||||
appRemovedFromFolderTaost: "Removed from folder.",
|
||||
|
|
@ -135,6 +136,7 @@ export const commonText = {
|
|||
breadcrumbGlobalDatasourceTitle: "Global datasources",
|
||||
breadcrumbDatabaseTitle: "Databse",
|
||||
breadcrumbApplications: "Applications",
|
||||
breadcrumbHome: "Home",
|
||||
breadcrumbSettings: "Settings",
|
||||
addNewDataSourceButton: "Add new datasource",
|
||||
|
||||
|
|
@ -261,3 +263,24 @@ export const widgetValue = (widgetName) => {
|
|||
export const customValidation = (name, message) => {
|
||||
return ["{{", `components.${name}.value ? true : '${message}'}}`];
|
||||
};
|
||||
|
||||
export const settingsText = {
|
||||
settingsHeader: "Settings",
|
||||
allUsersListItem: "All Users",
|
||||
allWorkspacesListItem: "All workspaces",
|
||||
manageInstanceSettingsListItem: "Manage instance settings",
|
||||
whiteLabellingListItem: "White labelling",
|
||||
instanceLoginListItem: "Instance login",
|
||||
emailProtocolListItem: "Email protocol (SMTP)",
|
||||
licenseListItem: "License",
|
||||
};
|
||||
|
||||
export const workspaceSettingsText = {
|
||||
WorkspaceSettingsHeader: "Workspace settings",
|
||||
usersListItem: "Users",
|
||||
groupsListItem: "Groups",
|
||||
workspaceLoginListItem: "Workspace login",
|
||||
customStylesListItem: "Custom styles",
|
||||
configureGitSyncListItem: "Configure git sync",
|
||||
themesListItem: "Themes",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,4 +49,17 @@ export const dashboardText = {
|
|||
folderName: (folderName) => {
|
||||
return folderName;
|
||||
},
|
||||
homePageDividerText: "OR START WITH",
|
||||
homePagePromptHeader: "What do you want to build today?",
|
||||
appCardTitle: "Create a blank app",
|
||||
appCardDescription: "Build custom apps that make internal processes efficient",
|
||||
datasourceCardTitle: "Connect to a data source",
|
||||
datasourceCardDescription:
|
||||
"Link your tools to existing databases, spreadsheets, APIs, and more",
|
||||
workflowCardTitle: "Create a workflow",
|
||||
workflowCardDescription:
|
||||
"Automate repetitive tasks to streamline business process",
|
||||
exploreTemplateCardTitle: "Explore templates",
|
||||
exploreTemplateCardDescription:
|
||||
"Get started quickly with ready-to-deploy applications",
|
||||
};
|
||||
|
|
|
|||
68
cypress-tests/cypress/constants/texts/license.js
Normal file
68
cypress-tests/cypress/constants/texts/license.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
export const licenseText = {
|
||||
comparePlansText: "Compare plans",
|
||||
license: "License",
|
||||
licenseOverviewTitle: "License overview",
|
||||
licenseKeyLabel: "License Key",
|
||||
limitsTabTitle: "Limits",
|
||||
accessTabTitle: "Access",
|
||||
domainTabTitle: "Domain",
|
||||
updateButton: "Update",
|
||||
|
||||
licenseKeyTab: {
|
||||
licenseKeyTabTitle: "License Key",
|
||||
licenseLabel: "License",
|
||||
enterLicenseKeyPlaceholder: "Enter license key",
|
||||
},
|
||||
|
||||
limitsTab: {
|
||||
aiCreditsSubTab: "AI credits",
|
||||
appsSubTab: "Apps",
|
||||
workspacesSubTab: "Workspaces",
|
||||
usersSubTab: "Users",
|
||||
workflowsSubTab: "Workflows",
|
||||
tablesSubTab: "Tables",
|
||||
},
|
||||
|
||||
aiCreditsSubTab: {
|
||||
monthlyRecurringLabel: "Monthly recurring",
|
||||
addOnCreditsLabel: "Add on credits",
|
||||
},
|
||||
|
||||
appsSubTab: {
|
||||
noOfAppsLabel: "Number of Apps",
|
||||
},
|
||||
|
||||
workspacesSubTab: {
|
||||
noOfWorkspacesLabel: "Number of Workspaces",
|
||||
},
|
||||
|
||||
usersSubTab: {
|
||||
noOfTotalUsersLabel: "Number of Total Users",
|
||||
noOfBuildersLabel: "Number of Builders",
|
||||
noOfEndUsersLabel: "Number of End Users",
|
||||
noOfSuperAdminsLabel: "Number of Super Admins",
|
||||
},
|
||||
|
||||
workflowsSubTab: {
|
||||
noOfWorkflowsLabel: "Number of Workflows",
|
||||
},
|
||||
|
||||
tablesSubTab: {
|
||||
noOfTablesLabel: "Number of Tables",
|
||||
},
|
||||
|
||||
accessTab: {
|
||||
openIDConnectLabel: "Open ID Connect",
|
||||
apiKeyLabel: "Audit Logs",
|
||||
ldapLabel: "LDAP",
|
||||
samlLabel: "SAML",
|
||||
customStylesLabel: "Custom styles",
|
||||
multiEnvironmentLabel: "Multi-Environment",
|
||||
gitSyncLabel: "GitSync",
|
||||
},
|
||||
|
||||
domainTab: {
|
||||
noDomainLinkedLabel: "No Domain Linked",
|
||||
noDomainInfoText: "Please contact ToolJet team to link your domain",
|
||||
},
|
||||
};
|
||||
|
|
@ -85,12 +85,29 @@ export const groupsText = {
|
|||
warningText: "Users must be always be part of one default group. This will define the user count in your plan.",
|
||||
continueButtonText: "Continue",
|
||||
roleUpdateToastMessage: "Role updated successfully",
|
||||
endUserToBuilderMessage: "Changing the user role from end-user to builder will grant access the user access to all resources.Are you sure you want to continue?",
|
||||
endUserToAdminMessage: "Changing the user role from end-user to admin will grant the user access to all resources and settings.Are you sure you want to continue?",
|
||||
builderToEnduserMessage: "Changing the user role from builder to end-user will revoke their access to edit all resources.Are you sure you want to continue?",
|
||||
builderToAdminMessage: "Changing user role from builder to admin will grant access to all resources and settings.Are you sure you want to continue?",
|
||||
|
||||
endUserToBuilderMessage: Cypress.env('environment') === 'Community'
|
||||
? "Changing the user role from end-user to builder will grant access the user access to all resources.Are you sure you want to continue?"
|
||||
: "Changing user default group from end-user to builder will affect the count of users covered by your plan.Are you sure you want to continue?",
|
||||
|
||||
endUserToAdminMessage: Cypress.env('environment') === 'Community'
|
||||
? "Changing the user role from end-user to admin will grant the user access to all resources and settings.Are you sure you want to continue?"
|
||||
: "Changing user default group from end-user to admin will affect the count of users covered by your plan.Are you sure you want to continue?",
|
||||
|
||||
builderToEnduserMessage: Cypress.env('environment') === 'Community'
|
||||
? "Changing the user role from builder to end-user will revoke their access to edit all resources.Are you sure you want to continue?"
|
||||
: "Changing user default group from builder to end-user will affect the count of users covered by your plan.This will also remove the user from any custom groups with builder-like permissions.Are you sure you want to continue?",
|
||||
|
||||
builderToAdminMessage: Cypress.env('environment') === 'Community'
|
||||
? "Changing user role from builder to admin will grant access to all resources.Are you sure you want to continue?"
|
||||
: "Changing user role from builder to admin will grant access to all resources and settings.Are you sure you want to continue?",
|
||||
|
||||
adminToBuilderMessage: "Changing your user default group from admin to builder will revoke your access to settings.Are you sure you want to continue?",
|
||||
adminToEnduserMessage: "Changing the user role from admin to end-user will revoke their access to edit all resources and settings.Are you sure you want to continue?",
|
||||
|
||||
adminToEnduserMessage: Cypress.env('environment') === 'Community'
|
||||
? "Changing the user role from admin to end-user will revoke their access to edit all resources and settings.Are you sure you want to continue?"
|
||||
: "Changing your user group from admin to end-user will revoke your access to settings. This will also affect the count of users covered by your plan.Are you sure you want to continue?",
|
||||
|
||||
modalHeader: "Can not remove last active admin",
|
||||
modalMessage: "Cannot change role of last present admin, please add another admin and change the role",
|
||||
userAddedToast: "Users added to the group",
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ FROM information_schema.tables
|
|||
WHERE table_schema = 'public'
|
||||
AND table_type = 'BASE TABLE';`,
|
||||
postgresResponseNodeQuery: "return postgresql1.data",
|
||||
postgresExpectedValue: "employees",
|
||||
|
||||
postgresExpectedValue: "server_side_pagination",
|
||||
|
||||
restApiUrl: "http://9.234.17.31:8000/delay/10s",
|
||||
restApiResponseNodeQuery: "return restapi1.data",
|
||||
restApiExpectedValue: "<!DOCTYPE html>",
|
||||
|
|
@ -47,7 +47,7 @@ AND table_type = 'BASE TABLE';`,
|
|||
|
||||
runjsCodeForWebhooks: 'return "Verifying webhooks response"',
|
||||
runjsExpectedValueForWebhooks: "Verifying webhooks response",
|
||||
expectedStatusCodeText: 201,
|
||||
expectedStatusCodeText: 200,
|
||||
exportFixturePath: "cypress/fixtures/exportedApp.json",
|
||||
workflowLabel: "Workflow",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ describe("Chaining of queries", () => {
|
|||
cy.apiLogin();
|
||||
cy.apiCreateApp(`${fake.companyName}-chaining-App`);
|
||||
cy.openApp();
|
||||
cy.apiFetchDataSourcesId();
|
||||
cy.apiFetchDataSourcesIdFromApp();
|
||||
cy.viewport(1800, 1800);
|
||||
cy.dragAndDropWidget("Button");
|
||||
resizeQueryPanel("80");
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { importSelectors } from "Selectors/exportImport";
|
||||
import { commonText } from "Texts/common";
|
||||
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { importSelectors, exportAppModalSelectors } from "Selectors/exportImport";
|
||||
import { commonText } from "Texts/common";
|
||||
import { exportAppModalText } from "Texts/exportImport";
|
||||
|
||||
import {
|
||||
clickOnExportButtonAndVerify,
|
||||
exportAllVersionsAndVerify,
|
||||
verifyElementsOfExportModal,
|
||||
validateExportedAppStructure,
|
||||
} from "Support/utils/exportImport";
|
||||
import { selectAppCardOption, closeModal } from "Support/utils/common";
|
||||
import { selectAppCardOption, closeModal, deleteDownloadsFolder } from "Support/utils/common";
|
||||
|
||||
describe("App Export", () => {
|
||||
const TEST_DATA = {
|
||||
|
|
@ -19,35 +21,30 @@ describe("App Export", () => {
|
|||
},
|
||||
};
|
||||
|
||||
let data;
|
||||
|
||||
data = {
|
||||
const generateTestData = () => ({
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
appName: `${fake.companyName}-IE-App`,
|
||||
appReName: `${fake.companyName}-${fake.companyName}-IE-App`,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
});
|
||||
|
||||
let data;
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
appName: `${fake.companyName}-IE-App`,
|
||||
appReName: `${fake.companyName}-${fake.companyName}-IE-App`,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
data = generateTestData();
|
||||
|
||||
deleteDownloadsFolder();
|
||||
cy.exec("mkdir -p ./cypress/downloads/");
|
||||
cy.exec("cd ./cypress/downloads/ && rm -rf *");
|
||||
cy.exec("mkdir -p ./cypress/downloads/");
|
||||
cy.wait(3000);
|
||||
|
||||
cy.apiLogin();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.apiLogout();
|
||||
});
|
||||
|
||||
it("Verify the elements of export dialog box", () => {
|
||||
it("should verify elements of export dialog box", () => {
|
||||
cy.intercept("POST", "**/api/v2/resources/import").as("importApp");
|
||||
cy.intercept("POST", "**/api/v2/resources/export").as("exportApp");
|
||||
|
||||
cy.skipWalkthrough();
|
||||
|
||||
cy.apiLogin();
|
||||
|
|
@ -55,31 +52,25 @@ describe("App Export", () => {
|
|||
cy.get(importSelectors.importOptionInput)
|
||||
.eq(0)
|
||||
.selectFile(TEST_DATA.appFiles.multiVersion, { force: true });
|
||||
cy.wait(2000);
|
||||
cy.clearAndType(commonSelectors.appNameInput, data.appName);
|
||||
cy.get(importSelectors.importAppButton).click();
|
||||
cy.wait(3000);
|
||||
cy.wait("@importApp");
|
||||
cy.backToApps();
|
||||
cy.reload();
|
||||
|
||||
// Select the app card option to export the app
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
);
|
||||
|
||||
// Verify the elements of the export modal
|
||||
verifyElementsOfExportModal("v3", ["v2", "v1"], [true, false, false]);
|
||||
|
||||
// Close the modal
|
||||
closeModal(exportAppModalText.modalCloseButton);
|
||||
|
||||
// Ensure the modal title is no longer visible
|
||||
cy.get(
|
||||
commonSelectors.modalTitle(exportAppModalText.selectVersionTitle)
|
||||
).should("not.exist");
|
||||
|
||||
// Re-open the export modal and click the export button
|
||||
cy.wait(2000);
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
|
|
@ -90,95 +81,24 @@ describe("App Export", () => {
|
|||
const downloadedAppExportFileName = result.stdout.split("\n")[0];
|
||||
const filePath = `./cypress/downloads/${downloadedAppExportFileName}`;
|
||||
|
||||
// Ensure the file name contains the expected app export name
|
||||
expect(downloadedAppExportFileName).to.contain(
|
||||
data.appName.toLowerCase()
|
||||
);
|
||||
|
||||
// Read and validate the exported JSON file
|
||||
cy.readFile(filePath).then((appData) => {
|
||||
// Validate the app name
|
||||
const appNameFromFile = appData.app[0].definition.appV2.name;
|
||||
expect(appNameFromFile).to.equal(data.appName);
|
||||
|
||||
// Validate the schema for the student table in tooljetdb
|
||||
const tooljetDatabase = appData.tooljet_database.find(
|
||||
(db) => db.table_name === "student"
|
||||
);
|
||||
expect(tooljetDatabase).to.exist;
|
||||
expect(tooljetDatabase.schema).to.exist;
|
||||
|
||||
// Validate components and queries
|
||||
const components = appData.app[0].definition.appV2.components;
|
||||
|
||||
const text2Component = components.find(
|
||||
(component) => component.name === "text2"
|
||||
);
|
||||
expect(text2Component).to.exist;
|
||||
expect(text2Component.properties.text.value).to.equal(
|
||||
"{{constants.pageHeader}}"
|
||||
);
|
||||
|
||||
const textinput1 = components.find(
|
||||
(component) => component.name === "textinput1"
|
||||
);
|
||||
expect(textinput1).to.exist;
|
||||
expect(textinput1.properties.value.value).to.include("queries");
|
||||
|
||||
const textinput2 = components.find(
|
||||
(component) => component.name === "textinput2"
|
||||
);
|
||||
expect(textinput2).to.exist;
|
||||
expect(textinput2.properties.value.value).to.include("queries");
|
||||
|
||||
const textinput3 = components.find(
|
||||
(component) => component.name === "textinput3"
|
||||
);
|
||||
expect(textinput3).to.exist;
|
||||
expect(textinput3.properties.value.value).to.include("queries");
|
||||
|
||||
// Validate the data queries
|
||||
const dataQueries = appData.app[0].definition.appV2.dataQueries;
|
||||
|
||||
const postgresqlQuery = dataQueries.find(
|
||||
(query) => query.name === "postgresql1"
|
||||
);
|
||||
expect(postgresqlQuery).to.exist;
|
||||
expect(postgresqlQuery.options.query).to.include(
|
||||
"Select * from {{secrets.db_name}}"
|
||||
);
|
||||
|
||||
const restapiQuery = dataQueries.find(
|
||||
(query) => query.name === "restapi1"
|
||||
);
|
||||
expect(restapiQuery).to.exist;
|
||||
expect(restapiQuery.options.url).to.equal(
|
||||
"https://jsonplaceholder.typicode.com/users/1"
|
||||
);
|
||||
|
||||
const tooljetdbQuery = dataQueries.find(
|
||||
(query) => query.name === "tooljetdb1"
|
||||
);
|
||||
expect(tooljetdbQuery).to.exist;
|
||||
expect(tooljetdbQuery.options.operation).to.equal("list_rows");
|
||||
|
||||
// Ensure appVersions exists
|
||||
const appVersions = appData.app[0].definition.appV2.appVersions;
|
||||
expect(appVersions).to.exist;
|
||||
|
||||
// Map and verify app version names
|
||||
const versionNames = appVersions.map((version) => version.name);
|
||||
expect(versionNames).to.include.members(["v1", "v2", "v3"]);
|
||||
validateExportedAppStructure(appData, data.appName, {
|
||||
expectedVersions: ["v1", "v2", "v3"],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cy.exec("cd ./cypress/downloads/ && rm -rf *");
|
||||
deleteDownloadsFolder();
|
||||
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
);
|
||||
cy.get(`[data-cy="v1-radio-button"]`).check();
|
||||
cy.get(exportAppModalSelectors.versionRadioButton("v3")).check();
|
||||
cy.get(
|
||||
commonSelectors.buttonSelector(exportAppModalText.exportSelectedVersion)
|
||||
).click();
|
||||
|
|
@ -187,32 +107,32 @@ describe("App Export", () => {
|
|||
const downloadedAppExportFileName = result.stdout.split("\n")[0];
|
||||
const filePath = `./cypress/downloads/${downloadedAppExportFileName}`;
|
||||
|
||||
// Ensure the file name contains the expected app export name
|
||||
expect(downloadedAppExportFileName).to.contain(
|
||||
data.appName.toLowerCase()
|
||||
);
|
||||
|
||||
// Read and validate the exported JSON file
|
||||
cy.readFile(filePath).then((appData) => {
|
||||
// Validate the app name
|
||||
const appNameFromFile = appData.app[0].definition.appV2.name;
|
||||
expect(appNameFromFile).to.equal(data.appName);
|
||||
validateExportedAppStructure(appData, data.appName, {
|
||||
validateVersions: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.skip("Verify 'Export app' functionality of an application inside app editor", () => {
|
||||
it("should verify export app functionality inside app editor", () => {
|
||||
cy.intercept("POST", "**/api/v2/resources/export").as("exportApp");
|
||||
|
||||
data.appName2 = `${fake.companyName}-App`;
|
||||
cy.apiCreateApp(data.appName2);
|
||||
cy.openApp(data.appName2);
|
||||
|
||||
cy.dragAndDropWidget("Text Input", 50, 50);
|
||||
cy.dragAndDropWidget("Text Input", 200, 200);
|
||||
|
||||
cy.get('[data-cy="left-sidebar-settings-button"]').click();
|
||||
cy.get('[data-cy="button-user-status-change"]').click();
|
||||
cy.get(commonSelectors.leftSideBarSettingsButton).click();
|
||||
cy.get(commonSelectors.buttonSelector("Export app")).click();
|
||||
|
||||
verifyElementsOfExportModal("v1");
|
||||
|
||||
exportAllVersionsAndVerify(data.appName1, "v1");
|
||||
exportAllVersionsAndVerify(data.appName2, "v1");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -2,13 +2,16 @@ import { fake } from "Fixtures/fake";
|
|||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { appVersionSelectors, importSelectors } from "Selectors/exportImport";
|
||||
import { dashboardSelector } from "Selectors/dashboard";
|
||||
import { buttonText } from "Texts/button";
|
||||
|
||||
import { importText } from "Texts/exportImport";
|
||||
import { importAndVerifyApp } from "Support/utils/exportImport";
|
||||
import {
|
||||
importAndVerifyApp,
|
||||
verifyImportModalElements,
|
||||
setupDataSourceWithConstants,
|
||||
} from "Support/utils/exportImport";
|
||||
import { switchVersionAndVerify } from "Support/utils/version";
|
||||
import { renameApp } from 'Support/utils/editor/editorHeaderOperations';
|
||||
|
||||
describe("App Import Functionality", () => {
|
||||
describe("App Import", () => {
|
||||
const TEST_DATA = {
|
||||
toolJetImage: "cypress/fixtures/Image/tooljet.png",
|
||||
invalidApp: "cypress/fixtures/templates/invalid_app.json",
|
||||
|
|
@ -19,112 +22,94 @@ describe("App Import Functionality", () => {
|
|||
},
|
||||
};
|
||||
|
||||
const generateTestData = () => ({
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
appName: `${fake.companyName}-IE-App`,
|
||||
appReName: `${fake.companyName}-${fake.companyName}-IE-App`,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
});
|
||||
|
||||
const setupWorkspaceConstants = (dsEnv) => {
|
||||
cy.apiCreateWorkspaceConstant("pageHeader", "Import and Export", ["Global"], [dsEnv]);
|
||||
cy.apiCreateWorkspaceConstant("db_name", "persons", ["Secret"], [dsEnv]);
|
||||
};
|
||||
|
||||
const getDataSourceEnvironment = () => {
|
||||
const edition = Cypress.env("environment");
|
||||
return edition === "Community" ? "production" : "development";
|
||||
};
|
||||
|
||||
const verifyAppNameInEditor = (expectedName) => {
|
||||
cy.get('[data-cy="edit-app-name-button"]')
|
||||
.should("be.visible")
|
||||
.verifyVisibleElement("have.text", expectedName);
|
||||
};
|
||||
|
||||
const setupCommunityOrEnterpriseDataSource = () => {
|
||||
const edition = Cypress.env("environment");
|
||||
if (edition === "Community" || edition === "Enterprise") {
|
||||
const dsEnv = getDataSourceEnvironment();
|
||||
setupDataSourceWithConstants(dsEnv);
|
||||
setupWorkspaceConstants(dsEnv);
|
||||
return dsEnv;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
let data;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport(1400, 1400);
|
||||
data = {
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
appName: `${fake.companyName}-IE-App`,
|
||||
appReName: `${fake.companyName}-${fake.companyName}-IE-App`,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
data = generateTestData();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.apiLogout();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug).then((workspace) => {
|
||||
Cypress.env("workspaceId", workspace.body.organization_id);
|
||||
});
|
||||
cy.skipWalkthrough();
|
||||
});
|
||||
|
||||
it("should verify app import functionality", () => {
|
||||
cy.apiLogin();
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
|
||||
// Test invalid file import
|
||||
cy.get(dashboardSelector.importAppButton).click();
|
||||
importAndVerifyApp(
|
||||
TEST_DATA.toolJetImage,
|
||||
importText.couldNotImportAppToastMessage
|
||||
);
|
||||
|
||||
cy.wait(500);
|
||||
cy.get(dashboardSelector.importAppButton).click();
|
||||
importAndVerifyApp(
|
||||
TEST_DATA.invalidApp,
|
||||
"Could not import: SyntaxError: Expected ',' or '}' after property value in JSON at position 246 (line 11 column 13)"
|
||||
);
|
||||
|
||||
cy.wait(500);
|
||||
|
||||
// Test valid app import
|
||||
});
|
||||
it("should verify invalid import files", () => {
|
||||
cy.get(importSelectors.dropDownMenu).should("be.visible").click();
|
||||
cy.get(importSelectors.importOptionLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
importText.importOption
|
||||
);
|
||||
|
||||
cy.get(dashboardSelector.importAppButton).click();
|
||||
importAndVerifyApp(
|
||||
TEST_DATA.toolJetImage,
|
||||
importText.couldNotImportAppToastMessage
|
||||
);
|
||||
|
||||
cy.get(dashboardSelector.importAppButton).should("be.visible").click();
|
||||
importAndVerifyApp(
|
||||
TEST_DATA.invalidApp,
|
||||
"Could not import: SyntaxError: Expected ',' or '}' after property value in JSON at position 246 (line 11 column 13)"
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify app with multiple version", () => {
|
||||
cy.intercept("POST", "/api/v2/resources/import").as("importApp");
|
||||
|
||||
cy.get(importSelectors.importOptionInput)
|
||||
.eq(0)
|
||||
.selectFile(TEST_DATA.appFiles.multiVersion, {
|
||||
force: true,
|
||||
});
|
||||
cy.wait(1500);
|
||||
.selectFile(TEST_DATA.appFiles.multiVersion, { force: true });
|
||||
|
||||
cy.get(importSelectors.importAppTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Import app"
|
||||
);
|
||||
cy.get(commonSelectors.appNameLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
"App name"
|
||||
);
|
||||
cy.get(commonSelectors.appNameInput)
|
||||
.should("be.visible")
|
||||
.and("have.value", "three-versions");
|
||||
cy.get(commonSelectors.appNameInfoLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
"App name must be unique and max 50 characters"
|
||||
);
|
||||
cy.get(commonSelectors.cancelButton)
|
||||
.should("be.visible")
|
||||
.and("have.text", "Cancel");
|
||||
cy.get(commonSelectors.importAppButton).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Import app"
|
||||
);
|
||||
verifyImportModalElements("three-versions");
|
||||
|
||||
cy.get(importSelectors.importAppButton).click();
|
||||
cy.get(".go3958317564")
|
||||
cy.get(commonSelectors.toastMessage)
|
||||
.should("be.visible")
|
||||
.and("have.text", importText.appImportedToastMessage);
|
||||
|
||||
// Verify imported app
|
||||
cy.get(commonSelectors.toastCloseButton).click();
|
||||
cy.wait(500);
|
||||
cy.get(commonSelectors.appNameInput).verifyVisibleElement(
|
||||
"contain.value",
|
||||
"three-versions"
|
||||
);
|
||||
verifyAppNameInEditor("three-versions");
|
||||
cy.get(appVersionSelectors.currentVersionField("v3")).should("be.visible");
|
||||
|
||||
// Configure app
|
||||
cy.skipEditorPopover();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText);
|
||||
cy.get(appVersionSelectors.appVersionLabel).should("be.visible");
|
||||
cy.get(commonWidgetSelector.draggableWidget("button1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
|
||||
cy.renameApp(data.appName);
|
||||
cy.get(commonSelectors.appNameInput).verifyVisibleElement(
|
||||
"contain.value",
|
||||
data.appName
|
||||
);
|
||||
cy.waitForAutoSave();
|
||||
|
||||
// Verify initial widget states
|
||||
renameApp(data.appName);
|
||||
verifyAppNameInEditor(data.appName);
|
||||
|
||||
verifyCommonData({
|
||||
text2: "",
|
||||
|
|
@ -132,72 +117,16 @@ describe("App Import Functionality", () => {
|
|||
textInput2: "Leanne Graham",
|
||||
});
|
||||
|
||||
// cy.get(
|
||||
// commonWidgetSelector.draggableWidget("textInput3")
|
||||
// ).verifyVisibleElement("have.value", "");
|
||||
|
||||
// Setup database and data sources
|
||||
cy.visit(`${data.workspaceSlug}/database`);
|
||||
cy.get('[data-cy="student-table"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"student"
|
||||
);
|
||||
|
||||
// cy.apiAddDataToTable("student", {
|
||||
// name: "Paramu",
|
||||
// country: "India",
|
||||
// state: "Kerala",
|
||||
// });
|
||||
cy.get('[data-cy="student-table"]').verifyVisibleElement("have.text", "student");
|
||||
|
||||
cy.visit(`${data.workspaceSlug}/data-sources`);
|
||||
cy.get('[data-cy="postgresql-button"]').should("be.visible");
|
||||
setupCommunityOrEnterpriseDataSource();
|
||||
|
||||
cy.ifEnv("Community", () => {
|
||||
cy.apiUpdateDataSource("postgresql", "production", {
|
||||
options: [
|
||||
{
|
||||
key: "password",
|
||||
value: `${Cypress.env("pg_password")}`,
|
||||
encrypted: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.apiUpdateDataSource("postgresql", "development", {
|
||||
options: [
|
||||
{
|
||||
key: "password",
|
||||
value: `${Cypress.env("pg_password")}`,
|
||||
encrypted: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
cy.ifEnv("Community", () => {
|
||||
cy.apiCreateWorkspaceConstant(
|
||||
"pageHeader",
|
||||
"Import and Export",
|
||||
["Global"],
|
||||
["production"]
|
||||
);
|
||||
cy.apiCreateWorkspaceConstant("db_name", "persons", ["Secret"], ["production"]);
|
||||
});
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.apiCreateWorkspaceConstant(
|
||||
"pageHeader",
|
||||
"Import and Export",
|
||||
["Global"],
|
||||
["development"]
|
||||
);
|
||||
cy.apiCreateWorkspaceConstant("db_name", "persons", ["Secret"], ["development"]);
|
||||
});
|
||||
|
||||
// Verify app after setup
|
||||
cy.wait("@importApp").then((interception) => {
|
||||
const appId = interception.response.body.imports.app[0].id;
|
||||
cy.log(`Imported app id: ${appId}`);
|
||||
cy.openApp(
|
||||
"",
|
||||
Cypress.env("workspaceId"),
|
||||
|
|
@ -211,31 +140,28 @@ describe("App Import Functionality", () => {
|
|||
textInput1: "John",
|
||||
textInput2: "Leanne Graham",
|
||||
});
|
||||
// cy.get(
|
||||
// commonWidgetSelector.draggableWidget("textInput3")
|
||||
// ).verifyVisibleElement("have.value", "India");
|
||||
|
||||
switchVersionAndVerify("v3", "v1");
|
||||
|
||||
verifyCommonData({
|
||||
text2: "Import and Export",
|
||||
textInput1: "John",
|
||||
textInput2: "Leanne Graham",
|
||||
});
|
||||
});
|
||||
|
||||
cy.wait(1000);
|
||||
cy.backToApps();
|
||||
|
||||
// Test single version import
|
||||
it("should verify app with single version", () => {
|
||||
cy.get(importSelectors.dropDownMenu).click();
|
||||
const dsEnv = setupCommunityOrEnterpriseDataSource();
|
||||
|
||||
importAndVerifyApp(TEST_DATA.appFiles.singleVersion);
|
||||
|
||||
// Verify final state
|
||||
cy.get(commonSelectors.appNameInput).verifyVisibleElement(
|
||||
"contain.value",
|
||||
"one_version"
|
||||
);
|
||||
verifyAppNameInEditor("one_version");
|
||||
|
||||
if (dsEnv) {
|
||||
setupDataSourceWithConstants(dsEnv);
|
||||
}
|
||||
|
||||
cy.reload();
|
||||
verifyCommonData({
|
||||
text2: "Import and Export",
|
||||
textInput1: "John",
|
||||
|
|
|
|||
|
|
@ -1,53 +1,40 @@
|
|||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { fake } from "Fixtures/fake";
|
||||
import { releaseApp } from "Support/utils/common";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import {
|
||||
resolveHost,
|
||||
verifySlugValidations,
|
||||
verifySuccessfulSlugUpdate,
|
||||
verifyURLs,
|
||||
resolveHost,
|
||||
} from "Support/utils/apps";
|
||||
import { appPromote } from "Support/utils/platform/multiEnv";
|
||||
import { releaseApp } from "Support/utils/common";
|
||||
|
||||
describe("App Slug", () => {
|
||||
const data = {};
|
||||
const host = resolveHost();
|
||||
|
||||
beforeEach(() => {
|
||||
data.slug = `${fake.companyName.toLowerCase()}-app`;
|
||||
data.appName = `${fake.companyName} App`;
|
||||
cy.defaultWorkspaceLogin();
|
||||
const generateTestData = () => ({
|
||||
slug: `${fake.companyName.toLowerCase()}-app`,
|
||||
appName: `${fake.companyName} App`,
|
||||
});
|
||||
|
||||
before(() => {
|
||||
data.appName = `${fake.companyName} App`;
|
||||
const host = resolveHost();
|
||||
let data;
|
||||
|
||||
beforeEach(() => {
|
||||
data = generateTestData();
|
||||
cy.apiLogin();
|
||||
cy.skipWalkthrough();
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.wait(1000);
|
||||
cy.apiLogout();
|
||||
cy.openApp()
|
||||
});
|
||||
|
||||
it("Verify app slug cases in global settings", () => {
|
||||
const workspaceId = Cypress.env("workspaceId");
|
||||
const appId = Cypress.env("appId");
|
||||
const appUrl = `${host}/${Cypress.env("workspaceId")}/apps/${Cypress.env("appId")}/`;
|
||||
|
||||
cy.apiLogin();
|
||||
cy.skipWalkthrough();
|
||||
|
||||
cy.visit(appUrl);
|
||||
cy.url().then((url) => {
|
||||
if (url !== appUrl) {
|
||||
cy.visit(appUrl);
|
||||
}
|
||||
});
|
||||
cy.url().should("eq", appUrl);
|
||||
|
||||
cy.wait(1000);
|
||||
cy.url().should("eq", `${host}/${Cypress.env("workspaceId")}/apps/${Cypress.env("appId")}/`);
|
||||
|
||||
cy.get('[data-cy="query-manager-toggle-button"]', { timeout: 20000 }).should("be.visible").click();
|
||||
|
||||
cy.get(commonSelectors.leftSideBarSettingsButton).click();
|
||||
|
||||
// Verify initial state
|
||||
cy.get(commonWidgetSelector.appSlugLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Unique app slug"
|
||||
|
|
@ -68,26 +55,21 @@ describe("App Slug", () => {
|
|||
|
||||
cy.get(commonWidgetSelector.appLinkField).verifyVisibleElement(
|
||||
"have.text",
|
||||
`${host}/${workspaceId}/apps/${appId}`
|
||||
`${host}/${Cypress.env("workspaceId")}/apps/${Cypress.env("appId")}`
|
||||
);
|
||||
|
||||
// Validate all error cases
|
||||
verifySlugValidations(commonWidgetSelector.appSlugInput);
|
||||
|
||||
// Verify successful slug update
|
||||
cy.clearAndType(commonWidgetSelector.appSlugInput, data.slug);
|
||||
verifySuccessfulSlugUpdate(workspaceId, data.slug);
|
||||
verifySuccessfulSlugUpdate(Cypress.env("workspaceId"), data.slug);
|
||||
|
||||
// Verify persistence
|
||||
cy.get('[data-cy="left-sidebar-debugger-button"]').click();
|
||||
cy.get(commonSelectors.leftSideBarSettingsButton).click();
|
||||
cy.get(commonWidgetSelector.appSlugInput).should("have.value", data.slug);
|
||||
|
||||
// Release and verify URLs
|
||||
releaseApp();
|
||||
verifyURLs(workspaceId, data.slug, true);
|
||||
verifyURLs(Cypress.env("workspaceId"), data.slug, true);
|
||||
|
||||
// Verify duplicate slug validation
|
||||
cy.visit("/my-workspace");
|
||||
cy.apiCreateApp(data.slug);
|
||||
cy.openApp("my-workspace");
|
||||
|
|
@ -101,20 +83,14 @@ describe("App Slug", () => {
|
|||
});
|
||||
|
||||
it("Verify app slug cases in share modal", () => {
|
||||
cy.apiLogin();
|
||||
const workspaceId = Cypress.env("workspaceId");
|
||||
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.openApp("my-workspace");
|
||||
|
||||
// Set up initial slug
|
||||
cy.get(commonSelectors.leftSideBarSettingsButton).click();
|
||||
cy.get(commonWidgetSelector.appSlugInput).clear();
|
||||
cy.clearAndType(commonWidgetSelector.appSlugInput, data.slug);
|
||||
|
||||
releaseApp();
|
||||
|
||||
// Verify share modal
|
||||
cy.get(commonWidgetSelector.shareAppButton).click();
|
||||
cy.get(commonWidgetSelector.appLink).verifyVisibleElement(
|
||||
"have.text",
|
||||
|
|
@ -125,12 +101,11 @@ describe("App Slug", () => {
|
|||
data.slug
|
||||
);
|
||||
|
||||
// Validate all error cases in share modal
|
||||
verifySlugValidations(commonWidgetSelector.appNameSlugInput);
|
||||
|
||||
cy.wait(500);
|
||||
cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug);
|
||||
cy.get('[data-cy="app-slug-info-label"]')
|
||||
.should("be.visible")
|
||||
.invoke("text")
|
||||
.then((text) => {
|
||||
expect(text.trim()).to.eq(
|
||||
|
|
@ -138,26 +113,23 @@ describe("App Slug", () => {
|
|||
);
|
||||
});
|
||||
|
||||
// Verify successful slug update in share modal
|
||||
data.slug = `${fake.companyName.toLowerCase()}-app`;
|
||||
cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug);
|
||||
const newSlug = `${fake.companyName.toLowerCase()}-app`;
|
||||
cy.clearAndType(commonWidgetSelector.appNameSlugInput, newSlug);
|
||||
cy.get('[data-cy="app-slug-accepted-label"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"Slug accepted!"
|
||||
);
|
||||
|
||||
// Close modal and verify URLs
|
||||
cy.get(commonWidgetSelector.modalCloseButton).click();
|
||||
verifyURLs(workspaceId, data.slug, true);
|
||||
verifyURLs(Cypress.env("workspaceId"), newSlug, true);
|
||||
|
||||
// Verify duplicate slug validation in share modal
|
||||
cy.visit("/my-workspace");
|
||||
cy.apiCreateApp(data.slug);
|
||||
cy.apiCreateApp(newSlug);
|
||||
cy.openApp("my-workspace");
|
||||
|
||||
releaseApp();
|
||||
cy.get(commonWidgetSelector.shareAppButton).click();
|
||||
cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug);
|
||||
cy.clearAndType(commonWidgetSelector.appNameSlugInput, newSlug);
|
||||
cy.get(commonWidgetSelector.appSlugErrorLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
"This app slug is already taken."
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
verifyElementsOfCreateNewVersionModal,
|
||||
navigateToEditVersionModal,
|
||||
switchVersionAndVerify,
|
||||
openPreviewSettings,
|
||||
} from "Support/utils/version";
|
||||
import { appVersionSelectors } from "Selectors/exportImport";
|
||||
import { editVersionSelectors } from "Selectors/version";
|
||||
|
|
@ -27,48 +28,48 @@ import { createRestAPIQuery } from "Support/utils/dataSource";
|
|||
import { deleteQuery } from "Support/utils/queries";
|
||||
import { selectEnv, appPromote } from "Support/utils/platform/multiEnv";
|
||||
describe("App Version", () => {
|
||||
const generateTestData = () => ({
|
||||
appName: `${fake.companyName}-Version-App`,
|
||||
datasourceName: fake.firstName.toLowerCase(),
|
||||
query1: fake.firstName.toLowerCase(),
|
||||
query2: fake.firstName.toLowerCase(),
|
||||
});
|
||||
|
||||
const verifyWidget = (selector, assertion, expectedValue) => {
|
||||
cy.get(commonWidgetSelector.draggableWidget(selector))
|
||||
.verifyVisibleElement(assertion, expectedValue);
|
||||
};
|
||||
|
||||
let data;
|
||||
|
||||
let currentVersion = "";
|
||||
let newVersion = [];
|
||||
let versionFrom = "";
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
appName: `${fake.companyName}-Version-App`,
|
||||
datasourceName: fake.firstName.toLowerCase(),
|
||||
query1: fake.firstName.toLowerCase(),
|
||||
query2: fake.firstName.toLowerCase(),
|
||||
};
|
||||
data = generateTestData();
|
||||
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.openApp();
|
||||
cy.viewport(1400, 1400);
|
||||
|
||||
});
|
||||
|
||||
it("should verify basic version management operations", () => {
|
||||
// Version modal verification
|
||||
cy.get(appVersionSelectors.appVersionLabel).should("be.visible");
|
||||
navigateToCreateNewVersionModal("v1");
|
||||
verifyElementsOfCreateNewVersionModal(["v1"]);
|
||||
|
||||
// Empty version name validation
|
||||
navigateToCreateNewVersionModal("v1");
|
||||
cy.get('[data-cy="create-new-version-button"]').click();
|
||||
cy.get(appVersionSelectors.createNewVersionButton).click();
|
||||
cy.get(appVersionSelectors.createNewVersionButton).click();
|
||||
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
"Version name should not be empty"
|
||||
);
|
||||
|
||||
cy.wait(2000);
|
||||
cy.get(appVersionSelectors.createVersionTitle).should("not.exist");
|
||||
|
||||
// Duplicate version name check
|
||||
verifyDuplicateVersion(["v1"], "v1");
|
||||
closeModal(commonText.closeButton);
|
||||
|
||||
// Version edit modal verification
|
||||
navigateToEditVersionModal("v1");
|
||||
verifyModal(
|
||||
editVersionText.editVersionTitle,
|
||||
|
|
@ -76,45 +77,39 @@ describe("App Version", () => {
|
|||
editVersionSelectors.versionNameInputField
|
||||
);
|
||||
closeModal(commonText.closeButton);
|
||||
cy.wait(1000);
|
||||
cy.get(editVersionSelectors.editVersionTitle).should("not.exist");
|
||||
|
||||
// Version editing
|
||||
editVersionAndVerify(
|
||||
"v1",
|
||||
["v2"],
|
||||
editVersionText.VersionNameUpdatedToastMessage
|
||||
);
|
||||
|
||||
// Component operations in version
|
||||
verifyComponentinrightpannel("table");
|
||||
cy.get(commonSelectors.rightSidebarPlusButton).click();
|
||||
cy.dragAndDropWidget("text");
|
||||
cy.waitForAutoSave();
|
||||
|
||||
// New version creation
|
||||
navigateToCreateNewVersionModal("v2");
|
||||
createNewVersion(["v3"], "v2");
|
||||
cy.waitForAutoSave();
|
||||
verifyComponentinrightpannel("table");
|
||||
|
||||
// Component deletion
|
||||
deleteComponentAndVerify("text1");
|
||||
cy.waitForAutoSave();
|
||||
cy.wait(2000);
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should("not.exist");
|
||||
|
||||
// Version deletion
|
||||
deleteVersionAndVerify(
|
||||
"v3",
|
||||
onlydeleteVersionText.deleteToastMessage("v3")
|
||||
);
|
||||
cy.get(appVersionSelectors.currentVersionField("v2")).should("be.visible");
|
||||
cy.wait(3000);
|
||||
cy.get(appVersionSelectors.currentVersionField("v3")).should("not.exist");
|
||||
|
||||
// cy.reload();
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible", {
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// Preview and release verification
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
||||
cy.ifEnv("Community", () => {
|
||||
|
|
@ -135,7 +130,6 @@ describe("App Version", () => {
|
|||
});
|
||||
|
||||
it("should verify version management with components and queries", () => {
|
||||
// Initial setup with component and datasource
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
data.datasourceName,
|
||||
|
|
@ -157,20 +151,15 @@ describe("App Version", () => {
|
|||
appPromote("development", "production");
|
||||
});
|
||||
|
||||
// Version v2 creation and verification and v2 is created from v1 production environment
|
||||
navigateToCreateNewVersionModal("v1");
|
||||
createNewVersion(["v2"], "v1");
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Leanne Graham"
|
||||
);
|
||||
verifyWidget("text1", "have.text", "Leanne Graham");
|
||||
cy.get(`[data-cy="list-query-${data.query1}"]`).should("be.visible");
|
||||
|
||||
// Modify v2 with new components and queries
|
||||
deleteComponentAndVerify("text1");
|
||||
cy.waitForAutoSave();
|
||||
deleteQuery(data.query1);
|
||||
cy.get('[data-cy="modal-confirm-button"]').click();
|
||||
cy.get(commonSelectors.modalConfirmButton).click();
|
||||
createRestAPIQuery(data.query2, data.datasourceName, "", "", "/2", true);
|
||||
cy.apiAddComponentToApp(
|
||||
data.appName,
|
||||
|
|
@ -181,7 +170,6 @@ describe("App Version", () => {
|
|||
);
|
||||
cy.waitForAutoSave();
|
||||
|
||||
// Version creation and state verification
|
||||
const versionChecks = [
|
||||
{
|
||||
create: { version: "v3", from: "v2" },
|
||||
|
|
@ -210,29 +198,21 @@ describe("App Version", () => {
|
|||
navigateToCreateNewVersionModal(check.create.from);
|
||||
createNewVersion([check.create.version], check.create.from);
|
||||
cy.waitForAutoSave();
|
||||
cy.wait(1000);
|
||||
if (check.verify.component.value) {
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(check.verify.component.selector)
|
||||
).verifyVisibleElement("have.value", check.verify.component.value);
|
||||
} else {
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(check.verify.component.selector)
|
||||
).verifyVisibleElement("have.text", check.verify.component.text);
|
||||
}
|
||||
cy.get(`[data-cy="list-query-${check.verify.query}"]`).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.get(appVersionSelectors.currentVersionField(check.create.version)).should("be.visible");
|
||||
|
||||
const assertion = check.verify.component.value ? "have.value" : "have.text";
|
||||
const expected = check.verify.component.value || check.verify.component.text;
|
||||
verifyWidget(check.verify.component.selector, assertion, expected);
|
||||
|
||||
cy.get(`[data-cy="list-query-${check.verify.query}"]`).should("be.visible");
|
||||
});
|
||||
|
||||
// Release and version state verification
|
||||
releasedVersionAndVerify("v5");
|
||||
cy.get(appVersionSelectors.currentVersionField("v5")).should(
|
||||
"have.class",
|
||||
"color-light-green"
|
||||
);
|
||||
|
||||
// Version switching and component verification
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
selectEnv("development");
|
||||
});
|
||||
|
|
@ -242,13 +222,9 @@ describe("App Version", () => {
|
|||
"not.have.class",
|
||||
"color-light-green"
|
||||
);
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Leanne Graham"
|
||||
);
|
||||
verifyWidget("text1", "have.text", "Leanne Graham");
|
||||
cy.get(`[data-cy="list-query-${data.query1}"]`).should("be.visible");
|
||||
|
||||
// Preview and version switching verification
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
||||
cy.ifEnv("Community", () => {
|
||||
|
|
@ -258,17 +234,12 @@ describe("App Version", () => {
|
|||
cy.url().should("include", "/home?env=development&version=v4");
|
||||
});
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Leanne Graham"
|
||||
);
|
||||
verifyWidget("text1", "have.text", "Leanne Graham");
|
||||
|
||||
cy.get('[data-cy="preview-settings"]').click();
|
||||
openPreviewSettings();
|
||||
switchVersionAndVerify("v4", "v5");
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textInput")
|
||||
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||
verifyWidget("textInput", "have.value", "Ervin Howell");
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.openApp(
|
||||
|
|
@ -281,62 +252,43 @@ describe("App Version", () => {
|
|||
navigateToCreateNewVersionModal("v5");
|
||||
createNewVersion(["v6"], "v5");
|
||||
cy.waitForAutoSave();
|
||||
cy.wait(1000);
|
||||
cy.get(appVersionSelectors.currentVersionField("v6")).should("be.visible");
|
||||
|
||||
appPromote("development", "staging");
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textInput")
|
||||
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||
verifyWidget("textInput", "have.value", "Ervin Howell");
|
||||
cy.get(`[data-cy="list-query-${data.query2}"]`).should("be.visible");
|
||||
|
||||
appPromote("staging", "production");
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textInput")
|
||||
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||
verifyWidget("textInput", "have.value", "Ervin Howell");
|
||||
cy.get(`[data-cy="list-query-${data.query2}"]`).should("be.visible");
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textInput")
|
||||
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||
verifyWidget("textInput", "have.value", "Ervin Howell");
|
||||
cy.url().should("include", "/home?env=production&version=v6");
|
||||
|
||||
cy.wait(1000);
|
||||
|
||||
cy.get('[data-cy="preview-settings"]').click();
|
||||
openPreviewSettings();
|
||||
switchVersionAndVerify("v6", "v1");
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("text1")
|
||||
).verifyVisibleElement("have.text", "Leanne Graham");
|
||||
// url bug
|
||||
// cy.url().should("include", "/home?env=production&version=v1");
|
||||
cy.wait(1000);
|
||||
cy.get('[data-cy="preview-settings"]').click();
|
||||
verifyWidget("text1", "have.text", "Leanne Graham");
|
||||
|
||||
openPreviewSettings();
|
||||
switchVersionAndVerify("v1", "v6");
|
||||
|
||||
cy.wait(1000);
|
||||
cy.get('[data-cy="preview-settings"]').click();
|
||||
openPreviewSettings();
|
||||
cy.forceClickOnCanvas();
|
||||
openPreviewSettings();
|
||||
selectEnv("staging");
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textInput")
|
||||
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||
// cy.url().should("include", "/home?env=staging&version=v6");
|
||||
verifyWidget("textInput", "have.value", "Ervin Howell");
|
||||
|
||||
|
||||
cy.wait(1000);
|
||||
cy.get('[data-cy="preview-settings"]').click();
|
||||
openPreviewSettings();
|
||||
selectEnv("development");
|
||||
|
||||
cy.wait(1000);
|
||||
cy.get('[data-cy="preview-settings"]').click();
|
||||
openPreviewSettings();
|
||||
switchVersionAndVerify("v6", "v1");
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("text1")
|
||||
).verifyVisibleElement("have.text", "Leanne Graham");
|
||||
verifyWidget("text1", "have.text", "Leanne Graham");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { fake } from "Fixtures/fake";
|
||||
import { logout, releaseApp } from "Support/utils/common";
|
||||
import { inviteUserToWorkspace } from "Support/utils/manageUsers";
|
||||
import { inviteUserToWorkspace, fetchAndVisitInviteLinkViaMH } from "Support/utils/manageUsers";
|
||||
import { setSignupStatus } from "Support/utils/manageSSO";
|
||||
import { onboardingSelectors } from "Selectors/onboarding";
|
||||
import { commonText } from "Texts/common";
|
||||
import { userSignUp, addNewUser } from "Support/utils/onboarding";
|
||||
import { userSignUp } from "Support/utils/onboarding";
|
||||
import {
|
||||
setUpSlug,
|
||||
setupAppWithSlug,
|
||||
|
|
@ -21,35 +21,46 @@ describe(
|
|||
retries: { runMode: 2 },
|
||||
},
|
||||
() => {
|
||||
const generateTestData = () => ({
|
||||
appName: `${fake.companyName} P P App`,
|
||||
slug: `${fake.companyName} P P App`.toLowerCase().replace(/\s+/g, "-"),
|
||||
firstName: fake.firstName,
|
||||
email: fake.email.toLowerCase(),
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
appPublicSlug: `${fake.companyName} Public App`
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, "-"),
|
||||
appPublicName: `${fake.companyName} Public App`,
|
||||
appPrivateSlug: `${fake.companyName} Private App`
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, "-"),
|
||||
appPrivateName: `${fake.companyName} Private App`,
|
||||
});
|
||||
|
||||
const verifyWidget = (widgetName) => {
|
||||
cy.get(commonWidgetSelector.draggableWidget(widgetName)).should("be.visible");
|
||||
};
|
||||
|
||||
const getAppUrl = (slug) => `${Cypress.config("baseUrl")}/applications/${slug}`;
|
||||
|
||||
let data;
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
appName: `${fake.companyName} P P App`,
|
||||
slug: `${fake.companyName} P P App`.toLowerCase().replace(/\s+/g, "-"),
|
||||
firstName: fake.firstName,
|
||||
email: fake.email.toLowerCase(),
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
};
|
||||
|
||||
data = generateTestData();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.skipWalkthrough();
|
||||
});
|
||||
|
||||
it("Verify private and public app share functionality", () => {
|
||||
it("should verify private and public app share functionality", () => {
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.openApp();
|
||||
cy.apiAddComponentToApp(data.appName, "text1");
|
||||
cy.dragAndDropWidget("text", 500, 500);
|
||||
|
||||
// Check unreleased version state
|
||||
cy.get('[data-cy="share-button-link"]>span').should("be.visible").click();
|
||||
cy.contains("This version has not been released yet").should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.get(commonWidgetSelector.shareAppButton).should("be.visible").click();
|
||||
cy.contains("This version has not been released yet").should("be.visible");
|
||||
cy.get(commonWidgetSelector.modalCloseButton).click();
|
||||
|
||||
// Release and verify share modal
|
||||
releaseApp();
|
||||
cy.get(commonWidgetSelector.shareAppButton).click();
|
||||
for (const elements in commonWidgetSelector.shareModalElements) {
|
||||
|
|
@ -61,7 +72,6 @@ describe(
|
|||
);
|
||||
}
|
||||
|
||||
// Verify share modal elements
|
||||
const shareModalSelectors = [
|
||||
"copyAppLinkButton",
|
||||
"makePublicAppToggle",
|
||||
|
|
@ -73,9 +83,8 @@ describe(
|
|||
cy.get(commonWidgetSelector[selector]).should("be.visible");
|
||||
});
|
||||
|
||||
// Configure and verify slug
|
||||
cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug);
|
||||
cy.get('[data-cy="app-slug-accepted-label"]')
|
||||
cy.get(commonSelectors.appSlugAccept)
|
||||
.should("be.visible")
|
||||
.and("have.text", "Slug accepted!");
|
||||
|
||||
|
|
@ -83,24 +92,14 @@ describe(
|
|||
cy.forceClickOnCanvas();
|
||||
cy.backToApps();
|
||||
|
||||
// Test private access
|
||||
logout();
|
||||
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) });
|
||||
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.wait(2000);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
cy.appUILogin();
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
verifyWidget("text1");
|
||||
|
||||
// Test public access
|
||||
// cy.get(commonSelectors.viewerPageLogo).click();
|
||||
cy.openApp(
|
||||
"appSlug",
|
||||
Cypress.env("workspaceId"),
|
||||
|
|
@ -113,75 +112,39 @@ describe(
|
|||
cy.backToApps();
|
||||
|
||||
logout();
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) });
|
||||
verifyWidget("text1");
|
||||
});
|
||||
|
||||
it("Verify app private and public app visibility for the same workspace user", () => {
|
||||
it("should verify app private and public app visibility for the same workspace user", () => {
|
||||
setupAppWithSlug(data.appName, data.slug);
|
||||
|
||||
inviteUserToWorkspace(data.firstName, data.email);
|
||||
logout();
|
||||
cy.visit("/");
|
||||
cy.wait(2000);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should(
|
||||
"be.visible"
|
||||
);
|
||||
|
||||
// Test private access
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) });
|
||||
cy.wait(2000);
|
||||
cy.appUILogin(data.email, "password");
|
||||
verifyWidget("text1");
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) });
|
||||
verifyWidget("text1");
|
||||
|
||||
// Test with private app valid session
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
|
||||
// cy.get(commonSelectors.viewerPageLogo).click();
|
||||
|
||||
// Test public access
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.wait(1000);
|
||||
cy.apiMakeAppPublic();
|
||||
logout();
|
||||
cy.wait(1000);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) });
|
||||
verifyWidget("text1");
|
||||
|
||||
// Test with public app with valid session
|
||||
cy.apiLogin(data.email, "password");
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) });
|
||||
verifyWidget("text1");
|
||||
});
|
||||
|
||||
it("Verify app private and public app visibility for the same instance user", () => {
|
||||
it("should verify app private and public app visibility for the same instance user", () => {
|
||||
setupAppWithSlug(data.appName, data.slug);
|
||||
|
||||
cy.apiLogout();
|
||||
|
|
@ -189,131 +152,77 @@ describe(
|
|||
InstanceSSO(true, true, true);
|
||||
});
|
||||
userSignUp(data.firstName, data.email, data.workspaceName);
|
||||
cy.wait(1000);
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) });
|
||||
|
||||
cy.visit("/");
|
||||
logout();
|
||||
|
||||
// Test public access
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiMakeAppPublic();
|
||||
logout();
|
||||
|
||||
cy.wait(1000);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) });
|
||||
verifyWidget("text1");
|
||||
|
||||
// Verify public app with valid session
|
||||
cy.apiLogin(data.email, "password");
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) });
|
||||
verifyWidget("text1");
|
||||
});
|
||||
|
||||
it("Should redirect to workspace login and handle signup flow of existing and non-existing user", () => {
|
||||
setSignupStatus(true);
|
||||
setupAppWithSlug(data.appName, data.slug);
|
||||
|
||||
cy.apiLogout();
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
|
||||
cy.get(commonSelectors.workspaceName).verifyVisibleElement(
|
||||
"have.text",
|
||||
"My workspace"
|
||||
);
|
||||
|
||||
// Test signup flow
|
||||
it("should redirect to workspace login and handle signup flow of existing and non-existing user", () => {
|
||||
cy.intercept("POST", "/api/onboarding/signup").as("signup");
|
||||
cy.get(commonSelectors.createAnAccountLink).click();
|
||||
cy.wait(3000);
|
||||
cy.intercept('GET', '**/api/white-labelling').as('whiteLabelling');
|
||||
|
||||
cy.apiUserInvite(`${data.firstName}_invited`, `invited_${data.email}`);
|
||||
|
||||
setSignupStatus(true);
|
||||
setupAppWithSlug(data.appPublicName, data.appPublicSlug, 'public');
|
||||
const publicAppId = Cypress.env("appId");
|
||||
cy.apiMakeAppPublic(publicAppId);
|
||||
setupAppWithSlug(data.appPrivateName, data.appPrivateSlug);
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.slug) })
|
||||
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.appPublicSlug) });
|
||||
verifyWidget('public')
|
||||
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.appPrivateSlug) });
|
||||
verifyWidget('private')
|
||||
|
||||
cy.apiLogout();
|
||||
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.appPublicSlug) });
|
||||
verifyWidget('public')
|
||||
|
||||
cy.visitSlug({ actualUrl: getAppUrl(data.appPrivateSlug) });
|
||||
cy.wait('@whiteLabelling');
|
||||
|
||||
cy.get(commonSelectors.createAnAccountLink, { timeout: 20000 }).click();
|
||||
|
||||
cy.get(onboardingSelectors.loginPasswordInput).should("be.visible").click({ force: true });
|
||||
cy.clearAndType(commonSelectors.inputFieldFullName, data.firstName);
|
||||
cy.clearAndType(commonSelectors.inputFieldEmailAddress, data.email);
|
||||
cy.clearAndType('[data-cy="email-input"]', data.email);
|
||||
cy.clearAndType(onboardingSelectors.loginPasswordInput, "password");
|
||||
cy.get(commonSelectors.signUpButton).click();
|
||||
cy.wait('@signup');
|
||||
cy.get(`[data-cy="resend-verification-email-button"]`).should("be.visible");
|
||||
|
||||
cy.wait("@signup").then((interception) => {
|
||||
expect(interception.response.statusCode).to.eq(201);
|
||||
});
|
||||
fetchAndVisitInviteLinkViaMH(data.email);
|
||||
verifyWidget('private')
|
||||
|
||||
// Process invitation
|
||||
onboardUserFromAppLink(data.email, data.slug);
|
||||
// cy.apiLogout();
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
// cy.visitSlug({ actualUrl: getAppUrl(data.appPrivateSlug) })
|
||||
// cy.get(onboardingSelectors.loginPasswordInput).should("be.visible").click({ force: true });
|
||||
// cy.clearAndType('[data-cy="email-input"]', `invited_${data.email}`);
|
||||
// cy.clearAndType(onboardingSelectors.loginPasswordInput, "password");
|
||||
|
||||
// cy.get('[data-cy="viewer-page-logo"]').click();
|
||||
cy.visit("/my-workspace");
|
||||
cy.wait(2000);
|
||||
logout();
|
||||
cy.wait(1000);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should(
|
||||
"be.visible"
|
||||
);
|
||||
|
||||
// Setup new workspace and app
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.apiLogout();
|
||||
cy.apiLogin();
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
setSignupStatus(true, data.workspaceName);
|
||||
|
||||
data.slug = fake.firstName.toLowerCase().replace(/\s+/g, "-");
|
||||
|
||||
cy.createApp(data.appName);
|
||||
|
||||
cy.wait(2000);
|
||||
cy.dragAndDropWidget("Text", 500, 500);
|
||||
releaseApp();
|
||||
setUpSlug(data.slug);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.backToApps();
|
||||
|
||||
// Test signup flow in new workspace
|
||||
cy.apiLogout();
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
|
||||
cy.get(commonSelectors.workspaceName).verifyVisibleElement(
|
||||
"have.text",
|
||||
data.workspaceName
|
||||
);
|
||||
|
||||
cy.get(commonSelectors.createAnAccountLink).click();
|
||||
cy.wait(3000);
|
||||
cy.clearAndType(commonSelectors.inputFieldFullName, data.firstName);
|
||||
cy.clearAndType(commonSelectors.inputFieldEmailAddress, data.email);
|
||||
cy.clearAndType(onboardingSelectors.loginPasswordInput, "password");
|
||||
cy.get(commonSelectors.signUpButton).click();
|
||||
cy.wait("@signup").then((interception) => {
|
||||
expect(interception.response.statusCode).to.eq(201);
|
||||
});
|
||||
|
||||
onboardUserFromAppLink(data.email, data.slug, data.workspaceName, false);
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should verify restricted app access", () => {
|
||||
it("should verify restricted app access", () => {
|
||||
data.workspaceName = fake.firstName;
|
||||
data.workspaceSlug = fake.firstName.toLowerCase().replace(/\s+/g, "-");
|
||||
|
||||
|
|
@ -330,65 +239,12 @@ describe(
|
|||
|
||||
inviteUserToWorkspace(data.firstName, data.email);
|
||||
|
||||
// Verify restricted access
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
verifyRestrictedAccess();
|
||||
cy.get('[data-cy="back-to-home-button"]').click();
|
||||
cy.get(commonSelectors.backToHomeButton).click();
|
||||
cy.get(commonSelectors.homePageLogo).should("be.visible");
|
||||
|
||||
cy.apiLogout();
|
||||
});
|
||||
|
||||
it.skip("Should verify private app access for different workspace users", () => {
|
||||
const firstName1 = fake.firstName;
|
||||
const email1 = fake.email.toLowerCase();
|
||||
const permissionName = fake.firstName.toLowerCase(); // Defined but not used in original
|
||||
const urls = {
|
||||
editor: `${Cypress.config("baseUrl")}/my-workspace/apps/${data.slug}/home`,
|
||||
preview: `${Cypress.config("baseUrl")}/applications/${data.slug}/home?version=v1`,
|
||||
released: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
};
|
||||
|
||||
// Setup workspace and app
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.apiLogout();
|
||||
cy.apiLogin();
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
setupAppWithSlug(data.appName, data.slug);
|
||||
|
||||
// Invite workspace user
|
||||
addNewUser(data.firstName, data.email);
|
||||
cy.wait(500);
|
||||
|
||||
// Verify access restrictions
|
||||
cy.visitSlug({ actualUrl: urls.editor });
|
||||
verifyRestrictedAccess();
|
||||
cy.get('[data-cy="back-to-home-button"]').click();
|
||||
cy.get(commonSelectors.homePageLogo).should("be.visible");
|
||||
|
||||
cy.visitSlug({ actualUrl: urls.preview });
|
||||
|
||||
// Switch users and verify access
|
||||
cy.apiLogout();
|
||||
cy.apiLogin();
|
||||
cy.apiDeleteGranularPermission("end-user");
|
||||
|
||||
cy.apiLogin(data.email, "password");
|
||||
cy.visitSlug({ actualUrl: urls.editor });
|
||||
verifyRestrictedAccess();
|
||||
cy.get('[data-cy="back-to-home-button"]').click();
|
||||
cy.get(commonSelectors.homePageLogo).should("be.visible");
|
||||
cy.visitSlug({ actualUrl: urls.preview });
|
||||
|
||||
cy.apiLogout();
|
||||
|
||||
// Test with new user
|
||||
userSignUp(firstName1, email1, data.workspaceName);
|
||||
cy.visitSlug({ actualUrl: urls.editor });
|
||||
cy.visitSlug({ actualUrl: urls.preview });
|
||||
cy.visitSlug({ actualUrl: urls.released });
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { groupsSelector } from "Selectors/manageGroups";
|
||||
import { fake } from "Fixtures/fake";
|
||||
import {
|
||||
navigateToManageGroups,
|
||||
viewAppCardOptions,
|
||||
} from "Support/utils/common";
|
||||
import {
|
||||
OpenGroupCardOption,
|
||||
verifyGroupCardOptions,
|
||||
duplicateMultipleGroups,
|
||||
createGroupAddAppAndUserToGroup,
|
||||
groupPermission,
|
||||
} from "Support/utils/manageGroups";
|
||||
import { cyParamName } from "Selectors/common";
|
||||
import { roleBasedOnboarding } from "Support/utils/onboarding";
|
||||
|
||||
const data = {};
|
||||
data.groupName = fake.firstName.replaceAll("[^A-Za-z]", "");
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
const workspaceName = fake.firstName;
|
||||
const workspaceSlug = fake.firstName.toLowerCase().replace(/[^A-Za-z]/g, "");
|
||||
|
||||
describe("Groups duplication", () => {
|
||||
beforeEach(() => {
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiCreateWorkspace(workspaceName, workspaceSlug);
|
||||
cy.visit(`${workspaceSlug}`);
|
||||
cy.apiLogout();
|
||||
cy.apiLogin();
|
||||
cy.visit(`${workspaceSlug}`);
|
||||
groupPermission(
|
||||
[
|
||||
"appsCreateCheck",
|
||||
"appsDeleteCheck",
|
||||
"workspaceVarCheckbox",
|
||||
"foldersCreateCheck",
|
||||
],
|
||||
"Admin"
|
||||
);
|
||||
cy.apiCreateApp(data.appName);
|
||||
|
||||
});
|
||||
|
||||
it("Should verify the group duplication feature", () => {
|
||||
data.firstName = fake.firstName;
|
||||
data.email = fake.email.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
cy.visit(`${workspaceSlug}`);
|
||||
roleBasedOnboarding(data.firstName, data.email, "builder");
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.visit(`${workspaceSlug}`);
|
||||
navigateToManageGroups();
|
||||
verifyGroupCardOptions("Admin");
|
||||
cy.wait(3000);
|
||||
cy.get('[datacy="groups-list-option-button"]').click();
|
||||
cy.get('[data-cy="delete-group-card-option"] > .col').should(
|
||||
"have.class",
|
||||
"disable"
|
||||
);
|
||||
duplicateMultipleGroups(["Admin", "Builder", "End-user"]);
|
||||
createGroupAddAppAndUserToGroup(data.groupName, data.email);
|
||||
groupPermission(
|
||||
[
|
||||
"appsCreateCheck",
|
||||
"appsDeleteCheck",
|
||||
"workspaceVarCheckbox",
|
||||
"foldersCreateCheck",
|
||||
],
|
||||
data.groupName,
|
||||
true
|
||||
);
|
||||
cy.wait(1000);
|
||||
verifyGroupCardOptions(data.groupName);
|
||||
cy.get(groupsSelector.duplicateOption).click();
|
||||
|
||||
cy.get(commonSelectors.defaultModalTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Duplicate group"
|
||||
);
|
||||
cy.get(commonSelectors.modalMessage).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Duplicate the following parts of the group"
|
||||
);
|
||||
cy.get(groupsSelector.usersCheckInput).should("be.visible");
|
||||
cy.verifyLabel("Users");
|
||||
cy.get(groupsSelector.permissionCheckInput).should("be.visible");
|
||||
cy.verifyLabel("Permissions");
|
||||
cy.get(groupsSelector.appsCheckInput).should("be.visible");
|
||||
cy.verifyLabel("Apps");
|
||||
cy.get(commonSelectors.cancelButton).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Cancel"
|
||||
);
|
||||
cy.get(groupsSelector.confimButton).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Duplicate"
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
"Group duplicated successfully!"
|
||||
);
|
||||
|
||||
cy.wait(500);
|
||||
cy.get(
|
||||
groupsSelector.duplicatedGroupLink(data.groupName)
|
||||
).verifyVisibleElement("have.text", `${data.groupName}_copy`);
|
||||
|
||||
OpenGroupCardOption(data.groupName);
|
||||
cy.get(groupsSelector.deleteGroupOption).click();
|
||||
cy.get(commonSelectors.buttonSelector("Yes")).click();
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin(data.email, "password");
|
||||
cy.visit(`${workspaceSlug}`);
|
||||
cy.wait(2000);
|
||||
cy.get(commonSelectors.appCreateButton).should("be.visible");
|
||||
cy.get(commonSelectors.createNewFolderButton).should("be.visible");
|
||||
cy.wait(2000);
|
||||
cy.reload();
|
||||
viewAppCardOptions(data.appName);
|
||||
cy.contains("Delete app").should("exist");
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).should("be.visible");
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.visit(`${workspaceSlug}`);
|
||||
navigateToManageGroups();
|
||||
OpenGroupCardOption(`${data.groupName}_copy`);
|
||||
cy.get(groupsSelector.deleteGroupOption).click();
|
||||
cy.get(commonSelectors.buttonSelector("Yes")).click();
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin(data.email, "password");
|
||||
cy.visit(`${workspaceSlug}`);
|
||||
cy.get(commonSelectors.appCreateButton).should("not.exist");
|
||||
cy.get(commonSelectors.createNewFolderButton).should("not.exist");
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).should("not.exist");
|
||||
});
|
||||
});
|
||||
|
|
@ -31,21 +31,17 @@ describe("dashboard", () => {
|
|||
folderName: `${fake.companyName.toLowerCase()}-folder`,
|
||||
cloneAppName: `cloned-${fake.companyName}-App`,
|
||||
updatedFolderName: `new-${fake.companyName.toLowerCase()}-folder`,
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
cy.intercept("GET", "/api/library_apps").as("appLibrary");
|
||||
cy.intercept("DELETE", "/api/folders/*").as("folderDeleted");
|
||||
cy.skipWalkthrough();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.apiLogout();
|
||||
cy.apiLogin();
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
});
|
||||
|
||||
it("should verify the elements on empty dashboard", () => {
|
||||
cy.intercept("GET", "/api/apps*", {
|
||||
fixture: 'intercept/emptyDashboard.json'
|
||||
}).as("dashboardPage");
|
||||
|
||||
cy.intercept("GET", "/api/metadata", {
|
||||
body: {
|
||||
installed_version: "2.9.2",
|
||||
|
|
@ -53,13 +49,13 @@ describe("dashboard", () => {
|
|||
},
|
||||
}).as("version");
|
||||
|
||||
cy.get(commonSelectors.homePageLogo).should("be.visible");
|
||||
cy.visit("/");
|
||||
|
||||
cy.get(commonSelectors.workspaceName).verifyVisibleElement(
|
||||
"have.text",
|
||||
data.workspaceName
|
||||
"My workspace"
|
||||
);
|
||||
cy.get(commonSelectors.workspaceName).click();
|
||||
// cy.get(commonSelectors.editRectangleIcon).should("be.visible");
|
||||
|
||||
cy.get(commonSelectors.appCreateButton).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Create an app"
|
||||
|
|
@ -105,6 +101,12 @@ describe("dashboard", () => {
|
|||
|
||||
cy.wait(500);
|
||||
cy.get(commonSelectors.settingsIcon).click();
|
||||
|
||||
cy.get(dashboardSelector.versionLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Version 2.9.2"
|
||||
);
|
||||
|
||||
cy.get(commonSelectors.marketplaceOption).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Marketplace"
|
||||
|
|
@ -122,7 +124,7 @@ describe("dashboard", () => {
|
|||
commonText.logoutLink
|
||||
);
|
||||
|
||||
cy.get(commonSelectors.breadcrumbTitle).should(($el) => {
|
||||
cy.get(commonSelectors.breadcrumbHeaderTitle).should(($el) => {
|
||||
expect($el.contents().first().text().trim()).to.eq(
|
||||
commonText.breadcrumbApplications
|
||||
);
|
||||
|
|
@ -132,10 +134,6 @@ describe("dashboard", () => {
|
|||
dashboardText.dashboardAppsHeaderLabel
|
||||
);
|
||||
|
||||
cy.get(dashboardSelector.versionLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Version 2.9.2"
|
||||
);
|
||||
cy.get(dashboardSelector.emptyPageImage).should("be.visible");
|
||||
cy.get(dashboardSelector.emptyPageHeader).verifyVisibleElement(
|
||||
"have.text",
|
||||
|
|
@ -158,6 +156,12 @@ describe("dashboard", () => {
|
|||
|
||||
cy.get(dashboardSelector.appTemplateRow).should("be.visible");
|
||||
cy.reload();
|
||||
const env = Cypress.env("environment");
|
||||
if (env === "Enterprise" || env === "Cloud") {
|
||||
cy.get(commonSelectors.homePageLogo).should("be.visible");
|
||||
verifyTooltip(commonSelectors.homePageIcon, "Home");
|
||||
verifyTooltip(commonSelectors.globalWorkFlowsIcon, "Workflows");
|
||||
};
|
||||
verifyTooltip(commonSelectors.dashboardIcon, "Apps");
|
||||
verifyTooltip(commonSelectors.databaseIcon, "ToolJet Database");
|
||||
verifyTooltip(commonSelectors.globalDataSourceIcon, "Data sources");
|
||||
|
|
@ -171,17 +175,17 @@ describe("dashboard", () => {
|
|||
|
||||
it("Should verify app card elements and app card operations", () => {
|
||||
cy.exec("mkdir -p ./cypress/downloads/");
|
||||
cy.exec("cd ./cypress/downloads/ && rm -rf *");
|
||||
cy.exec("cd ./cypress/downloads/ && rm -rf '*'");
|
||||
|
||||
const customLayout = {
|
||||
desktop: { top: 100, left: 20 },
|
||||
mobile: { width: 8, height: 50 },
|
||||
};
|
||||
|
||||
cy.visit("/");
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
|
||||
cy.wait(2000);
|
||||
// cy.ifEnv(["Enterprise", "Cloud"], () => { cy.get('.basic-plan-migration-banner').invoke('css', 'display', 'none') });
|
||||
// cy.wait(2000);
|
||||
cy.get(commonSelectors.appCreationDetails).should("be.visible");
|
||||
cy.get(commonSelectors.appCard(data.appName)).should("be.visible");
|
||||
cy.get(commonSelectors.appTitle(data.appName)).verifyVisibleElement(
|
||||
|
|
@ -189,6 +193,7 @@ describe("dashboard", () => {
|
|||
data.appName
|
||||
);
|
||||
|
||||
|
||||
viewAppCardOptions(data.appName);
|
||||
cy.get(
|
||||
commonSelectors.appCardOptions(commonText.changeIconOption)
|
||||
|
|
@ -205,7 +210,6 @@ describe("dashboard", () => {
|
|||
cy.get(
|
||||
commonSelectors.appCardOptions(commonText.deleteAppOption)
|
||||
).verifyVisibleElement("have.text", commonText.deleteAppOption);
|
||||
|
||||
modifyAndVerifyAppCardIcon(data.appName);
|
||||
createFolder(data.folderName);
|
||||
|
||||
|
|
@ -261,12 +265,15 @@ describe("dashboard", () => {
|
|||
commonText.appRemovedFromFolderTaost,
|
||||
false
|
||||
);
|
||||
|
||||
cy.get(commonSelectors.modalComponent).should("not.exist");
|
||||
cy.get(commonSelectors.empytyFolderImage).should("be.visible");
|
||||
cy.wait(1000);
|
||||
cy.get(commonSelectors.emptyFolderText).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonText.emptyFolderText
|
||||
);
|
||||
|
||||
cy.get(commonSelectors.allApplicationsLink).click();
|
||||
deleteFolder(data.folderName);
|
||||
|
||||
|
|
@ -277,6 +284,7 @@ describe("dashboard", () => {
|
|||
cy.wait(2000);
|
||||
cy.get(commonSelectors.appCardOptions(commonText.exportAppOption)).click();
|
||||
cy.get(commonSelectors.exportAllButton).click();
|
||||
cy.wait(2000)
|
||||
|
||||
|
||||
cy.exec("ls ./cypress/downloads/").then((result) => {
|
||||
|
|
@ -292,9 +300,12 @@ describe("dashboard", () => {
|
|||
.and("have.text", dashboardText.appClonedToast);
|
||||
cy.wait(3000);
|
||||
|
||||
cy.get(commonSelectors.editorAppNameInput).click();
|
||||
cy.renameApp(data.cloneAppName);
|
||||
cy.apiAddComponentToApp(data.cloneAppName, "button", 25, 25);
|
||||
|
||||
cy.backToApps();
|
||||
cy.ifEnv(["Enterprise", "Cloud"], () => { cy.get('.basic-plan-migration-banner').invoke('css', 'display', 'none') });
|
||||
cy.wait("@appLibrary");
|
||||
cy.wait(1000);
|
||||
|
||||
|
|
@ -303,6 +314,7 @@ describe("dashboard", () => {
|
|||
cy.wait(1000);
|
||||
|
||||
viewAppCardOptions(data.cloneAppName);
|
||||
|
||||
cy.get(commonSelectors.deleteAppOption).click();
|
||||
cy.get(commonSelectors.modalMessage).verifyVisibleElement(
|
||||
"have.text",
|
||||
|
|
@ -337,7 +349,7 @@ describe("dashboard", () => {
|
|||
desktop: { top: 100, left: 20 },
|
||||
mobile: { width: 8, height: 50 },
|
||||
};
|
||||
|
||||
cy.visit("/");
|
||||
cy.createApp(data.appName);
|
||||
cy.apiAddComponentToApp(data.appName, "text1", customLayout);
|
||||
|
||||
|
|
@ -349,11 +361,11 @@ describe("dashboard", () => {
|
|||
);
|
||||
|
||||
navigateToAppEditor(data.appName);
|
||||
// cy.get(commonSelectors.canvas).should("contain", "text1");
|
||||
cy.get(".text-widget-section > div").should("be.visible");
|
||||
cy.backToApps();
|
||||
cy.wait("@appLibrary");
|
||||
|
||||
cy.ifEnv(["Enterprise", "Cloud"], () => { cy.get('.basic-plan-migration-banner').invoke('css', 'display', 'none') });
|
||||
cy.wait(2000);
|
||||
cy.deleteApp(data.appName);
|
||||
|
||||
cy.get(commonSelectors.appCard(data.appName)).should("not.exist");
|
||||
|
|
@ -364,7 +376,7 @@ describe("dashboard", () => {
|
|||
desktop: { top: 100, left: 20 },
|
||||
mobile: { width: 8, height: 50 },
|
||||
};
|
||||
|
||||
cy.visit("/");
|
||||
cy.createApp(data.appName);
|
||||
cy.apiAddComponentToApp(data.appName, "text1", customLayout);
|
||||
cy.backToApps();
|
||||
|
|
@ -473,7 +485,8 @@ describe("dashboard", () => {
|
|||
cy.get(dashboardSelector.folderName(data.updatedFolderName)).should(
|
||||
"not.exist"
|
||||
);
|
||||
|
||||
cy.ifEnv(["Enterprise", "Cloud"], () => { cy.get('.basic-plan-migration-banner').invoke('css', 'display', 'none') });
|
||||
cy.wait(2000);
|
||||
cy.get(commonSelectors.allApplicationsLink).click();
|
||||
cy.deleteApp(data.appName);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { groupsSelector } from "Selectors/manageGroups";
|
||||
import { workspaceConstantsSelectors } from "Selectors/workspaceConstants";
|
||||
import {
|
||||
createFolder,
|
||||
deleteFolder,
|
||||
navigateToManageGroups,
|
||||
selectAppCardOption,
|
||||
} from "Support/utils/common";
|
||||
|
|
@ -12,15 +9,25 @@ import {
|
|||
createGroupsAndAddUserInGroup,
|
||||
setupWorkspaceAndInviteUser,
|
||||
updateRole,
|
||||
} from "Support/utils/manageGroups";
|
||||
import {
|
||||
uiAppCRUDWorkflow,
|
||||
uiDataSourceCRUDWorkflow,
|
||||
uiFolderCRUDWorkflow,
|
||||
uiVerifyAdminPrivileges,
|
||||
uiVerifyBuilderPrivileges,
|
||||
uiWorkflowCRUDWorkflow,
|
||||
uiWorkspaceConstantCRUDWorkflow,
|
||||
} from "Support/utils/uiPermissions";
|
||||
import {
|
||||
verifyBasicPermissions,
|
||||
verifySettingsAccess,
|
||||
} from "Support/utils/manageGroups";
|
||||
import { addAndVerifyConstants } from "Support/utils/workspaceConstants";
|
||||
} from "Support/utils/userPermissions";
|
||||
import { commonText } from "Texts/common";
|
||||
import { dashboardText } from "Texts/dashboard";
|
||||
import { groupsText } from "Texts/manageGroups";
|
||||
|
||||
describe("Manage Groups", () => {
|
||||
describe("Basic Permissions", () => {
|
||||
let data = {};
|
||||
|
||||
before(() => {
|
||||
|
|
@ -52,9 +59,98 @@ describe("Manage Groups", () => {
|
|||
);
|
||||
|
||||
verifyBasicPermissions(false);
|
||||
verifySettingsAccess(false);
|
||||
});
|
||||
|
||||
it("should verify builder privileges and role updates in custom groups", () => {
|
||||
it("should verify builder privileges", () => {
|
||||
setupWorkspaceAndInviteUser(
|
||||
data.workspaceName,
|
||||
data.workspaceSlug,
|
||||
data.firstName,
|
||||
data.email,
|
||||
"builder"
|
||||
);
|
||||
|
||||
// UI-based privilege verification for Builder
|
||||
cy.get(".basic-plan-migration-banner").invoke("css", "display", "none");
|
||||
uiVerifyBuilderPrivileges();
|
||||
|
||||
// UI CRUD workflows validation
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
const uiTestAppName = `${data.appName}_ui`;
|
||||
const uiTestFolderName = `${data.folderName}-ui`;
|
||||
const uiTestConstName = `${data.firstName}_const`;
|
||||
const uiTestConstValue = "test_value";
|
||||
|
||||
// Perform UI-based CRUD operations
|
||||
uiAppCRUDWorkflow(uiTestAppName);
|
||||
uiFolderCRUDWorkflow(uiTestFolderName);
|
||||
uiWorkspaceConstantCRUDWorkflow(uiTestConstName, uiTestConstValue);
|
||||
|
||||
// Enterprise-specific UI workflows
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
const uiTestDsName = `${data.appName}_ds`;
|
||||
const uiTestWorkflowName = `${data.appName}_wf`;
|
||||
uiDataSourceCRUDWorkflow(uiTestDsName, "restapi");
|
||||
uiWorkflowCRUDWorkflow(uiTestWorkflowName);
|
||||
});
|
||||
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.openApp();
|
||||
cy.releaseApp();
|
||||
|
||||
//verify clone access
|
||||
cy.visit(data.workspaceSlug);
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.cloneAppOption)
|
||||
);
|
||||
cy.get(commonSelectors.cloneAppButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
dashboardText.appClonedToast,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify admin privileges", () => {
|
||||
setupWorkspaceAndInviteUser(
|
||||
data.workspaceName,
|
||||
data.workspaceSlug,
|
||||
data.firstName,
|
||||
data.email,
|
||||
"admin"
|
||||
);
|
||||
|
||||
// API-based verification
|
||||
verifyBasicPermissions(true);
|
||||
|
||||
// UI-based privilege verification for Admin (includes settings access)
|
||||
uiVerifyAdminPrivileges();
|
||||
|
||||
// UI CRUD workflows for validation
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
const uiTestAppName = `${data.appName}_admin_ui`;
|
||||
const uiTestFolderName = `${data.folderName}-admin-ui`;
|
||||
const uiTestConstName = `${data.firstName}_admin_const`;
|
||||
const uiTestConstValue = "admin_test_value";
|
||||
|
||||
// Perform UI-based CRUD operations
|
||||
uiAppCRUDWorkflow(uiTestAppName);
|
||||
uiFolderCRUDWorkflow(uiTestFolderName);
|
||||
uiWorkspaceConstantCRUDWorkflow(uiTestConstName, uiTestConstValue);
|
||||
|
||||
// Enterprise-specific UI workflows
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
const uiTestDsName = `${data.appName}_admin_ds`;
|
||||
const uiTestWorkflowName = `${data.appName}_admin_wf`;
|
||||
uiDataSourceCRUDWorkflow(uiTestDsName, "restapi");
|
||||
uiWorkflowCRUDWorkflow(uiTestWorkflowName);
|
||||
});
|
||||
});
|
||||
|
||||
it("should verify role updates in custom groups", () => {
|
||||
const builderGroup = fake.firstName.replace(/[^A-Za-z]/g, "");
|
||||
const endUserGroup = fake.firstName.replace(/[^A-Za-z]/g, "");
|
||||
|
||||
|
|
@ -66,47 +162,10 @@ describe("Manage Groups", () => {
|
|||
"builder"
|
||||
);
|
||||
|
||||
// Verify builder permissions
|
||||
verifyBasicPermissions(true);
|
||||
|
||||
// App operations
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.apiDeleteApp();
|
||||
|
||||
// Folder operations
|
||||
cy.apiCreateFolder(data.folderName);
|
||||
cy.apiDeleteFolder();
|
||||
|
||||
// Constants management
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).click();
|
||||
addAndVerifyConstants(data.firstName, data.appName);
|
||||
cy.get(
|
||||
workspaceConstantsSelectors.constDeleteButton(data.firstName)
|
||||
).click();
|
||||
cy.get(commonSelectors.yesButton).click();
|
||||
|
||||
verifySettingsAccess(false);
|
||||
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
cy.apiCreateApp(data.appName);
|
||||
|
||||
//verify clone access
|
||||
cy.reload();
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.cloneAppOption)
|
||||
);
|
||||
cy.get(commonSelectors.cloneAppButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
dashboardText.appClonedToast,
|
||||
false
|
||||
);
|
||||
// cy.get(commonSelectors.cancelButton).click();
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.visit(data.workspaceSlug);
|
||||
cy.apiCreateApp(`${data.appName}_builder`);
|
||||
navigateToManageGroups();
|
||||
|
||||
[builderGroup, endUserGroup].forEach((group) => {
|
||||
|
|
@ -140,43 +199,10 @@ describe("Manage Groups", () => {
|
|||
cy.apiLogout();
|
||||
cy.apiLogin(data.email, "password");
|
||||
cy.visit(data.workspaceSlug);
|
||||
cy.get(commonSelectors.appCard(data.appName))
|
||||
cy.get(commonSelectors.appCard(`${data.appName}_builder`))
|
||||
.trigger("mouseover")
|
||||
.trigger("mouseenter")
|
||||
.find(commonSelectors.editButton)
|
||||
.should("not.exist");
|
||||
});
|
||||
|
||||
it("should verify admin privileges", () => {
|
||||
setupWorkspaceAndInviteUser(
|
||||
data.workspaceName,
|
||||
data.workspaceSlug,
|
||||
data.firstName,
|
||||
data.email,
|
||||
"admin"
|
||||
);
|
||||
|
||||
verifyBasicPermissions(true);
|
||||
|
||||
// App operations
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.apiDeleteApp();
|
||||
|
||||
// Folder operations
|
||||
cy.apiCreateFolder(data.folderName);
|
||||
cy.apiDeleteFolder();
|
||||
|
||||
// Constants management
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).click();
|
||||
addAndVerifyConstants(data.firstName, data.appName);
|
||||
cy.get(
|
||||
workspaceConstantsSelectors.constDeleteButton(data.firstName)
|
||||
).click();
|
||||
cy.get(commonSelectors.yesButton).click();
|
||||
|
||||
// Settings access check - explicitly verify workspace settings
|
||||
cy.get(commonSelectors.settingsIcon).click();
|
||||
cy.get(commonSelectors.workspaceSettings).should("exist");
|
||||
cy.wait(1000);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,227 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { dataSourceSelector } from "Selectors/dataSource";
|
||||
import { groupsSelector } from "Selectors/manageGroups";
|
||||
import { navigateToManageGroups } from "Support/utils/common";
|
||||
import {
|
||||
createGroupsAndAddUserInGroup,
|
||||
setupWorkspaceAndInviteUser,
|
||||
} from "Support/utils/manageGroups";
|
||||
import { getGroupPermissionInput } from "Support/utils/userPermissions";
|
||||
import { groupsText } from "Texts/manageGroups";
|
||||
|
||||
describe("Custom Group Granular Access", () => {
|
||||
let data = {};
|
||||
const isEnterprise = Cypress.env("environment") === "Enterprise";
|
||||
|
||||
before(() => {
|
||||
cy.exec("mkdir -p ./cypress/downloads/");
|
||||
cy.wait(3000);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
firstName: fake.firstName,
|
||||
appName: fake.companyName,
|
||||
email: fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
workspaceName: fake.lastName.toLowerCase().replace(/[^A-Za-z]/g, ""),
|
||||
workspaceSlug: fake.lastName.toLowerCase().replace(/[^A-Za-z]/g, ""),
|
||||
folderName: fake.companyName,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.intercept("DELETE", "/api/folders/*").as("folderDeleted");
|
||||
cy.skipWalkthrough();
|
||||
cy.viewport(2400, 2000);
|
||||
});
|
||||
|
||||
it("should verify the granular permissions in custom groups", () => {
|
||||
const groupName = fake.firstName.replace(/[^A-Za-z]/g, "");
|
||||
const appName2 = fake.companyName;
|
||||
const appName3 = fake.companyName;
|
||||
const appSlug = appName3.toLowerCase().replace(/\s+/g, "-");
|
||||
const workflowName1 = `${data.appName}-workflow`;
|
||||
const workflowName2 = `${appName2}-workflow`;
|
||||
const datasourceName1 = `${data.appName}-datasource`;
|
||||
const datasourceName2 = `${appName2}-datasource`;
|
||||
|
||||
setupWorkspaceAndInviteUser(
|
||||
data.workspaceName,
|
||||
data.workspaceSlug,
|
||||
data.firstName,
|
||||
data.email,
|
||||
"builder"
|
||||
);
|
||||
|
||||
cy.apiLogin();
|
||||
cy.visit(data.workspaceSlug);
|
||||
cy.apiUpdateGroupPermission(
|
||||
"builder",
|
||||
getGroupPermissionInput(isEnterprise, false)
|
||||
);
|
||||
|
||||
cy.apiDeleteGranularPermission("builder", []);
|
||||
|
||||
navigateToManageGroups();
|
||||
createGroupsAndAddUserInGroup(groupName, data.email);
|
||||
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.apiCreateApp(appName2);
|
||||
|
||||
// App Hide from dashboard
|
||||
cy.apiCreateApp(appName3);
|
||||
cy.openApp();
|
||||
cy.apiAddComponentToApp(appName3, "text1");
|
||||
cy.apiPromoteAppVersion().then(() => {
|
||||
const stagingId = Cypress.env("stagingEnvId");
|
||||
cy.apiPromoteAppVersion(Cypress.env("stagingEnvId"));
|
||||
});
|
||||
cy.apiReleaseApp(appName3);
|
||||
cy.apiAddAppSlug(appName3, appSlug);
|
||||
cy.go("back");
|
||||
|
||||
// Configure app permissions
|
||||
navigateToManageGroups();
|
||||
cy.get(groupsSelector.groupLink(groupName)).click();
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
|
||||
// Setup permissions for both apps
|
||||
[data.appName, appName2, appName3].forEach((app) => {
|
||||
cy.ifEnv("Community", () => {
|
||||
cy.get(groupsSelector.addAppsButton).click();
|
||||
});
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get(groupsSelector.addPermissionButton).click();
|
||||
cy.get(groupsSelector.addAppButton).click();
|
||||
});
|
||||
|
||||
cy.clearAndType(groupsSelector.permissionNameInput, app);
|
||||
cy.get(groupsSelector.customRadio).check();
|
||||
cy.get(".css-1gfides").click({ force: true }).type(`${app}{enter}`);
|
||||
cy.get(groupsSelector.confimButton).click({ force: true });
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.createPermissionToast
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(groupsSelector.groupChip).contains(data.appName).click();
|
||||
cy.get(groupsSelector.editPermissionRadio).click();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
|
||||
//To hide app
|
||||
cy.get(groupsSelector.groupChip).contains(appName3).click();
|
||||
cy.get(groupsSelector.hidePermissionInput).check();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.apiCreateWorkflow(workflowName1);
|
||||
cy.apiCreateWorkflow(workflowName2);
|
||||
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
datasourceName1,
|
||||
"restapi",
|
||||
[{ key: "url", value: "https://jsonplaceholder.typicode.com/users" }]
|
||||
);
|
||||
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
datasourceName2,
|
||||
"restapi",
|
||||
[{ key: "url", value: "https://jsonplaceholder.typicode.com/users" }]
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.groupLink("Builder")).click();
|
||||
cy.get(groupsSelector.groupLink(groupName)).click();
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
|
||||
[workflowName1, workflowName2].forEach((workflow) => {
|
||||
cy.get(groupsSelector.addPermissionButton).click();
|
||||
cy.get(groupsSelector.addWorkflowButton).click();
|
||||
|
||||
cy.clearAndType(groupsSelector.permissionNameInput, workflow);
|
||||
cy.get(groupsSelector.customRadio).check();
|
||||
cy.get(".css-1gfides")
|
||||
.click({ force: true })
|
||||
.type(`${workflow}{enter}`);
|
||||
cy.get(groupsSelector.confimButton).click({ force: true });
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.createPermissionToast
|
||||
);
|
||||
});
|
||||
cy.get(groupsSelector.groupChip).contains(workflowName1).click();
|
||||
cy.get(groupsSelector.buildWorkflowradio).click();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
|
||||
[datasourceName1, datasourceName2].forEach((datasource) => {
|
||||
cy.get(groupsSelector.addPermissionButton).click();
|
||||
cy.get(groupsSelector.addDatasourceButton).click();
|
||||
|
||||
cy.clearAndType(groupsSelector.permissionNameInput, datasource);
|
||||
cy.get(groupsSelector.customRadio).check();
|
||||
cy.get(".css-1gfides")
|
||||
.click({ force: true })
|
||||
.type(`${datasource}{enter}`);
|
||||
cy.get(groupsSelector.confimButton).click({ force: true });
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.createPermissionToast
|
||||
);
|
||||
});
|
||||
cy.get(groupsSelector.groupChip).contains(datasourceName1).click();
|
||||
cy.get(groupsSelector.configureDatasourceradio).click();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
});
|
||||
|
||||
// Verify as builder
|
||||
cy.wait(1000);
|
||||
cy.apiLogout();
|
||||
cy.apiLogin(data.email);
|
||||
cy.visit(data.workspaceSlug);
|
||||
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
cy.get(commonSelectors.appCreateButton).should("not.exist");
|
||||
cy.get('.appcard-buttons-wrap [data-cy="edit-button"]').should(
|
||||
"have.lengthOf",
|
||||
1
|
||||
);
|
||||
cy.get('.appcard-buttons-wrap [data-cy="launch-button"]').should(
|
||||
"have.lengthOf",
|
||||
2
|
||||
);
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get(commonSelectors.globalWorkFlowsIcon).click();
|
||||
cy.get('[data-cy="create-new-workflows-button"]').should("not.exist");
|
||||
cy.get('.appcard-buttons-wrap [data-cy="edit-button"]').should(
|
||||
"have.lengthOf",
|
||||
1
|
||||
);
|
||||
cy.get('.appcard-buttons-wrap [data-cy="launch-button"]').should(
|
||||
"have.lengthOf",
|
||||
2
|
||||
);
|
||||
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
cy.get(
|
||||
dataSourceSelector.dataSourceNameButton(datasourceName1.toLowerCase())
|
||||
).click();
|
||||
cy.get(dataSourceSelector.dsNameInputField).should("be.enabled");
|
||||
cy.get(
|
||||
dataSourceSelector.dataSourceNameButton(datasourceName2.toLowerCase())
|
||||
).click();
|
||||
cy.get(dataSourceSelector.dsNameInputField).should("be.disabled");
|
||||
|
||||
cy.get(dataSourceSelector.commonDsLabelAndCount).click();
|
||||
cy.get('[data-cy="rest-api-add-button"]').should("be.disabled");
|
||||
});
|
||||
|
||||
//Visit hidden app url
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${appSlug}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { dataSourceSelector } from "Selectors/dataSource";
|
||||
import { groupsSelector } from "Selectors/manageGroups";
|
||||
import { navigateToManageGroups } from "Support/utils/common";
|
||||
import {
|
||||
createGroupsAndAddUserInGroup,
|
||||
setupWorkspaceAndInviteUser,
|
||||
} from "Support/utils/manageGroups";
|
||||
import {
|
||||
getGroupPermissionInput,
|
||||
verifyBuilderPermissions,
|
||||
} from "Support/utils/userPermissions";
|
||||
import { groupsText } from "Texts/manageGroups";
|
||||
|
||||
describe("Custom Group Permissions", () => {
|
||||
let data = {};
|
||||
const isEnterprise = Cypress.env("environment") === "Enterprise";
|
||||
|
||||
before(() => {
|
||||
cy.exec("mkdir -p ./cypress/downloads/");
|
||||
cy.wait(3000);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
firstName: fake.firstName,
|
||||
appName: fake.companyName,
|
||||
email: fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
workspaceName: fake.lastName.toLowerCase().replace(/[^A-Za-z]/g, ""),
|
||||
workspaceSlug: fake.lastName.toLowerCase().replace(/[^A-Za-z]/g, ""),
|
||||
folderName: fake.companyName,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.intercept("DELETE", "/api/folders/*").as("folderDeleted");
|
||||
cy.skipWalkthrough();
|
||||
cy.viewport(2400, 2000);
|
||||
});
|
||||
|
||||
it("should verify user permissions in custom groups", () => {
|
||||
const groupName = fake.firstName.replace(/[^A-Za-z]/g, "");
|
||||
const workflowName1 = fake.companyName;
|
||||
const datasourceName1 = fake.companyName
|
||||
.toLowerCase()
|
||||
.replace(/[^A-Za-z]/g, "");
|
||||
|
||||
setupWorkspaceAndInviteUser(
|
||||
data.workspaceName,
|
||||
data.workspaceSlug,
|
||||
data.firstName,
|
||||
data.email
|
||||
);
|
||||
cy.apiLogout();
|
||||
|
||||
// Setup custom group
|
||||
cy.apiLogin();
|
||||
cy.visit(data.workspaceSlug);
|
||||
cy.get(".basic-plan-migration-banner").invoke("css", "display", "none");
|
||||
|
||||
cy.apiUpdateGroupPermission(
|
||||
"builder",
|
||||
getGroupPermissionInput(isEnterprise, false)
|
||||
);
|
||||
|
||||
cy.visit(data.workspaceSlug);
|
||||
navigateToManageGroups();
|
||||
|
||||
createGroupsAndAddUserInGroup(groupName, data.email);
|
||||
|
||||
// Permission configuration and verification
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
|
||||
// App creation permission
|
||||
cy.get(groupsSelector.appsCreateCheck).check();
|
||||
cy.get(commonSelectors.defaultModalTitle).contains(
|
||||
groupsText.changeUserRoleHeader
|
||||
);
|
||||
cy.get(groupsSelector.changeRoleModalMessage).contains(
|
||||
groupsText.changeUserRoleMessage
|
||||
);
|
||||
cy.get(".item-list").contains(data.email);
|
||||
cy.get(groupsSelector.confimButton).should(
|
||||
"have.text",
|
||||
groupsText.continueButtonText
|
||||
);
|
||||
cy.get(commonSelectors.cancelButton).click();
|
||||
|
||||
// Other permissions
|
||||
const permissions =
|
||||
Cypress.env("environment") === "Community"
|
||||
? [
|
||||
groupsSelector.appsDeleteCheck,
|
||||
groupsSelector.foldersCreateCheck,
|
||||
groupsSelector.workspaceVarCheckbox,
|
||||
]
|
||||
: [
|
||||
groupsSelector.appsDeleteCheck,
|
||||
groupsSelector.appPromoteCheck,
|
||||
groupsSelector.appReleaseCheck,
|
||||
groupsSelector.workflowsCreateCheck,
|
||||
groupsSelector.workflowsDeleteCheck,
|
||||
groupsSelector.datasourcesCreateCheck,
|
||||
groupsSelector.datasourcesDeleteCheck,
|
||||
groupsSelector.foldersCreateCheck,
|
||||
groupsSelector.workspaceVarCheckbox,
|
||||
];
|
||||
|
||||
permissions.forEach((permission) => {
|
||||
cy.get(permission).check();
|
||||
cy.get(".modal-content").should("be.visible");
|
||||
cy.get(commonSelectors.cancelButton).click();
|
||||
});
|
||||
|
||||
// Granular permissions
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
|
||||
cy.ifEnv("Community", () => {
|
||||
cy.get(groupsSelector.addAppsButton).click();
|
||||
});
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get(groupsSelector.addPermissionButton).click();
|
||||
cy.get(groupsSelector.addAppButton).click();
|
||||
});
|
||||
|
||||
cy.clearAndType(groupsSelector.permissionNameInput, data.firstName);
|
||||
cy.get(groupsSelector.editPermissionRadio).click();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
|
||||
// Verify modal
|
||||
cy.get(".modal-content").should("be.visible");
|
||||
cy.get(groupsSelector.modalHeader).should(
|
||||
"have.text",
|
||||
groupsText.cantCreatePermissionModalHeader
|
||||
);
|
||||
cy.get(groupsSelector.modalMessage).should(
|
||||
"have.text",
|
||||
groupsText.cantCreatePermissionModalMessage
|
||||
);
|
||||
|
||||
cy.get(".item-list").contains(data.email);
|
||||
cy.get(commonSelectors.closeButton).click();
|
||||
|
||||
// Role transition
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
cy.get(groupsSelector.appsCreateCheck).check();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
permissions.forEach((permission) => {
|
||||
cy.get(permission).check();
|
||||
});
|
||||
cy.get(groupsSelector.groupLink("Builder")).click();
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
cy.get(`[data-cy="${data.email}-user-row"]`).should("be.visible");
|
||||
cy.apiLogout();
|
||||
cy.apiLogin(data.email);
|
||||
cy.visit(data.workspaceSlug);
|
||||
|
||||
verifyBuilderPermissions(
|
||||
data.appName,
|
||||
data.folderName,
|
||||
data.firstName,
|
||||
data.appName
|
||||
);
|
||||
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.visit(data.workspaceSlug);
|
||||
|
||||
// Reset permissions
|
||||
cy.apiDeleteGranularPermission("builder", []);
|
||||
cy.apiDeleteGranularPermission(groupName, []);
|
||||
cy.apiCreateApp(data.appName);
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.apiCreateWorkflow(workflowName1);
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
datasourceName1,
|
||||
"restapi",
|
||||
[{ key: "url", value: "https://jsonplaceholder.typicode.com/users" }]
|
||||
);
|
||||
});
|
||||
|
||||
cy.apiLogout();
|
||||
cy.apiLogin(data.email);
|
||||
cy.visit(data.workspaceSlug);
|
||||
|
||||
cy.get(commonSelectors.appCreateButton).should("be.enabled");
|
||||
cy.get(commonSelectors.appCard(data.appName)).should("not.exist");
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get(commonSelectors.globalWorkFlowsIcon).click();
|
||||
cy.get('[data-cy="create-new-workflows-button"]').should("exist");
|
||||
cy.get(commonSelectors.appCard(workflowName1)).should("not.exist");
|
||||
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
cy.get(dataSourceSelector.dataSourceNameButton(datasourceName1)).should(
|
||||
"exist"
|
||||
);
|
||||
cy.get(dataSourceSelector.commonDsLabelAndCount).click();
|
||||
cy.get('[data-cy="rest-api-add-button"]').should("be.enabled");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { commonEeSelectors } from "Selectors/eeCommon";
|
||||
import { groupsSelector } from "Selectors/manageGroups";
|
||||
import { navigateToManageGroups } from "Support/utils/common";
|
||||
import {
|
||||
apiAddUserToGroup,
|
||||
apiCreateGroup,
|
||||
apiDeleteGroup,
|
||||
} from "Support/utils/manageGroups";
|
||||
import {
|
||||
addGranularPermissionViaUI,
|
||||
createGroupViaUI,
|
||||
deleteGroupViaUI,
|
||||
openGroupThreeDotMenu,
|
||||
renameGroupViaUI,
|
||||
switchBetweenAllAndCustom,
|
||||
verifyDuplicateModal,
|
||||
verifyGroupCreatedInSidebar,
|
||||
verifyGroupRemovedFromSidebar,
|
||||
} from "Support/utils/platform/customGroups";
|
||||
|
||||
import {
|
||||
verifyCheckPermissionStates,
|
||||
verifyEmptyStates,
|
||||
verifyGranularAccessByRole,
|
||||
verifyGranularEditModal,
|
||||
verifyGranularPermissionModalStates,
|
||||
verifyGroupLinks,
|
||||
verifyPermissionCheckBoxLabelsAndHelperTexts,
|
||||
verifyUserRow,
|
||||
} from "Support/utils/platform/groupsUI";
|
||||
|
||||
import { getGroupPermissionInput } from "Support/utils/userPermissions";
|
||||
|
||||
describe("Custom groups UI and Functionality verification", () => {
|
||||
const isEnterprise = Cypress.env("environment") === "Enterprise";
|
||||
|
||||
const groupName = fake.firstName.replaceAll("[^A-Za-z]", "");
|
||||
const newGroupname = `New ${groupName}`;
|
||||
const groupName2 = `${fake.firstName.replaceAll("[^A-Za-z]", "")}2`;
|
||||
const groupName3 = `${fake.firstName.replaceAll("[^A-Za-z]", "")}3`;
|
||||
const duplicatedGroupName = `${groupName3}_copy`;
|
||||
const appPermissionName = "Apps";
|
||||
const workflowPermissionName = "Workflows";
|
||||
const datasourcePermissionName = " Data sources";
|
||||
const permissionName2 = "Permission2";
|
||||
const data = {
|
||||
firstName: fake.firstName,
|
||||
email: fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
let groupId3;
|
||||
|
||||
before(() => {
|
||||
cy.apiLogin();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
cy.viewport(2000, 1900);
|
||||
});
|
||||
|
||||
it("should create, rename, and delete a custom group", () => {
|
||||
navigateToManageGroups();
|
||||
createGroupViaUI(groupName);
|
||||
|
||||
verifyGroupCreatedInSidebar(groupName);
|
||||
|
||||
renameGroupViaUI(groupName, newGroupname);
|
||||
|
||||
verifyGroupCreatedInSidebar(newGroupname);
|
||||
|
||||
deleteGroupViaUI(newGroupname);
|
||||
|
||||
verifyGroupRemovedFromSidebar(newGroupname);
|
||||
});
|
||||
|
||||
it("should create custom group, verify empty states, add permissions, and manage granular access", () => {
|
||||
apiCreateGroup(groupName2);
|
||||
|
||||
cy.apiCreateApp(groupName);
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.apiCreateWorkflow(groupName);
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
groupName,
|
||||
"restapi",
|
||||
[{ key: "url", value: "https://jsonplaceholder.typicode.com/users" }]
|
||||
);
|
||||
});
|
||||
|
||||
navigateToManageGroups();
|
||||
cy.get(groupsSelector.groupLink(groupName2)).click();
|
||||
|
||||
verifyGroupLinks();
|
||||
verifyEmptyStates(true);
|
||||
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
verifyCheckPermissionStates("custom");
|
||||
verifyPermissionCheckBoxLabelsAndHelperTexts();
|
||||
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
addGranularPermissionViaUI(appPermissionName, {
|
||||
resourceType: "app",
|
||||
permission: "edit",
|
||||
scope: "all",
|
||||
});
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
addGranularPermissionViaUI(workflowPermissionName, {
|
||||
resourceType: "workflow",
|
||||
permission: "build",
|
||||
scope: "all",
|
||||
});
|
||||
|
||||
addGranularPermissionViaUI(datasourcePermissionName, {
|
||||
resourceType: "datasource",
|
||||
permission: "configure",
|
||||
scope: "all",
|
||||
});
|
||||
});
|
||||
verifyGranularAccessByRole("builder");
|
||||
verifyGranularEditModal("custom");
|
||||
|
||||
cy.get('[data-cy="apps-granular-access"]').realHover();
|
||||
cy.get('[data-cy="edit-apps-granular-access"]').click();
|
||||
|
||||
verifyGranularPermissionModalStates("app", "custom");
|
||||
|
||||
cy.get(groupsSelector.viewPermissionRadio).check();
|
||||
switchBetweenAllAndCustom("custom");
|
||||
|
||||
verifyGranularPermissionModalStates("app", "custom", {
|
||||
editRadio: { checked: false, enabled: true },
|
||||
viewRadio: { checked: true, enabled: true },
|
||||
hideCheckbox: { enabled: true },
|
||||
allAppsRadio: { checked: false, enabled: true },
|
||||
customRadio: { checked: true, enabled: true },
|
||||
});
|
||||
|
||||
switchBetweenAllAndCustom("all");
|
||||
verifyGranularPermissionModalStates("app", "custom", {
|
||||
editRadio: { checked: false, enabled: true },
|
||||
viewRadio: { checked: true, enabled: true },
|
||||
hideCheckbox: { enabled: true },
|
||||
allAppsRadio: { checked: true, enabled: true },
|
||||
customRadio: { checked: false, enabled: true },
|
||||
});
|
||||
|
||||
cy.get(groupsSelector.editPermissionRadio).check();
|
||||
verifyGranularPermissionModalStates("app", "custom", {
|
||||
editRadio: { checked: true, enabled: true },
|
||||
viewRadio: { checked: false, enabled: true },
|
||||
hideCheckbox: { enabled: false },
|
||||
allAppsRadio: { checked: true, enabled: true },
|
||||
customRadio: { checked: false, enabled: true },
|
||||
});
|
||||
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get('[data-cy="workflow-granular-access"]').realHover();
|
||||
cy.get('[data-cy="edit-workflow-granular-access"]').click();
|
||||
|
||||
verifyGranularPermissionModalStates("workflow", "custom");
|
||||
|
||||
cy.get(groupsSelector.executeWorkflowradio).check();
|
||||
verifyGranularPermissionModalStates("workflow", "custom", {
|
||||
buildRadio: { checked: false, enabled: true },
|
||||
executeRadio: { checked: true, enabled: true },
|
||||
});
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
|
||||
cy.get('[data-cy="datasource-granular-access"]').realHover();
|
||||
cy.get('[data-cy="edit-datasource-granular-access"]').click();
|
||||
verifyGranularPermissionModalStates("datasource", "custom");
|
||||
|
||||
cy.get(groupsSelector.buildWithDatasourceRadio).check();
|
||||
verifyGranularPermissionModalStates("datasource", "custom", {
|
||||
configureRadio: { checked: false, enabled: true },
|
||||
buildWithRadio: { checked: true, enabled: true },
|
||||
});
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
});
|
||||
|
||||
apiDeleteGroup(groupName2);
|
||||
});
|
||||
|
||||
it("should create group via API, add permissions, duplicate group and verify all permissions are copied", () => {
|
||||
cy.apiFullUserOnboarding(data.firstName, data.email, "builder");
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp(groupName3);
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.apiCreateWorkflow(groupName3);
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
groupName3,
|
||||
"restapi",
|
||||
[{ key: "url", value: "https://jsonplaceholder.typicode.com/users" }]
|
||||
);
|
||||
});
|
||||
|
||||
apiCreateGroup(groupName3).then((groupId) => {
|
||||
groupId3 = groupId;
|
||||
apiAddUserToGroup(groupId3, data.email);
|
||||
cy.apiCreateGranularPermission(
|
||||
groupName3,
|
||||
"Apps",
|
||||
"app",
|
||||
{ canEdit: true, canView: false, hideFromDashboard: false },
|
||||
[]
|
||||
);
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.apiCreateGranularPermission(
|
||||
groupName3,
|
||||
"Workflows",
|
||||
"workflow",
|
||||
{ canEdit: true, canView: false, hideFromDashboard: false },
|
||||
[]
|
||||
);
|
||||
cy.apiCreateGranularPermission(
|
||||
groupName3,
|
||||
"Data sources",
|
||||
"datasource",
|
||||
{ canUse: false, canConfigure: true },
|
||||
[]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
cy.apiUpdateGroupPermission(
|
||||
groupName3,
|
||||
getGroupPermissionInput(isEnterprise, true)
|
||||
);
|
||||
|
||||
navigateToManageGroups();
|
||||
cy.wait(2000);
|
||||
openGroupThreeDotMenu(groupName3);
|
||||
cy.get(groupsSelector.duplicateOption).click();
|
||||
verifyDuplicateModal(groupName3);
|
||||
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
|
||||
openGroupThreeDotMenu(groupName3);
|
||||
cy.get(groupsSelector.duplicateOption).click();
|
||||
cy.get(commonEeSelectors.confirmButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
"Group duplicated successfully"
|
||||
);
|
||||
|
||||
cy.wait(1000);
|
||||
verifyUserRow(data.firstName, ` ${data.email}`);
|
||||
cy.get(groupsSelector.groupLink(duplicatedGroupName)).click();
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
verifyCheckPermissionStates("builder");
|
||||
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
verifyGranularAccessByRole("builder");
|
||||
|
||||
apiDeleteGroup(duplicatedGroupName);
|
||||
apiDeleteGroup(groupName3);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,264 +0,0 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { groupsSelector } from "Selectors/manageGroups";
|
||||
import * as common from "Support/utils/common";
|
||||
import * as groups from "Support/utils/manageGroups";
|
||||
import { groupsText } from "Texts/manageGroups";
|
||||
|
||||
const verifyModalFields = (isEdit = false, groupName = '') => {
|
||||
// Modal header verification
|
||||
cy.get(groupsSelector.permissionNameLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.permissionNameLabel
|
||||
);
|
||||
cy.get(groupsSelector.permissionNameInput)
|
||||
.should("be.visible")
|
||||
.and(isEdit ? "have.value" : "have.attr",
|
||||
isEdit ? groupName : "placeholder",
|
||||
isEdit ? groupName : "Eg. Product analytics apps");
|
||||
cy.get(groupsSelector.permissionNameHelperText).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.permissionNameHelperText
|
||||
);
|
||||
|
||||
// Permission section verification
|
||||
const permissionChecks = [
|
||||
{ selector: groupsSelector.permissionLabel, text: groupsText.permissionLabel },
|
||||
{ selector: groupsSelector.editPermissionLabel, text: groupsText.editPermissionLabel },
|
||||
{ selector: groupsSelector.editPermissionHelperText, text: groupsText.appEditHelperText },
|
||||
{ selector: groupsSelector.viewPermissionLabel, text: groupsText.viewPermissionLabel },
|
||||
{ selector: groupsSelector.viewPermissionHelperText, text: groupsText.appViewHelperText }
|
||||
];
|
||||
|
||||
permissionChecks.forEach(({ selector, text }) => {
|
||||
cy.get(selector).verifyVisibleElement("have.text", text);
|
||||
});
|
||||
|
||||
// Hide permission verification
|
||||
cy.get(groupsSelector.hidePermissionInput).should("be.visible");
|
||||
cy.get(groupsSelector.appHidePermissionModalLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.appHideLabel
|
||||
);
|
||||
cy.get(groupsSelector.appHidePermissionModalHelperText).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.appHideHelperText
|
||||
);
|
||||
|
||||
// Resource section verification
|
||||
const resourceChecks = [
|
||||
{ selector: groupsSelector.resourceLabel, text: groupsText.resourcesheader },
|
||||
{ selector: groupsSelector.allAppsLabel, text: isEdit ? groupsText.allAppsLabel : groupsText.groupChipText },
|
||||
{ selector: groupsSelector.allAppsHelperText, text: groupsText.allAppsHelperText }
|
||||
];
|
||||
|
||||
resourceChecks.forEach(({ selector, text }) => {
|
||||
cy.get(selector).verifyVisibleElement("have.text", text);
|
||||
});
|
||||
|
||||
// Button states
|
||||
cy.get(groupsSelector.confimButton).verifyVisibleElement(
|
||||
"have.text",
|
||||
isEdit ? groupsText.updateButtonText : groupsText.addButtonText
|
||||
);
|
||||
cy.get(groupsSelector.confimButton).should(isEdit ? "be.enabled" : "be.disabled");
|
||||
cy.get(groupsSelector.cancelButton).verifyVisibleElement("have.text", groupsText.cancelButton);
|
||||
|
||||
if (isEdit) {
|
||||
cy.get(groupsSelector.deletePermissionIcon).should("be.visible");
|
||||
}
|
||||
};
|
||||
|
||||
const verifyPermissionSection = () => {
|
||||
const permissionElements = [
|
||||
{
|
||||
resource: groupsSelector.resourcesApps,
|
||||
resourceText: groupsText.resourcesApps,
|
||||
check: groupsSelector.appsCreateCheck,
|
||||
label: groupsSelector.appsCreateLabel,
|
||||
labelText: groupsText.createLabel,
|
||||
helperText: groupsSelector.appCreateHelperText,
|
||||
helperContent: groupsText.appCreateHelperText
|
||||
},
|
||||
{
|
||||
resource: groupsSelector.resourcesFolders,
|
||||
resourceText: groupsText.resourcesFolders,
|
||||
check: groupsSelector.foldersCreateCheck,
|
||||
label: groupsSelector.foldersCreateLabel,
|
||||
labelText: groupsText.folderCreateLabel,
|
||||
helperText: groupsSelector.foldersHelperText,
|
||||
helperContent: groupsText.folderHelperText
|
||||
}
|
||||
];
|
||||
|
||||
permissionElements.forEach(({ resource, resourceText, check, label, labelText, helperText, helperContent }) => {
|
||||
cy.get(resource).verifyVisibleElement("have.text", resourceText);
|
||||
cy.get(check).should("be.visible");
|
||||
cy.get(check).check();
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, groupsText.permissionUpdatedToast);
|
||||
cy.get(check).uncheck();
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, groupsText.permissionUpdatedToast);
|
||||
cy.get(label).verifyVisibleElement("have.text", labelText);
|
||||
cy.get(helperText).verifyVisibleElement("have.text", helperContent);
|
||||
});
|
||||
};
|
||||
|
||||
const verifyEmptyStates = () => {
|
||||
// Users empty state
|
||||
cy.get(groupsSelector.userEmptyPageIcon).should("be.visible");
|
||||
cy.get(groupsSelector.userEmptyPageTitle).verifyVisibleElement("have.text", groupsText.userEmptyPageTitle);
|
||||
cy.get(groupsSelector.userEmptyPageHelperText).verifyVisibleElement("have.text", groupsText.userEmptyPageHelperText);
|
||||
|
||||
// Granular permissions empty state
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
cy.get(groupsSelector.granularEmptyPageIcon).should("be.visible");
|
||||
cy.get(groupsSelector.emptyPagePermissionTitle).verifyVisibleElement("have.text", groupsText.emptyPagePermissionTitle);
|
||||
cy.get(groupsSelector.emptyPagePermissionHelperText).verifyVisibleElement("have.text", groupsText.emptyPagePermissionHelperText);
|
||||
};
|
||||
|
||||
const verifyGroupLinks = () => {
|
||||
const links = [
|
||||
{ selector: groupsSelector.usersLink, text: groupsText.usersLink },
|
||||
{ selector: groupsSelector.permissionsLink, text: groupsText.permissionsLink },
|
||||
{ selector: groupsSelector.granularLink, text: "Granular access" }
|
||||
];
|
||||
|
||||
links.forEach(({ selector, text }) => {
|
||||
cy.get(selector).verifyVisibleElement("have.text", text);
|
||||
});
|
||||
};
|
||||
|
||||
describe("Manage Groups", () => {
|
||||
const groupName = fake.firstName.replaceAll("[^A-Za-z]", "");
|
||||
const newGroupname = `New ${groupName}`;
|
||||
const data = {
|
||||
firstName: fake.firstName,
|
||||
email: fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", "")
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.defaultWorkspaceLogin();
|
||||
});
|
||||
|
||||
it("Should verify the elements and functionalities on manage groups page", () => {
|
||||
// Setup workspace
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
common.navigateToManageGroups();
|
||||
|
||||
// Verify page headers
|
||||
cy.get(commonSelectors.breadcrumbTitle).should(($el) => {
|
||||
expect($el.contents().first().text().trim()).to.eq("Workspace settings");
|
||||
});
|
||||
cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement("have.text", "Groups");
|
||||
|
||||
// Verify base group elements
|
||||
groups.manageGroupsElements();
|
||||
|
||||
// Test group creation flow
|
||||
const verifyGroupCreation = () => {
|
||||
cy.get(groupsSelector.createNewGroupButton).should("be.visible").click();
|
||||
cy.get(groupsSelector.addNewGroupModalTitle).verifyVisibleElement("have.text", groupsText.cardTitle);
|
||||
cy.get(groupsSelector.groupNameInput).should("be.visible");
|
||||
cy.get(groupsSelector.cancelButton).verifyVisibleElement("have.text", groupsText.cancelButton);
|
||||
cy.get(groupsSelector.createGroupButton).verifyVisibleElement("have.text", groupsText.createGroupButton);
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
};
|
||||
|
||||
// Test duplicate group name
|
||||
const testDuplicateGroup = () => {
|
||||
cy.get(groupsSelector.createNewGroupButton).click();
|
||||
cy.clearAndType(groupsSelector.groupNameInput, groupsText.admin);
|
||||
cy.get(groupsSelector.createGroupButton).click();
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, groupsText.groupNameExistToast);
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
cy.get(groupsSelector.groupNameInput).should("not.exist");
|
||||
};
|
||||
|
||||
// Create and verify new group
|
||||
const createNewGroup = () => {
|
||||
cy.get(groupsSelector.createNewGroupButton).click();
|
||||
cy.clearAndType(groupsSelector.groupNameInput, groupName);
|
||||
cy.get(groupsSelector.createGroupButton).click();
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, groupsText.groupCreatedToast);
|
||||
|
||||
// Verify group creation
|
||||
cy.get(groupsSelector.groupLink(groupName)).verifyVisibleElement("have.text", groupName);
|
||||
cy.get(groupsSelector.groupLink(groupName)).click();
|
||||
cy.get(groupsSelector.groupPageTitle(groupName)).verifyVisibleElement("have.text", `${groupName} (0)`);
|
||||
cy.get(groupsSelector.groupNameUpdateLink).should("be.visible");
|
||||
|
||||
// Verify group links and tables
|
||||
verifyGroupLinks();
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
cy.get(groupsSelector.nameTableHeader).verifyVisibleElement("have.text", groupsText.userNameTableHeader);
|
||||
cy.get(groupsSelector.emailTableHeader).verifyVisibleElement("have.text", groupsText.emailTableHeader);
|
||||
verifyEmptyStates();
|
||||
};
|
||||
|
||||
verifyGroupCreation();
|
||||
testDuplicateGroup();
|
||||
createNewGroup();
|
||||
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
verifyPermissionSection();
|
||||
|
||||
// Test granular access section
|
||||
const verifyGranularAccess = () => {
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
cy.get(groupsSelector.addAppButton).click();
|
||||
verifyModalFields();
|
||||
|
||||
// Add permission
|
||||
cy.clearAndType(groupsSelector.permissionNameInput, groupName);
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
|
||||
// Verify edit mode
|
||||
cy.get(`[data-cy="${groupName.toLowerCase()}-text"]`).click();
|
||||
cy.get(`${groupsSelector.addEditPermissionModalTitle}:eq(2)`)
|
||||
.verifyVisibleElement("have.text", groupsText.editPermissionModalTitle);
|
||||
cy.get(groupsSelector.editPermissionRadio).check();
|
||||
verifyModalFields(true, groupName);
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
};
|
||||
|
||||
verifyGranularAccess();
|
||||
|
||||
// Test group rename and delete
|
||||
const verifyGroupRename = () => {
|
||||
cy.get(groupsSelector.groupNameUpdateLink).click();
|
||||
cy.get(groupsSelector.updateGroupNameModalTitle)
|
||||
.verifyVisibleElement("have.text", groupsText.updateGroupNameModalTitle);
|
||||
cy.clearAndType(groupsSelector.groupNameInput, newGroupname);
|
||||
cy.get(groupsSelector.createGroupButton).click();
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, groupsText.groupNameUpdateSucessToast);
|
||||
cy.get(groupsSelector.groupLink(newGroupname))
|
||||
.verifyVisibleElement("have.text", newGroupname);
|
||||
};
|
||||
|
||||
const verifyGroupDelete = () => {
|
||||
cy.get(groupsSelector.groupLink(newGroupname)).click();
|
||||
groups.OpenGroupCardOption(newGroupname);
|
||||
cy.get(groupsSelector.deleteGroupOption).click();
|
||||
|
||||
// Verify delete confirmation
|
||||
cy.get(groupsSelector.confirmText)
|
||||
.verifyVisibleElement("have.text", groupsText.confirmText);
|
||||
cy.get(commonSelectors.buttonSelector("Cancel"))
|
||||
.verifyVisibleElement("have.text", groupsText.confirmCancelButton);
|
||||
cy.get(commonSelectors.buttonSelector("Yes"))
|
||||
.verifyVisibleElement("have.text", groupsText.confirmYesButton);
|
||||
cy.get(commonSelectors.buttonSelector("Cancel")).click();
|
||||
|
||||
// Actual delete
|
||||
groups.OpenGroupCardOption(newGroupname);
|
||||
cy.get(groupsSelector.deleteGroupOption).click();
|
||||
cy.get(commonSelectors.buttonSelector("Yes")).click();
|
||||
};
|
||||
|
||||
verifyGroupRename();
|
||||
verifyGroupDelete();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,37 +1,30 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import {
|
||||
exportAppModalSelectors,
|
||||
importSelectors,
|
||||
} from "Selectors/exportImport";
|
||||
import { groupsSelector } from "Selectors/manageGroups";
|
||||
import { workspaceConstantsSelectors } from "Selectors/workspaceConstants";
|
||||
import {
|
||||
createFolder,
|
||||
deleteFolder,
|
||||
logout,
|
||||
navigateToManageGroups,
|
||||
navigateToManageUsers,
|
||||
releaseApp,
|
||||
selectAppCardOption,
|
||||
} from "Support/utils/common";
|
||||
import {
|
||||
createGroupsAndAddUserInGroup,
|
||||
inviteUserBasedOnRole,
|
||||
setupAndUpdateRole,
|
||||
setupWorkspaceAndInviteUser,
|
||||
updateRole,
|
||||
verifyBasicPermissions,
|
||||
verifySettingsAccess,
|
||||
verifyUserPrivileges,
|
||||
} from "Support/utils/manageGroups";
|
||||
import { addAndVerifyConstants } from "Support/utils/workspaceConstants";
|
||||
import { commonText } from "Texts/common";
|
||||
import { exportAppModalText, importText } from "Texts/exportImport";
|
||||
import { groupsText } from "Texts/manageGroups";
|
||||
|
||||
describe("Manage Groups", () => {
|
||||
let data = {};
|
||||
const isEnterprise = Cypress.env("environment") === "Enterprise";
|
||||
|
||||
before(() => {
|
||||
cy.exec("mkdir -p ./cypress/downloads/");
|
||||
|
|
@ -52,6 +45,7 @@ describe("Manage Groups", () => {
|
|||
cy.defaultWorkspaceLogin();
|
||||
cy.intercept("DELETE", "/api/folders/*").as("folderDeleted");
|
||||
cy.skipWalkthrough();
|
||||
cy.viewport(2400, 2000);
|
||||
});
|
||||
|
||||
it("should verify the last active admin role update protection", () => {
|
||||
|
|
@ -89,215 +83,6 @@ describe("Manage Groups", () => {
|
|||
cy.get(commonSelectors.closeButton).click();
|
||||
});
|
||||
|
||||
it("should verify user privileges in custom groups", () => {
|
||||
const groupName = fake.firstName.replace(/[^A-Za-z]/g, "");
|
||||
const appName2 = fake.companyName;
|
||||
const appName3 = fake.companyName;
|
||||
const appSlug = appName3.toLowerCase().replace(/\s+/g, "-");
|
||||
|
||||
setupWorkspaceAndInviteUser(
|
||||
data.workspaceName,
|
||||
data.workspaceSlug,
|
||||
data.firstName,
|
||||
data.email
|
||||
);
|
||||
cy.apiLogout();
|
||||
|
||||
// Setup custom group
|
||||
cy.apiLogin();
|
||||
cy.visit(data.workspaceSlug);
|
||||
navigateToManageGroups();
|
||||
cy.get(groupsSelector.groupLink("Builder")).click();
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
cy.get(groupsSelector.appsCreateCheck).uncheck();
|
||||
cy.get(groupsSelector.appsDeleteCheck).uncheck();
|
||||
cy.get(groupsSelector.foldersCreateCheck).uncheck();
|
||||
cy.get(groupsSelector.workspaceVarCheckbox).uncheck();
|
||||
|
||||
createGroupsAndAddUserInGroup(groupName, data.email);
|
||||
|
||||
// Permission configuration and verification
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
|
||||
// App creation permission
|
||||
cy.get(groupsSelector.appsCreateCheck).check();
|
||||
cy.get(commonSelectors.defaultModalTitle).contains(
|
||||
groupsText.changeUserRoleHeader
|
||||
);
|
||||
cy.get(groupsSelector.changeRoleModalMessage).contains(
|
||||
groupsText.changeUserRoleMessage
|
||||
);
|
||||
cy.get(".item-list").contains(data.email);
|
||||
cy.get(groupsSelector.confimButton).should(
|
||||
"have.text",
|
||||
groupsText.continueButtonText
|
||||
);
|
||||
cy.get(commonSelectors.cancelButton).click();
|
||||
|
||||
// Other permissions
|
||||
const permissions = [
|
||||
groupsSelector.appsDeleteCheck,
|
||||
groupsSelector.foldersCreateCheck,
|
||||
groupsSelector.workspaceVarCheckbox,
|
||||
];
|
||||
|
||||
permissions.forEach((permission) => {
|
||||
cy.get(permission).check();
|
||||
cy.get(".modal-content").should("be.visible");
|
||||
cy.get(commonSelectors.cancelButton).click();
|
||||
});
|
||||
|
||||
// Granular permissions
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
cy.get(groupsSelector.addAppButton).click();
|
||||
cy.clearAndType(groupsSelector.permissionNameInput, data.firstName);
|
||||
cy.get(groupsSelector.editPermissionRadio).click();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
|
||||
// Verify modal
|
||||
cy.get(".modal-content").should("be.visible");
|
||||
cy.get(groupsSelector.modalHeader).should(
|
||||
"have.text",
|
||||
groupsText.cantCreatePermissionModalHeader
|
||||
);
|
||||
cy.get(groupsSelector.modalMessage).should(
|
||||
"have.text",
|
||||
groupsText.cantCreatePermissionModalMessage
|
||||
);
|
||||
|
||||
cy.get(".item-list").contains(data.email);
|
||||
cy.get(commonSelectors.closeButton).click();
|
||||
|
||||
// Role transition
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
cy.get(groupsSelector.appsCreateCheck).check();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
permissions.forEach((permission) => {
|
||||
cy.get(permission).check();
|
||||
});
|
||||
cy.get(groupsSelector.groupLink("Builder")).click();
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
cy.get(`[data-cy="${data.email}-user-row"]`).should("be.visible");
|
||||
cy.apiLogout();
|
||||
cy.apiLogin(data.email);
|
||||
cy.visit(data.workspaceSlug);
|
||||
|
||||
// Verify builder permissions
|
||||
verifyBasicPermissions(true);
|
||||
|
||||
// App operations
|
||||
cy.createApp(data.appName);
|
||||
// cy.verifyToastMessage(
|
||||
// commonSelectors.toastMessage,
|
||||
// commonText.appCreatedToast
|
||||
// );
|
||||
cy.backToApps();
|
||||
|
||||
cy.wait(2500);
|
||||
cy.deleteApp(data.appName);
|
||||
|
||||
|
||||
// Folder operations
|
||||
createFolder(data.folderName);
|
||||
deleteFolder(data.folderName);
|
||||
|
||||
// Constants management
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).click();
|
||||
addAndVerifyConstants(data.firstName, data.appName);
|
||||
cy.get(
|
||||
workspaceConstantsSelectors.constDeleteButton(data.firstName)
|
||||
).click();
|
||||
cy.get(commonSelectors.yesButton).click();
|
||||
|
||||
verifySettingsAccess(false);
|
||||
cy.get(commonSelectors.settingsIcon).click();
|
||||
cy.wait(500);
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.visit(data.workspaceSlug);
|
||||
navigateToManageGroups();
|
||||
cy.get(groupsSelector.groupLink(groupName)).click();
|
||||
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
cy.get(groupsSelector.appsCreateCheck).uncheck();
|
||||
permissions.forEach((permission) => {
|
||||
cy.get(permission).uncheck();
|
||||
});
|
||||
|
||||
cy.wait(2000);
|
||||
cy.get(groupsSelector.groupLink("Builder")).click();
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
cy.wait(2000);
|
||||
cy.get(groupsSelector.granularAccessPermission)
|
||||
.trigger("mouseenter")
|
||||
.click({ force: true });
|
||||
cy.get(groupsSelector.deletePermissionIcon).click();
|
||||
cy.get(groupsSelector.yesButton).click();
|
||||
|
||||
// Create test apps
|
||||
cy.get(commonSelectors.homePageLogo).click();
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.apiCreateApp(appName2);
|
||||
|
||||
// App Hide from dashboard
|
||||
cy.apiCreateApp(appName3);
|
||||
cy.openApp();
|
||||
cy.apiAddComponentToApp(appName3, "text1");
|
||||
releaseApp();
|
||||
cy.get(commonWidgetSelector.shareAppButton).click();
|
||||
cy.clearAndType(commonWidgetSelector.appNameSlugInput, `${appSlug}`);
|
||||
cy.wait(500);
|
||||
cy.get(commonWidgetSelector.modalCloseButton).click();
|
||||
cy.backToApps();
|
||||
|
||||
// Configure app permissions
|
||||
navigateToManageGroups();
|
||||
cy.get(groupsSelector.groupLink(groupName)).click();
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
|
||||
// Setup permissions for both apps
|
||||
[data.appName, appName2, appName3].forEach((app) => {
|
||||
cy.get(groupsSelector.addAppButton).click();
|
||||
cy.clearAndType(groupsSelector.permissionNameInput, app);
|
||||
cy.get(groupsSelector.customradio).check();
|
||||
cy.get(".css-1gfides").click({ force: true }).type(`${app}{enter}`);
|
||||
cy.get(groupsSelector.confimButton).click({ force: true });
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.createPermissionToast
|
||||
);
|
||||
});
|
||||
cy.get(groupsSelector.groupChip).contains(data.appName).click();
|
||||
cy.get(groupsSelector.editPermissionRadio).click();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
|
||||
//To hide app
|
||||
cy.get(groupsSelector.groupChip).contains(appName3).click();
|
||||
cy.get(groupsSelector.hidePermissionInput).check();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
|
||||
// Verify as end user
|
||||
cy.wait(1000);
|
||||
cy.apiLogout()
|
||||
cy.apiLogin(data.email);
|
||||
cy.visit(data.workspaceSlug);
|
||||
|
||||
cy.get('.appcard-buttons-wrap [data-cy="edit-button"]').should(
|
||||
"have.lengthOf",
|
||||
1
|
||||
);
|
||||
cy.get('.appcard-buttons-wrap [data-cy="launch-button"]').should(
|
||||
"have.lengthOf",
|
||||
2
|
||||
);
|
||||
|
||||
//Visit hidden app url
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${appSlug}`,
|
||||
});
|
||||
});
|
||||
|
||||
it("should verify user role updating sequence", () => {
|
||||
const roleUpdateSequence = [
|
||||
{
|
||||
|
|
@ -407,7 +192,7 @@ describe("Manage Groups", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("should verify query creation and import access for Builders and Admin", () => {
|
||||
it.skip("should verify query creation and import access for Builders and Admin", () => {
|
||||
const firstName2 = fake.firstName;
|
||||
const email2 = fake.email.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
const workspaceName2 = fake.firstName
|
||||
|
|
@ -454,7 +239,7 @@ describe("Manage Groups", () => {
|
|||
cy.wait(500);
|
||||
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env('server_host')}/api/data-sources`,
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
`cypress-${data.dsName}-qc-postgresql`,
|
||||
"postgresql",
|
||||
[
|
||||
|
|
@ -479,21 +264,21 @@ describe("Manage Groups", () => {
|
|||
//Create and run postgres query in the app
|
||||
// Need to enable once bug is fixed
|
||||
/*
|
||||
|
||||
|
||||
addQuery(
|
||||
"table_preview",
|
||||
`SELECT * FROM persons;`,
|
||||
`cypress-${data.dsName1}-postgresql`
|
||||
);
|
||||
|
||||
cy.get('[data-cy="list-query-table_preview"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"table_preview "
|
||||
);
|
||||
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
|
||||
verifyValueOnInspector("table_preview", "7 items ");
|
||||
*/
|
||||
|
||||
|
||||
addQuery(
|
||||
"table_preview",
|
||||
`SELECT * FROM persons;`,
|
||||
`cypress-${data.dsName1}-postgresql`
|
||||
);
|
||||
|
||||
cy.get('[data-cy="list-query-table_preview"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"table_preview "
|
||||
);
|
||||
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
|
||||
verifyValueOnInspector("table_preview", "7 items ");
|
||||
*/
|
||||
|
||||
cy.backToApps();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,202 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { groupsSelector } from "Selectors/manageGroups";
|
||||
import { navigateToManageGroups } from "Support/utils/common";
|
||||
import {
|
||||
commonGroupVerification,
|
||||
toggleAllPermissions,
|
||||
verifyAdminHelperText,
|
||||
verifyCheckPermissionStates,
|
||||
verifyEditUserRoleModal,
|
||||
verifyEmptyStates,
|
||||
verifyEnduserHelperText,
|
||||
verifyGranularAccessByRole,
|
||||
verifyGranularAddModal,
|
||||
verifyGranularEditModal,
|
||||
verifyPermissionCheckBoxLabelsAndHelperTexts,
|
||||
verifyUserRow,
|
||||
} from "Support/utils/platform/groupsUI";
|
||||
import { groupsText } from "Texts/manageGroups";
|
||||
|
||||
describe("User Role UI and Functionality verification", () => {
|
||||
const data = {
|
||||
firstName: fake.firstName,
|
||||
email: fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
|
||||
before(() => {
|
||||
cy.apiLogin();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
navigateToManageGroups();
|
||||
cy.viewport(2000, 1900);
|
||||
});
|
||||
|
||||
it("should verify admin role UI elements and interactions", () => {
|
||||
// Verify page headers
|
||||
cy.get(commonSelectors.breadcrumbTitle).should(($el) => {
|
||||
expect($el.contents().first().text().trim()).to.eq("Workspace settings");
|
||||
});
|
||||
cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Groups"
|
||||
);
|
||||
|
||||
cy.get('[data-cy="page-title"]').should(($el) => {
|
||||
expect($el.contents().last().text().trim()).to.eq("Groups");
|
||||
});
|
||||
|
||||
cy.verifyElement(
|
||||
groupsSelector.createNewGroupButton,
|
||||
groupsText.createNewGroupButton
|
||||
);
|
||||
|
||||
cy.get('[data-cy="user-role-title"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"USER ROLE"
|
||||
);
|
||||
cy.verifyElement('[data-cy="custom-groups-title"]', "CUSTOM GROUPS");
|
||||
cy.get('[data-cy="create-group-button-icon"]').should("be.visible");
|
||||
cy.get('[data-cy="search-icon"]').should("be.visible");
|
||||
|
||||
// Admin List Item Verification
|
||||
cy.verifyElement(groupsSelector.adminListItem, "Admin");
|
||||
cy.verifyElement(groupsSelector.adminTitle, "Admin (1)");
|
||||
cy.verifyElement(
|
||||
groupsSelector.textDefaultGroup,
|
||||
groupsText.textDefaultGroup
|
||||
);
|
||||
|
||||
// Verify tabs visibility
|
||||
cy.get(groupsSelector.usersLink).should("be.visible");
|
||||
cy.get(groupsSelector.permissionsLink).should("be.visible");
|
||||
cy.get(groupsSelector.granularLink).should("be.visible");
|
||||
|
||||
commonGroupVerification();
|
||||
|
||||
// Users Tab Verification
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
cy.get('[data-cy="user-group-search-btn"]').should("be.visible");
|
||||
cy.verifyElement(
|
||||
groupsSelector.nameTableHeader,
|
||||
groupsText.userNameTableHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.emailTableHeader,
|
||||
groupsText.emailTableHeader
|
||||
);
|
||||
|
||||
verifyUserRow("The Developer", "dev@tooljet.io");
|
||||
|
||||
cy.get('[data-cy="edit-role-button"]')
|
||||
.should("be.visible")
|
||||
.and("be.enabled");
|
||||
cy.get('[data-cy="edit-role-button"]').click();
|
||||
verifyEditUserRoleModal("dev@tooljet.io");
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
|
||||
// Permissions Tab Verification
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
verifyAdminHelperText(0);
|
||||
|
||||
verifyCheckPermissionStates("admin");
|
||||
verifyPermissionCheckBoxLabelsAndHelperTexts();
|
||||
|
||||
// Granular Access Tab Verification
|
||||
verifyGranularAccessByRole("admin");
|
||||
verifyAdminHelperText(1);
|
||||
});
|
||||
|
||||
it("should verify builder role UI elements and interactions", () => {
|
||||
// Builder Group Navigation and Title Verification
|
||||
cy.get(groupsSelector.groupLink("Builder")).click();
|
||||
cy.verifyElement(groupsSelector.builderListItem, "Builder");
|
||||
cy.verifyElement(groupsSelector.builderTitle, "Builder (0)");
|
||||
cy.verifyElement(
|
||||
groupsSelector.textDefaultGroup,
|
||||
groupsText.textDefaultGroup
|
||||
);
|
||||
|
||||
// Verify tabs visibility
|
||||
cy.get(groupsSelector.usersLink).should("be.visible");
|
||||
cy.get(groupsSelector.permissionsLink).should("be.visible");
|
||||
cy.get(groupsSelector.granularLink).should("be.visible");
|
||||
|
||||
// Users Tab Verification - Empty State
|
||||
verifyEmptyStates();
|
||||
|
||||
// Permissions Tab Verification
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
verifyCheckPermissionStates("builder");
|
||||
verifyPermissionCheckBoxLabelsAndHelperTexts();
|
||||
toggleAllPermissions();
|
||||
|
||||
// Granular Access Tab Verification
|
||||
verifyGranularAccessByRole("builder");
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHideLabel,
|
||||
groupsText.appHideLabelPermissionModal
|
||||
);
|
||||
|
||||
// Edit Modal Verification
|
||||
verifyGranularEditModal("builder");
|
||||
|
||||
// Add Modal Verification
|
||||
verifyGranularAddModal("builder");
|
||||
});
|
||||
|
||||
it("should verify enduser role UI and interaction", () => {
|
||||
// End-user Group Navigation and Title Verification
|
||||
cy.get(groupsSelector.groupLink("End-user")).click();
|
||||
cy.get(groupsSelector.groupLink("End-user")).verifyVisibleElement(
|
||||
"have.text",
|
||||
"End-user"
|
||||
);
|
||||
cy.get(groupsSelector.enduserTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
"End-user (0)"
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.textDefaultGroup,
|
||||
groupsText.textDefaultGroup
|
||||
);
|
||||
|
||||
// Verify tabs visibility
|
||||
cy.get(groupsSelector.usersLink).should("be.visible");
|
||||
cy.get(groupsSelector.permissionsLink).should("be.visible");
|
||||
cy.get(groupsSelector.granularLink).should("be.visible");
|
||||
|
||||
// Users Tab Verification - Empty State
|
||||
verifyEmptyStates();
|
||||
|
||||
// Permissions Tab Verification
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
verifyEnduserHelperText(0);
|
||||
verifyCheckPermissionStates("enduser");
|
||||
verifyPermissionCheckBoxLabelsAndHelperTexts();
|
||||
|
||||
// Granular Access Tab Verification
|
||||
cy.reload();
|
||||
cy.wait(2000);
|
||||
cy.get(groupsSelector.groupLink("End-user")).click();
|
||||
|
||||
verifyGranularAccessByRole("enduser");
|
||||
verifyEnduserHelperText(1);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHideLabel,
|
||||
groupsText.appHideLabelPermissionModal
|
||||
);
|
||||
|
||||
// Edit Modal Verification
|
||||
verifyGranularEditModal("enduser");
|
||||
|
||||
// Add Modal Verification
|
||||
verifyGranularAddModal("enduser");
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { dashboardSelector } from "Selectors/dashboard";
|
||||
import { commonText } from "Texts/common";
|
||||
import { dashboardText } from "Texts/dashboard";
|
||||
|
||||
describe("Home Page Dashboard Testcases", () => {
|
||||
let data = {};
|
||||
const isEnterprise = Cypress.env("environment") === "Enterprise";
|
||||
|
||||
before(function () {
|
||||
if (Cypress.env("environment") === "Community") {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
beforeEach(() => {
|
||||
|
||||
data = {
|
||||
firstName: fake.firstName,
|
||||
email: fake.email.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
cy.intercept("GET", "/api/library_apps").as("appLibrary");
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
});
|
||||
|
||||
it("Should verify elements on home page dashboard", () => {
|
||||
|
||||
cy.get(commonSelectors.homePageIcon).click();
|
||||
cy.get(commonSelectors.breadcrumbHeaderTitle).should(($el) => {
|
||||
expect($el.contents().first().text().trim()).to.eq(
|
||||
commonText.breadcrumbHome
|
||||
);
|
||||
});
|
||||
cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonText.breadcrumbHome
|
||||
);
|
||||
|
||||
cy.get(dashboardSelector.aiIcon).should("be.visible");
|
||||
cy.get(dashboardSelector.homePagePromptHeader).verifyVisibleElement(
|
||||
"have.text",
|
||||
dashboardText.homePagePromptHeader
|
||||
);
|
||||
|
||||
cy.get(dashboardSelector.promptInput).should("be.visible");
|
||||
cy.get(dashboardSelector.homePagePromptTextArea).should("be.enabled").type("Build a task manager app");
|
||||
cy.get(dashboardSelector.promptEnterButton).should("have.attr", "class").and("include", "tw-opacity-100");
|
||||
|
||||
|
||||
cy.get(dashboardSelector.homePageDividerText).verifyVisibleElement(
|
||||
"have.text",
|
||||
dashboardText.homePageDividerText
|
||||
);
|
||||
|
||||
// Define card types
|
||||
const cardTypes = [
|
||||
{ type: 'app', title: dashboardText.appCardTitle, description: dashboardText.appCardDescription, url: "my-workspace" },
|
||||
{ type: 'datasource', title: dashboardText.datasourceCardTitle, description: dashboardText.datasourceCardDescription, url: 'data-sources' },
|
||||
{ type: 'workflow', title: dashboardText.workflowCardTitle, description: dashboardText.workflowCardDescription, url: 'workflows' },
|
||||
{ type: 'template', title: dashboardText.templateCardTitle, description: dashboardText.templateCardDescription, url: '#' }
|
||||
];
|
||||
|
||||
|
||||
const env = Cypress.env('environment');
|
||||
|
||||
// Filter cards based on environment
|
||||
const cardsToTest = cardTypes.filter(cardType => {
|
||||
if (env === 'Enterprise') return ['app', 'datasource', 'workflow'].includes(cardType.type);
|
||||
if (env === 'Cloud') return ['add', 'datasource', 'template'].includes(cardType.type);
|
||||
return false;
|
||||
});
|
||||
|
||||
cardsToTest.forEach(cardType => {
|
||||
cy.get(dashboardSelector.widgetCardName(cardType.type)).within(() => {
|
||||
cy.get(dashboardSelector.homePageIcon(cardType.type)).should("be.visible");
|
||||
cy.get(dashboardSelector.widgetCardTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
cardType.title
|
||||
);
|
||||
cy.get(dashboardSelector.widgetCardDescription).verifyVisibleElement(
|
||||
"have.text",
|
||||
cardType.description
|
||||
);
|
||||
|
||||
});
|
||||
cy.get(dashboardSelector.widgetCardName(cardType.type)).should("have.attr", "href").and("include", cardType.url);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("Should verify Home page accessibility for the specific role", () => {
|
||||
//Invite End-user
|
||||
cy.apiFullUserOnboarding(data.firstName, data.email);
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin(data.email);
|
||||
cy.visit("/");
|
||||
cy.get(commonSelectors.homePageIcon).should("not.exist");
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
|
||||
//Update role to Builder
|
||||
cy.apiUpdateUserRole(data.email, "builder");
|
||||
cy.apiLogout();
|
||||
|
||||
cy.apiLogin(data.email);
|
||||
cy.visit("/");
|
||||
cy.get(commonSelectors.homePageIcon).should("be.visible");
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { commonText, settingsText, workspaceSettingsText } from "Texts/common";
|
||||
import { fake } from "Fixtures/fake";
|
||||
import { licenseText } from "Texts/license";
|
||||
import { licenseSelectors } from "Selectors/license";
|
||||
import { usersSelector } from "Selectors/manageUsers";
|
||||
import {
|
||||
switchTabs,
|
||||
verifylicenseTab,
|
||||
verifySubTabs,
|
||||
verifyAccessTab,
|
||||
verifyDomainTab,
|
||||
verifyResourceLimit,
|
||||
verifyTooltip,
|
||||
} from "Support/utils/license";
|
||||
import * as common from "Support/utils/common";
|
||||
import { dashboardSelector } from "../../../../../constants/selectors/dashboard";
|
||||
import { navigateToEditUser } from "Support/utils/manageUsers";
|
||||
import {
|
||||
instanceSettingsSelector,
|
||||
whiteLabellingSelectors,
|
||||
} from "Selectors/eeCommon";
|
||||
import { workflowSelector } from "Selectors/workflows";
|
||||
import { commonEeSelectors } from "Selectors/eeCommon";
|
||||
|
||||
describe("License Page", () => {
|
||||
const data = {};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
});
|
||||
|
||||
it("Should verify license page elements with the basic plan ", () => {
|
||||
common.navigateToSettingPage();
|
||||
cy.get(licenseSelectors.listOfItems(licenseText.license)).click();
|
||||
|
||||
cy.contains(licenseText.licenseOverviewTitle).should("be.visible");
|
||||
cy.get(commonSelectors.breadcrumbPageTitle).should(
|
||||
"have.text",
|
||||
licenseText.license
|
||||
);
|
||||
cy.get(licenseSelectors.comparePlansText)
|
||||
.should("be.visible")
|
||||
.and("contain.text", licenseText.comparePlansText);
|
||||
|
||||
switchTabs(licenseText.licenseKeyLabel);
|
||||
verifylicenseTab();
|
||||
|
||||
switchTabs(licenseText.limitsTabTitle);
|
||||
verifySubTabs(
|
||||
licenseText.limitsTab.aiCreditsSubTab,
|
||||
licenseText.aiCreditsSubTab,
|
||||
{
|
||||
"Monthly recurring": "0/0",
|
||||
"Add on credits": "0/0",
|
||||
}
|
||||
);
|
||||
verifySubTabs(licenseText.limitsTab.appsSubTab, licenseText.appsSubTab, {
|
||||
"Number of Apps": "1/2",
|
||||
});
|
||||
verifySubTabs(
|
||||
licenseText.limitsTab.workspacesSubTab,
|
||||
licenseText.workspacesSubTab,
|
||||
{
|
||||
"Number of Workspaces": "1/1",
|
||||
}
|
||||
);
|
||||
verifySubTabs(licenseText.limitsTab.usersSubTab, licenseText.usersSubTab, {
|
||||
"Number of Total Users": "1/52",
|
||||
"Number of Builders": "1/2",
|
||||
"Number of End Users": "0/50",
|
||||
"Number of Super Admins": "1/1",
|
||||
});
|
||||
verifySubTabs(
|
||||
licenseText.limitsTab.workflowsSubTab,
|
||||
licenseText.workflowsSubTab,
|
||||
{
|
||||
"Number of Workflows": "0/2",
|
||||
}
|
||||
);
|
||||
verifySubTabs(
|
||||
licenseText.limitsTab.tablesSubTab,
|
||||
licenseText.tablesSubTab,
|
||||
{
|
||||
"Number of Tables": "Unlimited",
|
||||
}
|
||||
);
|
||||
|
||||
switchTabs(licenseText.accessTabTitle);
|
||||
verifyAccessTab();
|
||||
|
||||
switchTabs(licenseText.domainTabTitle);
|
||||
verifyDomainTab();
|
||||
});
|
||||
|
||||
it("Should verify banners and tooltips with the basic plan ", () => {
|
||||
cy.get(commonSelectors.workspaceName).click();
|
||||
|
||||
cy.get('[data-cy="workspace-count"]').should("be.visible");
|
||||
verifyResourceLimit("workspaces", "basic");
|
||||
|
||||
cy.get(dashboardSelector.homePageContent).click();
|
||||
verifyResourceLimit("apps", "basic");
|
||||
|
||||
// cy.get(workflowSelector.globalWorkFlowsIcon).click();
|
||||
// verifyResourceLimit("workflow", "basic");
|
||||
|
||||
common.navigateToManageUsers();
|
||||
cy.get(usersSelector.buttonAddUsers).click();
|
||||
verifyResourceLimit("builders", "basic", "usage");
|
||||
|
||||
cy.reload();
|
||||
|
||||
common.navigateToManageGroups();
|
||||
cy.get('[data-cy="create-new-group-button"]').should("be.disabled");
|
||||
verifyTooltip(
|
||||
'[data-cy="create-new-group-button"]',
|
||||
"Custom groups are available only in paid plans",
|
||||
true
|
||||
);
|
||||
|
||||
verifyResourceLimit(
|
||||
"custom-groups",
|
||||
"basic",
|
||||
"custom-groups",
|
||||
"Custom groups & permissions are paid features"
|
||||
);
|
||||
|
||||
cy.get(
|
||||
licenseSelectors.listOfItems(workspaceSettingsText.customStylesListItem)
|
||||
).click();
|
||||
cy.get('[data-cy="modal-close"]').click();
|
||||
cy.get(licenseSelectors.paidFeatureButton).should("be.visible");
|
||||
|
||||
cy.get(
|
||||
licenseSelectors.listOfItems(
|
||||
workspaceSettingsText.configureGitSyncListItem
|
||||
)
|
||||
).click();
|
||||
cy.get(licenseSelectors.paidFeatureButton).should("be.visible");
|
||||
|
||||
cy.get(
|
||||
licenseSelectors.listOfItems(workspaceSettingsText.themesListItem)
|
||||
).click();
|
||||
verifyResourceLimit(
|
||||
"custom-themes",
|
||||
"basic",
|
||||
"custom-themes",
|
||||
"Custom themes is our paid feature. Upgrade to a paid plan to add and customize themes."
|
||||
);
|
||||
|
||||
common.navigateToSettingPage();
|
||||
cy.get(licenseSelectors.listOfItems(settingsText.allUsersListItem)).click({
|
||||
force: true,
|
||||
});
|
||||
navigateToEditUser(commonText.email);
|
||||
verifyResourceLimit(
|
||||
"edit-user",
|
||||
"basic",
|
||||
"edit-user",
|
||||
"Edit user details with our paid plans. For more,"
|
||||
);
|
||||
|
||||
cy.reload();
|
||||
cy.get(
|
||||
licenseSelectors.listOfItems(settingsText.manageInstanceSettingsListItem)
|
||||
).click();
|
||||
cy.get(licenseSelectors.paidFeatureButton).should("be.visible");
|
||||
cy.get(instanceSettingsSelector.allowWorkspaceToggle).should("be.disabled");
|
||||
cy.get(
|
||||
licenseSelectors.listOfItems(settingsText.whiteLabellingListItem)
|
||||
).click();
|
||||
cy.get(licenseSelectors.paidFeatureButton).should("be.visible");
|
||||
cy.get(whiteLabellingSelectors.appLogoInput).should("be.disabled");
|
||||
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).click();
|
||||
verifyTooltip(
|
||||
licenseSelectors.listOfItems("staging"),
|
||||
"Multi-environments are available only in paid plans",
|
||||
true
|
||||
);
|
||||
verifyTooltip(
|
||||
licenseSelectors.listOfItems("production"),
|
||||
"Multi-environments are available only in paid plans",
|
||||
true
|
||||
);
|
||||
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
cy.get(commonSelectors.settingsIcon).click();
|
||||
verifyTooltip(
|
||||
commonEeSelectors.auditLogIcon,
|
||||
"Audit logs are available only in paid plans",
|
||||
true
|
||||
);
|
||||
|
||||
// cy.apiCreateApp(`${fake.companyName}-license-App`);
|
||||
// cy.openApp();
|
||||
|
||||
// cy.get('[data-cy="list-current-env-name"]').click();
|
||||
// cy.get('[data-cy="env-name-list"]')
|
||||
// .eq(1)
|
||||
// .within(() => {
|
||||
// verifyTooltip(
|
||||
// '[data-cy="env-name-dropdown"]',
|
||||
// "Multi-environments are available only in paid plans"
|
||||
// );
|
||||
// });
|
||||
|
||||
// cy.get('[data-cy="env-name-list"]')
|
||||
// .eq(2)
|
||||
// .within(() => {
|
||||
// verifyTooltip(
|
||||
// '[data-cy="env-name-dropdown"]',
|
||||
// "Multi-environments are available only in paid plans"
|
||||
// );
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { commonText } from "Texts/common";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { onboardingSelectors } from "Selectors/onboarding";
|
||||
import { onboardingText } from "Texts/onboarding";
|
||||
import { logout } from "Support/utils/common";
|
||||
import {
|
||||
bannerElementsVerification,
|
||||
onboardingStepOne,
|
||||
onboardingStepTwo,
|
||||
onboardingStepThree,
|
||||
onboardingStepTwo,
|
||||
} from "Support/utils/onboarding";
|
||||
import { updateLicense } from "Support/utils/platform/eeCommon";
|
||||
import { commonText } from "Texts/common";
|
||||
import { onboardingText } from "Texts/onboarding";
|
||||
|
||||
describe("Self host onboarding", () => {
|
||||
const envVar = Cypress.env("environment");
|
||||
|
|
@ -82,21 +82,10 @@ describe("Self host onboarding", () => {
|
|||
cy.get(check.selector).verifyVisibleElement("have.text", check.text);
|
||||
});
|
||||
|
||||
cy.ifEnv("Community", () => {
|
||||
cy.get(commonSelectors.signUpTermsHelperText).should(($el) => {
|
||||
expect($el.contents().first().text().trim()).to.eq(
|
||||
// commonText.selfHostSignUpTermsHelperText
|
||||
"By signing up you are agreeing to the"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get(commonSelectors.signUpTermsHelperText).should(($el) => {
|
||||
expect($el.contents().first().text().trim()).to.eq(
|
||||
"By signing up you are agreeing to the"
|
||||
);
|
||||
});
|
||||
cy.get(commonSelectors.signUpTermsHelperText).should(($el) => {
|
||||
expect($el.contents().first().text().trim()).to.eq(
|
||||
"By signing up you are agreeing to the"
|
||||
);
|
||||
});
|
||||
|
||||
const links = [
|
||||
|
|
@ -272,8 +261,9 @@ describe("Self host onboarding", () => {
|
|||
onboardingStepThree();
|
||||
});
|
||||
|
||||
// cy.get(commonSelectors.skipbutton).click();
|
||||
cy.backToApps();
|
||||
cy.wait(2000);
|
||||
cy.get(commonWidgetSelector.previewButton).eq(1).should("be.visible");
|
||||
cy.go("back");
|
||||
|
||||
logout();
|
||||
cy.appUILogin();
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@ import { fake } from "Fixtures/fake";
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { postgreSqlSelector } from "Selectors/postgreSql";
|
||||
import { postgreSqlText } from "Texts/postgreSql";
|
||||
import { deleteDatasource } from "Support/utils/dataSource";
|
||||
import { deleteDatasource} from "Support/utils/dataSource";
|
||||
import { dataSourceSelector } from "Selectors/dataSource";
|
||||
import { workflowsText } from "Texts/workflows";
|
||||
import { workflowSelector } from "Selectors/workflows";
|
||||
|
||||
import {
|
||||
enterJsonInputInStartNode,
|
||||
deleteAppandWorkflowAfterExecution,
|
||||
verifyPreviewOutputText,
|
||||
verifyTextInResponseOutputLimited,
|
||||
navigateBackToWorkflowsDashboard,
|
||||
} from "Support/utils/workFlows";
|
||||
|
||||
const data = {};
|
||||
|
|
@ -19,15 +19,15 @@ describe("Workflows in apps", () => {
|
|||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.wfName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.appName = `${data.wfName}-wf-app`;
|
||||
data.workflowName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.appName = `${data.workflowName}-wf-app`;
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Creating workflows with runjs and validating execution in apps", () => {
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.createWorkflowApp(data.workflowName);
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(workflowsText.runjsNodeLabel);
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ describe("Workflows in apps", () => {
|
|||
cy.apiCreateApp(data.appName);
|
||||
cy.openApp();
|
||||
|
||||
cy.addWorkflowInApp(data.wfName);
|
||||
cy.addWorkflowInApp(data.workflowName);
|
||||
|
||||
cy.get(dataSourceSelector.queryPreviewButton).click();
|
||||
|
||||
|
|
@ -59,19 +59,19 @@ describe("Workflows in apps", () => {
|
|||
|
||||
// cy.verifyToastMessage(
|
||||
// commonSelectors.toastMessage,
|
||||
// `Query (${data.dsName}) completed.`
|
||||
// `Query (${data.dataSourceName}) completed.`
|
||||
// );
|
||||
|
||||
deleteAppandWorkflowAfterExecution(data.wfName, data.appName);
|
||||
cy.apiDeleteApp();
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
});
|
||||
|
||||
it("Creating workflows with postgres and validating execution in apps", () => {
|
||||
const dsName = `cypress-${data.dataSourceName}-manual-pgsql`;
|
||||
const dataSourceName = `cypress-${data.dataSourceName}-manual-pgsql`;
|
||||
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
dsName,
|
||||
dataSourceName,
|
||||
"postgresql",
|
||||
[
|
||||
{ key: "connection_type", value: "manual", encrypted: false },
|
||||
|
|
@ -94,7 +94,7 @@ describe("Workflows in apps", () => {
|
|||
]
|
||||
);
|
||||
|
||||
cy.get(dataSourceSelector.dataSourceNameButton(dsName))
|
||||
cy.get(dataSourceSelector.dataSourceNameButton(dataSourceName))
|
||||
.should("be.visible")
|
||||
.click();
|
||||
cy.get(postgreSqlSelector.buttonTestConnection).click();
|
||||
|
|
@ -103,9 +103,10 @@ describe("Workflows in apps", () => {
|
|||
}).should("have.text", postgreSqlText.labelConnectionVerified);
|
||||
cy.reload();
|
||||
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.apiCreateWorkflow(data.workflowName)
|
||||
cy.openWorkflow();
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(dsName);
|
||||
cy.connectDataSourceNode(dataSourceName);
|
||||
|
||||
cy.get(workflowSelector.nodeName(workflowsText.postgresqlNodeName)).click({
|
||||
force: true,
|
||||
|
|
@ -127,7 +128,7 @@ describe("Workflows in apps", () => {
|
|||
cy.apiCreateApp(data.appName);
|
||||
cy.openApp();
|
||||
|
||||
cy.addWorkflowInApp(data.wfName);
|
||||
cy.addWorkflowInApp(data.workflowName);
|
||||
|
||||
cy.get(dataSourceSelector.queryPreviewButton).click();
|
||||
|
||||
|
|
@ -135,10 +136,10 @@ describe("Workflows in apps", () => {
|
|||
|
||||
// cy.verifyToastMessage(
|
||||
// commonSelectors.toastMessage,
|
||||
// `Query (${data.dsName}) completed.`
|
||||
// `Query (${data.dataSourceName}) completed.`
|
||||
// );
|
||||
deleteAppandWorkflowAfterExecution(data.wfName, data.appName);
|
||||
|
||||
deleteDatasource(`cypress-${data.dataSourceName}-manual-pgsql`);
|
||||
cy.apiDeleteApp();
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
cy.apiDeleteGDS(`cypress-${data.dataSourceName}-manual-pgsql`);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { fake } from "Fixtures/fake";
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { postgreSqlSelector } from "Selectors/postgreSql";
|
||||
import { postgreSqlText } from "Texts/postgreSql";
|
||||
import { deleteWorkflowAndDS } from "Support/utils/dataSource";
|
||||
import { deleteWorkflowAndDS, deleteDatasource } from "Support/utils/dataSource";
|
||||
import { dataSourceSelector } from "Selectors/dataSource";
|
||||
import { harperDbText } from "Texts/harperDb";
|
||||
import { workflowsText } from "Texts/workflows";
|
||||
|
|
@ -14,6 +14,7 @@ import {
|
|||
import {
|
||||
enterJsonInputInStartNode,
|
||||
verifyTextInResponseOutputLimited,
|
||||
navigateBackToWorkflowsDashboard,
|
||||
} from "Support/utils/workFlows";
|
||||
|
||||
const data = {};
|
||||
|
|
@ -22,14 +23,14 @@ describe("Workflows with Datasource", () => {
|
|||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.wfName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.workflowName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("RunJS workflow - execute and validate", () => {
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.createWorkflowApp(data.workflowName);
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(workflowsText.runjsNodeLabel);
|
||||
|
||||
|
|
@ -50,16 +51,16 @@ describe("Workflows with Datasource", () => {
|
|||
);
|
||||
cy.verifyTextInResponseOutput(workflowsText.responseNodeExpectedValueText);
|
||||
|
||||
cy.deleteWorkflow(data.wfName);
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
});
|
||||
|
||||
it("Postgres workflow - execute and validate", () => {
|
||||
const dsName = `cypress-${data.dataSourceName}-manual-pgsql`;
|
||||
const dataSourceName = `cypress-${data.dataSourceName}-manual-pgsql`;
|
||||
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
dsName,
|
||||
dataSourceName,
|
||||
"postgresql",
|
||||
[
|
||||
{ key: "connection_type", value: "manual", encrypted: false },
|
||||
|
|
@ -82,7 +83,7 @@ describe("Workflows with Datasource", () => {
|
|||
]
|
||||
);
|
||||
|
||||
cy.get(dataSourceSelector.dataSourceNameButton(dsName))
|
||||
cy.get(dataSourceSelector.dataSourceNameButton(dataSourceName))
|
||||
.should("be.visible")
|
||||
.click();
|
||||
cy.get(postgreSqlSelector.buttonTestConnection).click();
|
||||
|
|
@ -91,9 +92,10 @@ describe("Workflows with Datasource", () => {
|
|||
}).should("have.text", postgreSqlText.labelConnectionVerified);
|
||||
cy.reload();
|
||||
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.apiCreateWorkflow(data.workflowName)
|
||||
cy.openWorkflow();
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(dsName);
|
||||
cy.connectDataSourceNode(dataSourceName);
|
||||
|
||||
cy.get(workflowSelector.nodeName(workflowsText.postgresqlNodeName)).click({
|
||||
force: true,
|
||||
|
|
@ -112,15 +114,17 @@ describe("Workflows with Datasource", () => {
|
|||
);
|
||||
verifyTextInResponseOutputLimited(workflowsText.postgresExpectedValue);
|
||||
|
||||
deleteWorkflowAndDS(data.wfName, dsName);
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
|
||||
cy.apiDeleteGDS(dataSourceName);
|
||||
});
|
||||
|
||||
it("REST API workflow - execute and validate", () => {
|
||||
const dsName = `cypress-${data.dataSourceName}-restapi`;
|
||||
const dataSourceName = `cypress-${data.dataSourceName}-restapi`;
|
||||
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
dsName,
|
||||
dataSourceName,
|
||||
"restapi",
|
||||
[
|
||||
{ key: "url", value: "https://jsonplaceholder.typicode.com" },
|
||||
|
|
@ -152,9 +156,10 @@ describe("Workflows with Datasource", () => {
|
|||
]
|
||||
);
|
||||
cy.reload();
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.apiCreateWorkflow(data.workflowName)
|
||||
cy.openWorkflow();
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(dsName);
|
||||
cy.connectDataSourceNode(dataSourceName);
|
||||
|
||||
cy.get(workflowSelector.nodeName(workflowsText.restapiNodeName)).click({
|
||||
force: true,
|
||||
|
|
@ -174,40 +179,41 @@ describe("Workflows with Datasource", () => {
|
|||
);
|
||||
cy.verifyTextInResponseOutput(workflowsText.restApiExpectedValue);
|
||||
|
||||
deleteWorkflowAndDS(data.wfName, dsName);
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
|
||||
cy.apiDeleteGDS(dataSourceName);
|
||||
});
|
||||
|
||||
it("HarperDB workflow - execute and validate", () => {
|
||||
const dsName = `cypress-${data.dataSourceName}-harperdb`;
|
||||
const dataSourceName = `cypress-${data.dataSourceName}-harperdb`;
|
||||
const Host = Cypress.env("harperdb_host");
|
||||
const Port = Cypress.env("harperdb_port");
|
||||
const Username = Cypress.env("harperdb_username");
|
||||
const Password = Cypress.env("harperdb_password");
|
||||
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
cy.installMarketplacePlugin(workflowsText.harperDbPluginName);
|
||||
cy.installMarketplacePlugin("HarperDB");
|
||||
|
||||
selectAndAddDataSource(
|
||||
"databases",
|
||||
harperDbText.harperDb,
|
||||
data.dataSourceName
|
||||
);
|
||||
selectAndAddDataSource("databases", harperDbText.harperDb, data.dataSourceName);
|
||||
|
||||
fillDataSourceTextField(
|
||||
harperDbText.hostLabel,
|
||||
harperDbText.hostInputPlaceholder,
|
||||
Host
|
||||
);
|
||||
|
||||
fillDataSourceTextField(
|
||||
harperDbText.portLabel,
|
||||
harperDbText.portPlaceholder,
|
||||
Port
|
||||
);
|
||||
|
||||
fillDataSourceTextField(
|
||||
harperDbText.userNameLabel,
|
||||
harperDbText.userNamePlaceholder,
|
||||
Username
|
||||
);
|
||||
|
||||
fillDataSourceTextField(
|
||||
harperDbText.passwordlabel,
|
||||
harperDbText.passwordPlaceholder,
|
||||
|
|
@ -228,9 +234,10 @@ describe("Workflows with Datasource", () => {
|
|||
postgreSqlText.toastDSSaved
|
||||
);
|
||||
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.apiCreateWorkflow(data.workflowName)
|
||||
cy.openWorkflow();
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(dsName);
|
||||
cy.connectDataSourceNode(dataSourceName);
|
||||
|
||||
cy.get(workflowSelector.nodeName(workflowsText.harperdbNodeName)).click({
|
||||
force: true,
|
||||
|
|
@ -256,7 +263,8 @@ describe("Workflows with Datasource", () => {
|
|||
workflowsText.harperDbResponseNodeQuery
|
||||
);
|
||||
cy.verifyTextInResponseOutput(workflowsText.harperDbExpectedValue);
|
||||
|
||||
deleteWorkflowAndDS(data.wfName, dsName);
|
||||
navigateBackToWorkflowsDashboard();
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
deleteDatasource(dataSourceName);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { fake } from "Fixtures/fake";
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { postgreSqlSelector } from "Selectors/postgreSql";
|
||||
import { postgreSqlText } from "Texts/postgreSql";
|
||||
import { deleteWorkflowAndDS } from "Support/utils/dataSource";
|
||||
import { deleteWorkflowAndDS,deleteDatasource } from "Support/utils/dataSource";
|
||||
import { dataSourceSelector } from "Selectors/dataSource";
|
||||
import { workflowsText } from "Texts/workflows";
|
||||
import { workflowSelector } from "Selectors/workflows";
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
enterJsonInputInStartNode,
|
||||
importWorkflowApp,
|
||||
verifyTextInResponseOutputLimited,
|
||||
navigateBackToWorkflowsDashboard
|
||||
} from "Support/utils/workFlows";
|
||||
|
||||
const data = {};
|
||||
|
|
@ -19,16 +20,16 @@ describe("Workflows Export/Import Sanity", () => {
|
|||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.wfName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.workflowName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("RunJS workflow - execute, export/import, re-execute", () => {
|
||||
const wfName = `${data.wfName}-runjs`;
|
||||
const workflowName = `${data.workflowName}-runjs`;
|
||||
|
||||
cy.createWorkflowApp(wfName);
|
||||
cy.createWorkflowApp(workflowName);
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(workflowsText.runjsNodeLabel);
|
||||
|
||||
|
|
@ -49,23 +50,22 @@ describe("Workflows Export/Import Sanity", () => {
|
|||
);
|
||||
cy.verifyTextInResponseOutput(workflowsText.responseNodeExpectedValueText);
|
||||
|
||||
cy.exportWorkflowApp(wfName);
|
||||
|
||||
importWorkflowApp(wfName, workflowsText.exportFixturePath);
|
||||
cy.exportWorkflowApp(workflowName);
|
||||
cy.apiDeleteWorkflow(workflowName);
|
||||
importWorkflowApp(workflowName, workflowsText.exportFixturePath);
|
||||
cy.verifyTextInResponseOutput(workflowsText.responseNodeExpectedValueText);
|
||||
|
||||
cy.deleteWorkflow(wfName);
|
||||
cy.apiDeleteWorkflow(workflowName);
|
||||
cy.task("deleteFile", workflowsText.exportFixturePath);
|
||||
});
|
||||
|
||||
it("Postgres workflow - execute, export/import, re-execute", () => {
|
||||
const wfName = `${data.wfName}-pg`;
|
||||
const dsName = `cypress-${data.dataSourceName}-manual-pgsql`;
|
||||
const workflowName = `${data.workflowName}-pg`;
|
||||
const dataSourceName = `cypress-${data.dataSourceName}-manual-pgsql`;
|
||||
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
dsName,
|
||||
dataSourceName,
|
||||
"postgresql",
|
||||
[
|
||||
{ key: "connection_type", value: "manual", encrypted: false },
|
||||
|
|
@ -87,7 +87,7 @@ describe("Workflows Export/Import Sanity", () => {
|
|||
]
|
||||
);
|
||||
|
||||
cy.get(dataSourceSelector.dataSourceNameButton(dsName))
|
||||
cy.get(dataSourceSelector.dataSourceNameButton(dataSourceName))
|
||||
.should("be.visible")
|
||||
.click();
|
||||
|
||||
|
|
@ -98,9 +98,10 @@ describe("Workflows Export/Import Sanity", () => {
|
|||
|
||||
cy.reload();
|
||||
|
||||
cy.createWorkflowApp(wfName);
|
||||
cy.apiCreateWorkflow(data.workflowName)
|
||||
cy.openWorkflow();
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(dsName);
|
||||
cy.connectDataSourceNode(dataSourceName);
|
||||
|
||||
cy.get(workflowSelector.nodeName(workflowsText.postgresqlNodeName)).click({
|
||||
force: true,
|
||||
|
|
@ -120,12 +121,14 @@ describe("Workflows Export/Import Sanity", () => {
|
|||
);
|
||||
verifyTextInResponseOutputLimited(workflowsText.postgresExpectedValue);
|
||||
|
||||
cy.exportWorkflowApp(wfName);
|
||||
|
||||
importWorkflowApp(wfName, workflowsText.exportFixturePath);
|
||||
cy.exportWorkflowApp(workflowName);
|
||||
cy.apiDeleteWorkflow(workflowName);
|
||||
importWorkflowApp(workflowName, workflowsText.exportFixturePath);
|
||||
verifyTextInResponseOutputLimited(workflowsText.postgresExpectedValue);
|
||||
|
||||
cy.apiDeleteWorkflow(workflowName);
|
||||
|
||||
deleteWorkflowAndDS(wfName, dsName);
|
||||
cy.apiDeleteGDS(dataSourceName);
|
||||
cy.task("deleteFile", workflowsText.exportFixturePath);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { workflowSelector } from "Selectors/workflows";
|
|||
import {
|
||||
enterJsonInputInStartNode,
|
||||
revealWorkflowToken,
|
||||
navigateBackToWorkflowsDashboard
|
||||
} from "Support/utils/workFlows";
|
||||
|
||||
const data = {};
|
||||
|
|
@ -13,14 +14,14 @@ describe("Workflows with Webhooks", () => {
|
|||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.wfName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.workflowName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Creating workflows with runjs, triggering via webhook, and validating execution", () => {
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.createWorkflowApp(data.workflowName);
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(workflowsText.runjsNodeLabel);
|
||||
|
||||
|
|
@ -64,6 +65,6 @@ describe("Workflows with Webhooks", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
cy.deleteWorkflow(data.wfName);
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,17 +15,17 @@ describe("Workflows features", () => {
|
|||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.wfName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.appName = `${data.wfName}-wf-app`;
|
||||
data.childWFName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.parentWFName = `${data.wfName}-wf-app`;
|
||||
data.workflowName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.appName = `${data.workflowName}-wf-app`;
|
||||
data.childWorkflowName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.parentWorkflowName = `${data.workflowName}-wf-app`;
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Creating workflow with long string input and validating execution", () => {
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.createWorkflowApp(data.workflowName);
|
||||
enterJsonInputInStartNode(workflowsText.longStringJsonText);
|
||||
cy.connectDataSourceNode(workflowsText.runjsNodeLabel);
|
||||
|
||||
|
|
@ -45,11 +45,13 @@ describe("Workflows features", () => {
|
|||
workflowsText.responseNodeQuery
|
||||
);
|
||||
cy.verifyTextInResponseOutput(workflowsText.longStringJsonText);
|
||||
cy.deleteWorkflow(data.wfName);
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
});
|
||||
|
||||
it("Creating workflow with Node Preview Validation and execution", () => {
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.apiCreateWorkflow(data.workflowName)
|
||||
cy.openWorkflow();
|
||||
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(workflowsText.runjsNodeLabel);
|
||||
|
||||
|
|
@ -71,12 +73,14 @@ describe("Workflows features", () => {
|
|||
workflowsText.responseNodeQuery
|
||||
);
|
||||
cy.verifyTextInResponseOutput(workflowsText.jsonValuePlaceholder);
|
||||
cy.deleteWorkflow(data.wfName);
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
});
|
||||
|
||||
// Need to run after bug fixes
|
||||
it("Creating workflow inside Workflow and validating execution", () => {
|
||||
cy.createWorkflowApp(data.childWFName);
|
||||
cy.apiCreateWorkflow(data.childWorkflowName)
|
||||
cy.openWorkflow();
|
||||
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(workflowsText.runjsNodeLabel);
|
||||
|
||||
|
|
@ -97,9 +101,8 @@ describe("Workflows features", () => {
|
|||
);
|
||||
cy.verifyTextInResponseOutput(workflowsText.responseNodeExpectedValueText);
|
||||
|
||||
navigateBackToWorkflowsDashboard();
|
||||
|
||||
cy.createWorkflowApp(data.parentWFName);
|
||||
cy.apiCreateWorkflow(data.parentWorkflowName)
|
||||
cy.openWorkflow();
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(workflowsText.workflowNodeLabel);
|
||||
|
||||
|
|
@ -109,9 +112,9 @@ describe("Workflows features", () => {
|
|||
|
||||
cy.get('input[id^="react-select-"]')
|
||||
.eq(1)
|
||||
.type(data.childWFName, { force: true });
|
||||
.type(data.childWorkflowName, { force: true });
|
||||
cy.get(".react-select__option")
|
||||
.contains(data.childWFName)
|
||||
.contains(data.childWorkflowName)
|
||||
.click({ force: true });
|
||||
|
||||
cy.get("body").click(50, 50);
|
||||
|
|
@ -122,12 +125,13 @@ describe("Workflows features", () => {
|
|||
workflowsText.workflowResponseNodeQuery
|
||||
);
|
||||
// cy.verifyTextInResponseOutput(workflowsText.responseNodeExpectedValueText);
|
||||
cy.deleteWorkflow(data.childWFName);
|
||||
cy.deleteWorkflowfromDashboard(data.parentWFName);
|
||||
cy.apiDeleteWorkflow(data.childWorkflowName);
|
||||
cy.apiDeleteWorkflow(data.parentWorkflowName);
|
||||
});
|
||||
|
||||
it("Creating workflow with large datasets and validating execution", () => {
|
||||
cy.createWorkflowApp(data.wfName);
|
||||
cy.apiCreateWorkflow(data.workflowName)
|
||||
cy.openWorkflow();
|
||||
enterJsonInputInStartNode();
|
||||
cy.connectDataSourceNode(workflowsText.runjsNodeLabel);
|
||||
|
||||
|
|
@ -152,7 +156,6 @@ describe("Workflows features", () => {
|
|||
verifyTextInResponseOutputLimited(
|
||||
workflowsText.responseNodeExpectedValueTextForLargeDataset
|
||||
);
|
||||
|
||||
cy.deleteWorkflow(data.wfName);
|
||||
cy.apiDeleteWorkflow(data.workflowName);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
24
cypress-tests/cypress/fixtures/license/license.json
Normal file
24
cypress-tests/cypress/fixtures/license/license.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"basic": {
|
||||
"planName": "Basic",
|
||||
"Apps": 2,
|
||||
"Workspaces": 1,
|
||||
"Total Users": 52,
|
||||
"Builders": 2,
|
||||
"End Users": 50,
|
||||
"Super Admins": 1,
|
||||
"Workflows": 2,
|
||||
"Tables": "Unlimited"
|
||||
},
|
||||
"enterprise": {
|
||||
"planName": "Enterprise",
|
||||
"Apps": 3,
|
||||
"Workspaces": 2,
|
||||
"Total Users": 3,
|
||||
"Builders": 2,
|
||||
"End Users": 1,
|
||||
"Super Admins": 1,
|
||||
"Workflows": 2,
|
||||
"Tables": 2
|
||||
}
|
||||
}
|
||||
|
|
@ -14,11 +14,14 @@
|
|||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import "cypress-real-events/support";
|
||||
import "../commands/commands";
|
||||
import "../commands/apiCommands";
|
||||
import "../commands/workflowsApiCommands";
|
||||
import '../commands/workflowCommands';
|
||||
import '../commands/platform/platformApiCommands';
|
||||
import "@cypress/code-coverage/support";
|
||||
import "cypress-real-events";
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { commonWidgetSelector } from "Selectors/common";
|
||||
import { appPromote } from "Support/utils/platform/multiEnv";
|
||||
|
||||
const slugValidations = [
|
||||
|
|
@ -76,16 +76,20 @@ export const setUpSlug = (slug) => {
|
|||
cy.get(commonWidgetSelector.modalCloseButton).click();
|
||||
};
|
||||
|
||||
export const setupAppWithSlug = (appName, slug) => {
|
||||
export const setupAppWithSlug = (appName, slug, appType = 'private') => {
|
||||
const defaultLayout = {
|
||||
desktop: { top: 90, left: 9, width: 6, height: 40 },
|
||||
mobile: { top: 90, left: 9, width: 6, height: 40 },
|
||||
};
|
||||
cy.apiCreateApp(appName);
|
||||
cy.apiAddComponentToApp(appName, "text1");
|
||||
cy.apiAddComponentToApp(appName, appType, defaultLayout, "Text", appType);
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.openApp(
|
||||
"",
|
||||
Cypress.env("workspaceId"),
|
||||
Cypress.env("appId"),
|
||||
commonWidgetSelector.draggableWidget("text1")
|
||||
commonWidgetSelector.draggableWidget(appType)
|
||||
);
|
||||
appPromote("development", "production");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
import { profileSelector } from "Selectors/profile";
|
||||
import { appPromote } from "Support/utils/platform/multiEnv";
|
||||
import { commonText, path } from "Texts/common";
|
||||
import { commonEeSelectors } from "Selectors/eeCommon";
|
||||
|
||||
export const navigateToProfile = () => {
|
||||
cy.get(commonSelectors.settingsIcon).click();
|
||||
|
|
@ -101,15 +102,17 @@ export const navigateToAppEditor = (appName) => {
|
|||
};
|
||||
|
||||
export const viewAppCardOptions = (appName) => {
|
||||
cy.get(".homepage-app-card .home-app-card-header .menu-ico").then(($el) => {
|
||||
$el[0].style.setProperty("visibility", "visible", "important");
|
||||
});
|
||||
cy.contains('.homepage-app-card', appName).within(() => {
|
||||
cy.get('.home-app-card-header .menu-ico')
|
||||
.then(($el) => {
|
||||
$el[0].style.setProperty('visibility', 'visible', 'important');
|
||||
});
|
||||
|
||||
cy.get('[data-cy="app-card-menu-icon"]').click();
|
||||
cy.get('[data-cy="app-card-menu-icon"]').click();
|
||||
});
|
||||
};
|
||||
|
||||
export const viewFolderCardOptions = (folderName) => {
|
||||
cy.reloadAppForTheElement(folderName);
|
||||
cy.get(commonSelectors.folderListcard(folderName))
|
||||
.parent()
|
||||
.within(() => {
|
||||
|
|
@ -128,7 +131,7 @@ export const verifyModal = (title, buttonText, inputFiledSelector) => {
|
|||
cy.get(commonSelectors.buttonSelector(commonText.cancelButton))
|
||||
.should("be.visible")
|
||||
.and("have.text", commonText.cancelButton);
|
||||
cy.get(commonSelectors.buttonSelector(buttonText))
|
||||
cy.get(commonSelectors.buttonSelector(buttonText)).first()
|
||||
.should("be.visible")
|
||||
.and("have.text", buttonText);
|
||||
|
||||
|
|
@ -184,7 +187,6 @@ export const searchUser = (email) => {
|
|||
};
|
||||
|
||||
export const selectAppCardOption = (appName, appCardOption) => {
|
||||
cy.wait(1000);
|
||||
viewAppCardOptions(appName);
|
||||
cy.get(appCardOption).should("be.visible").click();
|
||||
};
|
||||
|
|
@ -251,3 +253,9 @@ export const fillInputField = (data) => {
|
|||
cy.get(inputSelector).type(`{selectall}{backspace}${value}`);
|
||||
});
|
||||
};
|
||||
|
||||
export const navigateToSettingPage = () => {
|
||||
cy.get(commonSelectors.settingsIcon).click();
|
||||
cy.get(commonEeSelectors.instanceSettingIcon).click();
|
||||
cy.get(commonSelectors.pageSectionHeader).should("be.visible");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { dashboardSelector } from "Selectors/dashboard";
|
||||
import { dashboardText } from "Texts/dashboard";
|
||||
import { commonText } from "Texts/common";
|
||||
import {
|
||||
viewAppCardOptions,
|
||||
verifyModal,
|
||||
closeModal,
|
||||
cancelModal,
|
||||
closeModal,
|
||||
verifyModal,
|
||||
viewAppCardOptions,
|
||||
} from "Support/utils/common";
|
||||
import { commonText } from "Texts/common";
|
||||
import { dashboardText } from "Texts/dashboard";
|
||||
|
||||
export const modifyAndVerifyAppCardIcon = (appName) => {
|
||||
var random = function (obj) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { postgreSqlSelector } from "Selectors/postgreSql";
|
||||
import { postgreSqlText } from "Texts/postgreSql";
|
||||
import { cyParamName } from "Selectors/common";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { commonSelectors, cyParamName } from "Selectors/common";
|
||||
import { dataSourceSelector } from "Selectors/dataSource";
|
||||
import { postgreSqlSelector } from "Selectors/postgreSql";
|
||||
import { navigateToAppEditor } from "Support/utils/common";
|
||||
import { verifyAppDelete } from "Support/utils/dashboard";
|
||||
import { postgreSqlText } from "Texts/postgreSql";
|
||||
|
||||
export const verifyCouldnotConnectWithAlert = (alertText) => {
|
||||
cy.get(postgreSqlSelector.connectionFailedText, {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
export const renameApp = (name) => {
|
||||
cy.get('[data-cy="edit-app-name-button"]').click();
|
||||
cy.get("[data-cy='app-name-input']").type(`{selectAll}{backspace}${name}`, { force: true });
|
||||
cy.get("[data-cy='rename-app']").click();
|
||||
};
|
||||
|
||||
export const verifyAppName = (name) => {
|
||||
cy.get('[data-cy="edit-app-name-button"]').should("have.text", name);
|
||||
}
|
||||
|
||||
export const verifyCurrentEnvironment = (envName) => {
|
||||
cy.get('[data-cy="list-current-env-name"]').should("have.text", envName);
|
||||
}
|
||||
|
||||
export const verifyCurrentVersion = (version) => {
|
||||
cy.get('[data-cy*="-current-version-text"]').should("have.text", version);
|
||||
}
|
||||
|
||||
export const addNewVersion = (newVersion, fromVersion) => {
|
||||
cy.get('[data-cy*="-current-version-text"]').click();
|
||||
cy.get('[data-cy="create-new-version-button"]').click();
|
||||
if (fromVersion) {
|
||||
cy.get('[data-cy="create-version-from-input-field"]').click();
|
||||
cy.contains('[id*="react-select"]', fromVersion).click();
|
||||
}
|
||||
cy.get('[data-cy="version-name-input-field"]').type(newVersion, { force: true });
|
||||
cy.get('[data-cy="create-new-version-button"]').click();
|
||||
cy.verifyToastMessage("Version Created");
|
||||
};
|
||||
|
||||
export const promoteEnv = () => {
|
||||
cy.get('[data-cy="promote-button"]').first().click();
|
||||
cy.get('[data-cy="promote-button"]').last().click();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// import { renameApp, verifyAppName, verifyCurrentEnvironment, verifyCurrentVersion, addNewVersion, promoteEnv } from 'cypress-tests/cypress/support/utils/editor/editorHeaderOperations';
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import {
|
||||
appVersionSelectors,
|
||||
exportAppModalSelectors,
|
||||
importSelectors,
|
||||
} from "Selectors/exportImport";
|
||||
import { exportAppModalText, appVersionText } from "Texts/exportImport";
|
||||
import { commonSelectors } from "Selectors/common";
|
||||
import { verifyModal, commonWidgetSelector } from "Support/utils/common";
|
||||
import { importText } from "Texts/exportImport";
|
||||
import { verifyModal } from "Support/utils/common";
|
||||
import { appVersionText, exportAppModalText, importText } from "Texts/exportImport";
|
||||
|
||||
export const verifyElementsOfExportModal = (
|
||||
currentVersionName,
|
||||
|
|
@ -137,3 +136,122 @@ export const importAndVerifyApp = (filePath, expectedToast) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const verifyImportModalElements = (expectedAppName) => {
|
||||
cy.get(importSelectors.importAppTitle).verifyVisibleElement("have.text", "Import app");
|
||||
cy.get(commonSelectors.appNameLabel).verifyVisibleElement("have.text", "App name");
|
||||
cy.get(commonSelectors.appNameInput)
|
||||
.should("be.visible")
|
||||
.and("have.value", expectedAppName);
|
||||
cy.get(commonSelectors.appNameInfoLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
"App name must be unique and max 50 characters"
|
||||
);
|
||||
cy.get(commonSelectors.cancelButton)
|
||||
.should("be.visible")
|
||||
.and("have.text", "Cancel");
|
||||
cy.get(commonSelectors.importAppButton).verifyVisibleElement("have.text", "Import app");
|
||||
};
|
||||
|
||||
export const setupDataSourceWithConstants = (dsEnv, password = Cypress.env("pg_password")) => {
|
||||
cy.apiUpdateDataSource("postgresql", dsEnv, {
|
||||
options: [{
|
||||
key: "password",
|
||||
value: password,
|
||||
encrypted: true,
|
||||
}],
|
||||
});
|
||||
};
|
||||
|
||||
export const validateExportedAppStructure = (
|
||||
appData,
|
||||
expectedAppName,
|
||||
options = {}
|
||||
) => {
|
||||
const {
|
||||
validateComponents = true,
|
||||
validateQueries = true,
|
||||
validateVersions = true,
|
||||
validateTooljetDB = true,
|
||||
expectedVersions = ["v1", "v2", "v3"],
|
||||
} = options;
|
||||
|
||||
// Validate the app name
|
||||
const appNameFromFile = appData.app[0].definition.appV2.name;
|
||||
expect(appNameFromFile).to.equal(expectedAppName);
|
||||
|
||||
// Validate the schema for the student table in tooljetdb
|
||||
if (validateTooljetDB) {
|
||||
const tooljetDatabase = appData.tooljet_database.find(
|
||||
(db) => db.table_name === "student"
|
||||
);
|
||||
expect(tooljetDatabase).to.exist;
|
||||
expect(tooljetDatabase.schema).to.exist;
|
||||
}
|
||||
|
||||
// Validate components
|
||||
if (validateComponents) {
|
||||
const components = appData.app[0].definition.appV2.components;
|
||||
|
||||
const text2Component = components.find(
|
||||
(component) => component.name === "text2"
|
||||
);
|
||||
expect(text2Component).to.exist;
|
||||
expect(text2Component.properties.text.value).to.equal(
|
||||
"{{constants.pageHeader}}"
|
||||
);
|
||||
|
||||
const textinput1 = components.find(
|
||||
(component) => component.name === "textinput1"
|
||||
);
|
||||
expect(textinput1).to.exist;
|
||||
expect(textinput1.properties.value.value).to.include("queries");
|
||||
|
||||
const textinput2 = components.find(
|
||||
(component) => component.name === "textinput2"
|
||||
);
|
||||
expect(textinput2).to.exist;
|
||||
expect(textinput2.properties.value.value).to.include("queries");
|
||||
|
||||
const textinput3 = components.find(
|
||||
(component) => component.name === "textinput3"
|
||||
);
|
||||
cy.log(JSON.stringify(appData.app[0].definition.appV2.appVersions));
|
||||
expect(textinput3).to.exist;
|
||||
expect(textinput3.properties.value.value).to.include("queries");
|
||||
}
|
||||
|
||||
// Validate the data queries
|
||||
if (validateQueries) {
|
||||
const dataQueries = appData.app[0].definition.appV2.dataQueries;
|
||||
|
||||
const postgresqlQuery = dataQueries.find(
|
||||
(query) => query.name === "postgresql1"
|
||||
);
|
||||
expect(postgresqlQuery).to.exist;
|
||||
expect(postgresqlQuery.options.query).to.include(
|
||||
"Select * from {{secrets.db_name}}"
|
||||
);
|
||||
|
||||
const restapiQuery = dataQueries.find((query) => query.name === "restapi1");
|
||||
expect(restapiQuery).to.exist;
|
||||
expect(restapiQuery.options.url).to.equal(
|
||||
"https://jsonplaceholder.typicode.com/users/1"
|
||||
);
|
||||
|
||||
const tooljetdbQuery = dataQueries.find(
|
||||
(query) => query.name === "tooljetdb1"
|
||||
);
|
||||
expect(tooljetdbQuery).to.exist;
|
||||
expect(tooljetdbQuery.options.operation).to.equal("list_rows");
|
||||
}
|
||||
|
||||
// Validate app versions
|
||||
if (validateVersions) {
|
||||
const appVersions = appData.app[0].definition.appV2.appVersions;
|
||||
expect(appVersions).to.exist;
|
||||
|
||||
const versionNames = appVersions.map((version) => version.name);
|
||||
expect(versionNames).to.include.members(expectedVersions);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
224
cypress-tests/cypress/support/utils/license.js
Normal file
224
cypress-tests/cypress/support/utils/license.js
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
import { licenseSelectors } from "Selectors/license";
|
||||
import { licenseText } from "Texts/license";
|
||||
|
||||
export const switchTabs = (tabTitle) => {
|
||||
cy.get(licenseSelectors.listOfItems(tabTitle)).should("be.visible").click();
|
||||
cy.get(licenseSelectors.tabTitle(tabTitle)).should("have.text", tabTitle);
|
||||
};
|
||||
|
||||
export const verifylicenseTab = () => {
|
||||
cy.get(licenseSelectors.label(licenseText.licenseKeyTab.licenseLabel)).should(
|
||||
"be.visible"
|
||||
);
|
||||
cy.get(licenseSelectors.licenseTextArea).should(
|
||||
"have.attr",
|
||||
"placeholder",
|
||||
licenseText.licenseKeyTab.enterLicenseKeyPlaceholder
|
||||
);
|
||||
};
|
||||
|
||||
export const verifySubTabs = (subTabName, subTabDataObj, valuesObj) => {
|
||||
const subTabData = Object.values(subTabDataObj);
|
||||
|
||||
cy.get(licenseSelectors.subTab(subTabName))
|
||||
.verifyVisibleElement("have.text", subTabName)
|
||||
.click();
|
||||
|
||||
subTabData.forEach((label) => {
|
||||
const displayLabel = /^Number of\s+/i.test(label)
|
||||
? label.replace(/^Number of\s+/i, "")
|
||||
: label;
|
||||
|
||||
cy.get(
|
||||
licenseSelectors.numberOfTextLabel(displayLabel)
|
||||
).verifyVisibleElement("have.text", label);
|
||||
|
||||
if (valuesObj && valuesObj[displayLabel] !== undefined) {
|
||||
cy.get(licenseSelectors.inputField(displayLabel)).verifyVisibleElement(
|
||||
"have.value",
|
||||
valuesObj[displayLabel]
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyAccessTab = () => {
|
||||
const accessTabLabels = Object.values(licenseText.accessTab);
|
||||
accessTabLabels.forEach((accessTabLabel) => {
|
||||
cy.get(licenseSelectors.label(accessTabLabel)).verifyVisibleElement(
|
||||
"have.text",
|
||||
accessTabLabel
|
||||
);
|
||||
cy.get(licenseSelectors.label(accessTabLabel))
|
||||
.next(licenseSelectors.circularToggleDisabledIcon)
|
||||
.should("be.visible");
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyDomainTab = () => {
|
||||
cy.get(licenseSelectors.warningIcon).should("be.visible");
|
||||
cy.get(licenseSelectors.noDomainLinkedLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
licenseText.domainTab.noDomainLinkedLabel
|
||||
);
|
||||
cy.get(licenseSelectors.noDomainInfoText).verifyVisibleElement(
|
||||
"have.text",
|
||||
licenseText.domainTab.noDomainInfoText
|
||||
);
|
||||
};
|
||||
|
||||
export const verifyTooltip = (
|
||||
selector,
|
||||
expectedTooltip,
|
||||
isDisabled = false
|
||||
) => {
|
||||
const hoverTarget = isDisabled
|
||||
? cy.get(selector).parent().trigger("mouseover", { force: true })
|
||||
: cy.get(selector).trigger("mouseover", { force: true });
|
||||
|
||||
cy.get(".tooltip", { timeout: 3000 })
|
||||
.should("be.visible")
|
||||
.and("contain.text", expectedTooltip);
|
||||
|
||||
hoverTarget.trigger("mouseout", { force: true });
|
||||
};
|
||||
|
||||
const normalizeText = (text) =>
|
||||
text
|
||||
.trim()
|
||||
.replace(/[\u00A0\s]+/g, " ")
|
||||
.replace(/[\u2018\u2019]/g, "'")
|
||||
.replace(/[\u201C\u201D]/g, '"');
|
||||
|
||||
const verifyLimitUI = (
|
||||
type,
|
||||
baseLabel,
|
||||
headingSelector,
|
||||
infoSelector,
|
||||
limit
|
||||
) => {
|
||||
cy.get(headingSelector)
|
||||
.invoke("text")
|
||||
.then((headingText) => {
|
||||
cy.log(`Heading: ${headingText}`);
|
||||
cy.get(infoSelector)
|
||||
.invoke("text")
|
||||
.then((infoText) => {
|
||||
cy.log(`Info: ${infoText}`);
|
||||
|
||||
const ratioMatch = headingText.match(/(\d+)\/(\d+)/);
|
||||
|
||||
if (ratioMatch) {
|
||||
const [_, currentCount, headingLimit] = ratioMatch.map(Number);
|
||||
cy.log(`Current ${type}: ${currentCount}`);
|
||||
|
||||
expect(headingLimit).to.equal(limit);
|
||||
expect(headingText).to.match(
|
||||
new RegExp(`${baseLabel}\\s+limit\\s+nearing`, "i")
|
||||
);
|
||||
expect(infoText).to.match(/nearing/i);
|
||||
} else {
|
||||
expect(headingText).to.match(
|
||||
new RegExp(`${baseLabel}\\s+limit\\s+reached`, "i")
|
||||
);
|
||||
expect(infoText).to.contain(
|
||||
`reached your limit for number of ${type}`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const verifyFeatureBanner = (cyPrefix, expectedHeading = null) => {
|
||||
const isSmallBanner = cyPrefix === "edit-user";
|
||||
const headingSelector = isSmallBanner
|
||||
? licenseSelectors.licenseBannerHeading
|
||||
: licenseSelectors.limitHeading(cyPrefix);
|
||||
const infoSelector = isSmallBanner
|
||||
? licenseSelectors.licenseBannerInfo
|
||||
: licenseSelectors.limitInfo(cyPrefix);
|
||||
|
||||
cy.get(headingSelector)
|
||||
.should("be.visible")
|
||||
.invoke("text")
|
||||
.then((headingText) => {
|
||||
const actual = normalizeText(headingText);
|
||||
if (expectedHeading) {
|
||||
const expected = normalizeText(expectedHeading);
|
||||
expect(actual).to.equal(expected);
|
||||
}
|
||||
});
|
||||
cy.get("body").then(($body) => {
|
||||
if ($body.find(infoSelector).length > 0) {
|
||||
cy.get(infoSelector).then(($el) => {
|
||||
const text = $el.text().trim();
|
||||
if ($el.is(":visible") && text) cy.log(`Feature banner info: ${text}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getLimitFromPlan = (planName, type) => {
|
||||
return cy.fixture("license/license.json").then((licenseData) => {
|
||||
const planKey = planName.toLowerCase();
|
||||
const plan = licenseData[planKey];
|
||||
expect(plan, `Plan "${planName}" should exist in license.json`).to.not.be
|
||||
.undefined;
|
||||
|
||||
const resourceKeyMap = {
|
||||
workspaces: "Workspaces",
|
||||
apps: "Apps",
|
||||
tables: "Tables",
|
||||
workflows: "Workflows",
|
||||
users: "Total Users",
|
||||
builders: "Builders",
|
||||
endusers: "End Users",
|
||||
superadmins: "Super Admins",
|
||||
};
|
||||
|
||||
const resourceKey = resourceKeyMap[type];
|
||||
if (!resourceKey) return cy.wrap(null);
|
||||
|
||||
const limit = plan[resourceKey];
|
||||
expect(limit, `Limit for "${resourceKey}" should exist in ${planName} plan`)
|
||||
.to.not.be.undefined;
|
||||
|
||||
cy.log(`Using limit from ${planName} plan: ${resourceKey} = ${limit}`);
|
||||
return cy.wrap(limit);
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyResourceLimit = (
|
||||
resourceType,
|
||||
limitOrPlan = null,
|
||||
dataCyPrefix = null,
|
||||
expectedHeading = null
|
||||
) => {
|
||||
const type = resourceType.toLowerCase().trim();
|
||||
const cyPrefix = dataCyPrefix || type;
|
||||
const typeCapitalized = type.charAt(0).toUpperCase() + type.slice(1);
|
||||
const baseLabel = typeCapitalized.slice(0, -1);
|
||||
const headingSelector = licenseSelectors.limitHeading(cyPrefix);
|
||||
const infoSelector = licenseSelectors.limitInfo(cyPrefix);
|
||||
|
||||
const isNumber = typeof limitOrPlan === "number";
|
||||
const planName = isNumber ? null : limitOrPlan;
|
||||
|
||||
if (isNumber) {
|
||||
return verifyLimitUI(
|
||||
type,
|
||||
baseLabel,
|
||||
headingSelector,
|
||||
infoSelector,
|
||||
limitOrPlan
|
||||
);
|
||||
}
|
||||
|
||||
getLimitFromPlan(planName, type).then((limit) => {
|
||||
if (limit === null) {
|
||||
verifyFeatureBanner(cyPrefix, expectedHeading);
|
||||
} else {
|
||||
verifyLimitUI(type, baseLabel, headingSelector, infoSelector, limit);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -10,631 +10,7 @@ import {
|
|||
} from "Support/utils/manageUsers";
|
||||
import { groupsText } from "Texts/manageGroups";
|
||||
|
||||
export const manageGroupsElements = () => {
|
||||
cy.get('[data-cy="page-title"]').should(($el) => {
|
||||
expect($el.contents().last().text().trim()).to.eq("Groups");
|
||||
});
|
||||
|
||||
cy.get('[data-cy="user-role-title"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"USER ROLE"
|
||||
);
|
||||
|
||||
// Admin Permissions
|
||||
// Admin List Item Verification
|
||||
cy.verifyElement(groupsSelector.adminListItem, "Admin");
|
||||
cy.verifyElement(groupsSelector.adminTitle, "Admin (1)");
|
||||
|
||||
// Group Permission Elements Verification
|
||||
cy.verifyElement(
|
||||
groupsSelector.createNewGroupButton,
|
||||
groupsText.createNewGroupButton
|
||||
);
|
||||
cy.verifyElement(groupsSelector.usersLink, groupsText.usersLink);
|
||||
cy.verifyElement(groupsSelector.permissionsLink, groupsText.permissionsLink);
|
||||
cy.verifyElement(groupsSelector.granularLink, "Granular access");
|
||||
|
||||
// Resource Verification
|
||||
cy.verifyElement(
|
||||
groupsSelector.textDefaultGroup,
|
||||
groupsText.textDefaultGroup
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.nameTableHeader,
|
||||
groupsText.userNameTableHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.emailTableHeader,
|
||||
groupsText.emailTableHeader
|
||||
);
|
||||
|
||||
// Permissions Page Navigation and Verifications
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
cy.get(groupsSelector.helperTextAdminAppAccess)
|
||||
.eq(0)
|
||||
.verifyVisibleElement("have.text", groupsText.adminAccessHelperText);
|
||||
|
||||
// Granular Access Verifications
|
||||
cy.verifyElement(groupsSelector.resourcesApps, groupsText.resourcesApps);
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionsTableHeader,
|
||||
groupsText.permissionsTableHeader
|
||||
);
|
||||
cy.get(groupsSelector.appsCreateCheck)
|
||||
.should("be.visible")
|
||||
.and("be.checked")
|
||||
.and("be.disabled");
|
||||
cy.verifyElement(groupsSelector.appsCreateLabel, groupsText.createLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appCreateHelperText,
|
||||
groupsText.appCreateHelperText
|
||||
);
|
||||
cy.get(groupsSelector.appsDeleteCheck)
|
||||
.should("be.visible")
|
||||
.and("be.checked")
|
||||
.and("be.disabled");
|
||||
cy.verifyElement(groupsSelector.appsDeleteLabel, groupsText.deleteLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appDeleteHelperText,
|
||||
groupsText.appDeleteHelperText
|
||||
);
|
||||
|
||||
// Folder Permissions
|
||||
cy.verifyElement(
|
||||
groupsSelector.resourcesFolders,
|
||||
groupsText.resourcesFolders
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.foldersCreateLabel,
|
||||
groupsText.folderCreateLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.foldersHelperText,
|
||||
groupsText.folderHelperText
|
||||
);
|
||||
cy.get(groupsSelector.foldersCreateCheck)
|
||||
.should("be.visible")
|
||||
.and("be.checked")
|
||||
.and("be.disabled");
|
||||
|
||||
// Workspace Variable Permissions
|
||||
cy.verifyElement(
|
||||
groupsSelector.resourcesWorkspaceVar,
|
||||
groupsText.resourcesWorkspaceVar
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.workspaceCreateLabel,
|
||||
groupsText.workspaceCreateLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.workspaceHelperText,
|
||||
groupsText.workspaceHelperText
|
||||
);
|
||||
cy.get(groupsSelector.workspaceVarCheckbox)
|
||||
.should("be.visible")
|
||||
.and("be.checked")
|
||||
.and("be.disabled");
|
||||
// Granular Permissions
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
cy.verifyElement(groupsSelector.nameTableHeader, groupsText.nameTableHeader);
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionsTableHeader,
|
||||
groupsText.granularAccessPermissionHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.resourceHeader}:eq(1)`,
|
||||
groupsText.resourcesTableHeader
|
||||
);
|
||||
cy.verifyElement(groupsSelector.appsText, " Apps");
|
||||
cy.get(groupsSelector.appEditRadio)
|
||||
.should("be.visible")
|
||||
.and("be.checked")
|
||||
.and("have.attr", "disabled");
|
||||
cy.verifyElement(groupsSelector.appEditLabel, groupsText.appEditLabelText);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appEditHelperText,
|
||||
groupsText.appEditHelperText
|
||||
);
|
||||
cy.get(groupsSelector.appViewRadio)
|
||||
.should("be.visible")
|
||||
.and("have.attr", "disabled");
|
||||
cy.verifyElement(groupsSelector.appViewLabel, groupsText.appViewLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appViewHelperText,
|
||||
groupsText.appViewHelperText
|
||||
);
|
||||
cy.get(groupsSelector.appHideCheckbox).should("be.visible");
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHideHelperText,
|
||||
groupsText.appHideHelperText
|
||||
);
|
||||
cy.verifyElement(groupsSelector.addAppButton, groupsText.addButton);
|
||||
cy.get(groupsSelector.addAppButton).should("be.disabled");
|
||||
|
||||
//Builder
|
||||
cy.get(groupsSelector.groupLink("Builder")).click();
|
||||
cy.verifyElement(groupsSelector.builderListItem, "Builder");
|
||||
cy.verifyElement(groupsSelector.builderTitle, "Builder (0)");
|
||||
|
||||
// Group Permission Elements Verification
|
||||
cy.verifyElement(
|
||||
groupsSelector.createNewGroupButton,
|
||||
groupsText.createNewGroupButton
|
||||
);
|
||||
cy.verifyElement(groupsSelector.usersLink, groupsText.usersLink);
|
||||
cy.verifyElement(groupsSelector.permissionsLink, groupsText.permissionsLink);
|
||||
cy.verifyElement(groupsSelector.granularLink, "Granular access");
|
||||
|
||||
// Resource Verification
|
||||
cy.verifyElement(
|
||||
groupsSelector.textDefaultGroup,
|
||||
groupsText.textDefaultGroup
|
||||
);
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
cy.verifyElement(
|
||||
groupsSelector.nameTableHeader,
|
||||
groupsText.userNameTableHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.emailTableHeader,
|
||||
groupsText.emailTableHeader
|
||||
);
|
||||
cy.get(groupsSelector.userEmptyPageIcon).should("be.visible");
|
||||
cy.get(groupsSelector.userEmptyPageTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.userEmptyPageTitle
|
||||
);
|
||||
cy.get(groupsSelector.userEmptyPageHelperText).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.userEmptyPageHelperText
|
||||
);
|
||||
|
||||
// Granular Access Verifications
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
cy.verifyElement(groupsSelector.resourcesApps, groupsText.resourcesApps);
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionsTableHeader,
|
||||
groupsText.permissionsTableHeader
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.appsCreateCheck).should("be.visible").and("be.checked");
|
||||
cy.verifyElement(groupsSelector.appsCreateLabel, groupsText.createLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appCreateHelperText,
|
||||
groupsText.appCreateHelperText
|
||||
);
|
||||
toggleCheckbox(
|
||||
groupsSelector.appsCreateCheck,
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.permissionUpdatedToast
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.appsDeleteCheck).should("be.visible").and("be.checked");
|
||||
cy.verifyElement(groupsSelector.appsDeleteLabel, groupsText.deleteLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appDeleteHelperText,
|
||||
groupsText.appDeleteHelperText
|
||||
);
|
||||
toggleCheckbox(
|
||||
groupsSelector.appsDeleteCheck,
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.permissionUpdatedToast
|
||||
);
|
||||
|
||||
// Folder Permissions
|
||||
cy.verifyElement(
|
||||
groupsSelector.resourcesFolders,
|
||||
groupsText.resourcesFolders
|
||||
);
|
||||
cy.get(groupsSelector.foldersCreateCheck)
|
||||
.should("be.visible")
|
||||
.and("be.checked");
|
||||
cy.verifyElement(
|
||||
groupsSelector.foldersCreateLabel,
|
||||
groupsText.folderCreateLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.foldersHelperText,
|
||||
groupsText.folderHelperText
|
||||
);
|
||||
toggleCheckbox(
|
||||
groupsSelector.foldersCreateCheck,
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.permissionUpdatedToast
|
||||
);
|
||||
|
||||
// Workspace Variable Permissions
|
||||
cy.verifyElement(
|
||||
groupsSelector.resourcesWorkspaceVar,
|
||||
groupsText.resourcesWorkspaceVar
|
||||
);
|
||||
cy.get(groupsSelector.workspaceVarCheckbox)
|
||||
.should("be.visible")
|
||||
.and("be.checked");
|
||||
cy.verifyElement(
|
||||
groupsSelector.workspaceCreateLabel,
|
||||
groupsText.workspaceCreateLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.workspaceHelperText,
|
||||
groupsText.workspaceHelperText
|
||||
);
|
||||
toggleCheckbox(
|
||||
groupsSelector.workspaceVarCheckbox,
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.permissionUpdatedToast
|
||||
);
|
||||
|
||||
// Granular Permissions
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
|
||||
cy.verifyElement(groupsSelector.nameTableHeader, groupsText.nameTableHeader);
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionsTableHeader,
|
||||
groupsText.granularAccessPermissionHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.resourceHeader}:eq(1)`,
|
||||
groupsText.resourcesTableHeader
|
||||
);
|
||||
cy.verifyElement(groupsSelector.appsText, groupsText.appsLink);
|
||||
cy.get(groupsSelector.appEditRadio)
|
||||
.should("be.visible")
|
||||
.and("be.checked")
|
||||
.and("be.enabled");
|
||||
cy.verifyElement(groupsSelector.appEditLabel, groupsText.appEditLabelText);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appEditHelperText,
|
||||
groupsText.appEditHelperText
|
||||
);
|
||||
cy.get(groupsSelector.appViewRadio).should("be.visible").and("be.enabled");
|
||||
cy.verifyElement(groupsSelector.appViewLabel, groupsText.appViewLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appViewHelperText,
|
||||
groupsText.appViewHelperText
|
||||
);
|
||||
cy.get(groupsSelector.appHideCheckbox)
|
||||
.should("be.visible")
|
||||
.and("be.disabled");
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHideLabel,
|
||||
groupsText.appHideLabelPermissionModal
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHideHelperText,
|
||||
groupsText.appHideHelperText
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.granularAccessPermission)
|
||||
.trigger("mouseenter")
|
||||
.click({ force: true });
|
||||
cy.get(".modal-base").should("be.visible");
|
||||
cy.get(groupsSelector.deletePermissionIcon)
|
||||
.should("be.visible")
|
||||
.and("be.enabled");
|
||||
cy.get(groupsSelector.deletePermissionIcon).click();
|
||||
cy.get(".confirm-dialogue-modal").should("be.visible");
|
||||
cy.verifyElement(groupsSelector.deleteMessage, groupsText.deleteMessage);
|
||||
cy.get(groupsSelector.yesButton).should("be.visible").and("be.enabled");
|
||||
cy.get(groupsSelector.cancelButton).should("be.visible").and("be.enabled");
|
||||
cy.contains("Cancel").click();
|
||||
cy.get(groupsSelector.granularAccessPermission)
|
||||
.trigger("mouseenter")
|
||||
.click({ force: true });
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.addEditPermissionModalTitle}:eq(2)`,
|
||||
groupsText.editPermissionModalTitle
|
||||
);
|
||||
permissionModal();
|
||||
|
||||
cy.get(groupsSelector.customradio).should("be.visible").should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.customLabel, groupsText.customLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.customHelperText,
|
||||
groupsText.customHelperText
|
||||
);
|
||||
|
||||
cy.verifyElement(groupsSelector.confimButton, groupsText.updateButtonText);
|
||||
cy.get(groupsSelector.confimButton).should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.cancelButton, groupsText.cancelButton);
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
|
||||
//Add modal
|
||||
cy.verifyElement(groupsSelector.addAppButton, groupsText.addButton);
|
||||
cy.get(groupsSelector.addAppButton)
|
||||
.should("be.visible")
|
||||
.and("be.enabled")
|
||||
.click();
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.addEditPermissionModalTitle}:eq(2)`,
|
||||
groupsText.addPermissionModalTitle
|
||||
);
|
||||
permissionModal();
|
||||
cy.get(groupsSelector.customradio).should("be.visible").should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.customLabel, groupsText.customLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.customHelperText,
|
||||
groupsText.customHelperText
|
||||
);
|
||||
cy.verifyElement(groupsSelector.confimButton, groupsText.addButtonText);
|
||||
cy.get(groupsSelector.confimButton).should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.cancelButton, groupsText.cancelButton);
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
|
||||
//End User
|
||||
|
||||
cy.get(groupsSelector.groupLink("End-user")).click();
|
||||
cy.get(groupsSelector.groupLink("End-user")).verifyVisibleElement(
|
||||
"have.text",
|
||||
"End-user"
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.enduserTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
"End-user (0)"
|
||||
);
|
||||
|
||||
cy.verifyElement(
|
||||
groupsSelector.createNewGroupButton,
|
||||
groupsText.createNewGroupButton
|
||||
);
|
||||
cy.verifyElement(groupsSelector.usersLink, groupsText.usersLink);
|
||||
cy.verifyElement(groupsSelector.permissionsLink, groupsText.permissionsLink);
|
||||
cy.verifyElement(groupsSelector.granularLink, "Granular access");
|
||||
|
||||
// Resource Verification
|
||||
cy.verifyElement(
|
||||
groupsSelector.textDefaultGroup,
|
||||
groupsText.textDefaultGroup
|
||||
);
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
cy.verifyElement(
|
||||
groupsSelector.nameTableHeader,
|
||||
groupsText.userNameTableHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.emailTableHeader,
|
||||
groupsText.emailTableHeader
|
||||
);
|
||||
cy.get(groupsSelector.userEmptyPageIcon).should("be.visible");
|
||||
cy.get(groupsSelector.userEmptyPageTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.userEmptyPageTitle
|
||||
);
|
||||
cy.get(groupsSelector.userEmptyPageHelperText).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.userEmptyPageHelperText
|
||||
);
|
||||
|
||||
// Granular Access Verifications
|
||||
cy.get(groupsSelector.permissionsLink).click();
|
||||
cy.get(groupsSelector.helperTextAdminAppAccess)
|
||||
.eq(0)
|
||||
.verifyVisibleElement("have.text", groupsText.enduserAccessHelperText);
|
||||
cy.verifyElement(groupsSelector.resourcesApps, groupsText.resourcesApps);
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionsTableHeader,
|
||||
groupsText.permissionsTableHeader
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.appsCreateCheck)
|
||||
.should("be.visible")
|
||||
.and("not.be.checked")
|
||||
.and("be.disabled");
|
||||
cy.verifyElement(groupsSelector.appsCreateLabel, groupsText.createLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appCreateHelperText,
|
||||
groupsText.appCreateHelperText
|
||||
);
|
||||
cy.get(groupsSelector.appsDeleteCheck)
|
||||
.should("be.visible")
|
||||
.and("not.be.checked")
|
||||
.and("be.disabled");
|
||||
cy.verifyElement(groupsSelector.appsDeleteLabel, groupsText.deleteLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appDeleteHelperText,
|
||||
groupsText.appDeleteHelperText
|
||||
);
|
||||
|
||||
// Folder Permissions
|
||||
cy.verifyElement(
|
||||
groupsSelector.resourcesFolders,
|
||||
groupsText.resourcesFolders
|
||||
);
|
||||
cy.get(groupsSelector.foldersCreateCheck)
|
||||
.should("be.visible")
|
||||
.and("not.be.checked")
|
||||
.and("be.disabled");
|
||||
cy.verifyElement(
|
||||
groupsSelector.foldersCreateLabel,
|
||||
groupsText.folderCreateLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.foldersHelperText,
|
||||
groupsText.folderHelperText
|
||||
);
|
||||
|
||||
// Workspace Variable Permissions
|
||||
cy.verifyElement(
|
||||
groupsSelector.resourcesWorkspaceVar,
|
||||
groupsText.resourcesWorkspaceVar
|
||||
);
|
||||
cy.get(groupsSelector.workspaceVarCheckbox)
|
||||
.should("be.visible")
|
||||
.and("not.be.checked")
|
||||
.and("be.disabled");
|
||||
cy.verifyElement(
|
||||
groupsSelector.workspaceCreateLabel,
|
||||
groupsText.workspaceCreateLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.workspaceHelperText,
|
||||
groupsText.workspaceHelperText
|
||||
);
|
||||
|
||||
// Granular Permissions
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
cy.verifyElement(groupsSelector.nameTableHeader, groupsText.nameTableHeader);
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionsTableHeader,
|
||||
groupsText.granularAccessPermissionHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.resourceHeader}:eq(1)`,
|
||||
groupsText.resourcesTableHeader
|
||||
);
|
||||
cy.verifyElement(groupsSelector.appsText, groupsText.appsLink);
|
||||
cy.get(groupsSelector.appEditRadio)
|
||||
.should("be.visible")
|
||||
.and("not.be.checked")
|
||||
.and("be.disabled");
|
||||
cy.verifyElement(groupsSelector.appEditLabel, groupsText.appEditLabelText);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appEditHelperText,
|
||||
groupsText.appEditHelperText
|
||||
);
|
||||
cy.get(groupsSelector.appViewRadio)
|
||||
.should("be.visible")
|
||||
.and("be.disabled")
|
||||
.and("be.checked");
|
||||
cy.verifyElement(groupsSelector.appViewLabel, groupsText.appViewLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appViewHelperText,
|
||||
groupsText.appViewHelperText
|
||||
);
|
||||
cy.get(groupsSelector.appHideCheckbox).should("be.visible").and("be.enabled");
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHideLabel,
|
||||
groupsText.appHideLabelPermissionModal
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHideHelperText,
|
||||
groupsText.appHideHelperText
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.granularAccessPermission)
|
||||
.trigger("mouseenter")
|
||||
.click({ force: true });
|
||||
cy.get(".modal-base").should("be.visible");
|
||||
cy.get(groupsSelector.deletePermissionIcon)
|
||||
.should("be.visible")
|
||||
.and("be.enabled");
|
||||
cy.get(groupsSelector.deletePermissionIcon).click();
|
||||
cy.get(".confirm-dialogue-modal").should("be.visible");
|
||||
cy.verifyElement(groupsSelector.deleteMessage, groupsText.deleteMessage);
|
||||
cy.get(groupsSelector.yesButton).should("be.visible").and("be.enabled");
|
||||
cy.get(groupsSelector.cancelButton).should("be.visible").and("be.enabled");
|
||||
cy.contains("Cancel").click();
|
||||
cy.get(groupsSelector.granularAccessPermission)
|
||||
.trigger("mouseenter")
|
||||
.click({ force: true });
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.addEditPermissionModalTitle}:eq(2)`,
|
||||
groupsText.editPermissionModalTitle
|
||||
);
|
||||
permissionModal();
|
||||
|
||||
cy.get(groupsSelector.customradio).should("be.visible").should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.customLabel, groupsText.customLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.customHelperText,
|
||||
groupsText.customHelperText
|
||||
);
|
||||
|
||||
cy.verifyElement(groupsSelector.confimButton, groupsText.updateButtonText);
|
||||
cy.get(groupsSelector.confimButton).should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.cancelButton, groupsText.cancelButton);
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
//Add Modal
|
||||
cy.verifyElement(groupsSelector.addAppButton, groupsText.addButton);
|
||||
cy.get(groupsSelector.addAppButton)
|
||||
.should("be.visible")
|
||||
.and("be.enabled")
|
||||
.click();
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.addEditPermissionModalTitle}:eq(2)`,
|
||||
groupsText.addPermissionModalTitle
|
||||
);
|
||||
permissionModal();
|
||||
cy.get(groupsSelector.customradio).should("be.visible").should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.customLabel, groupsText.customLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.customHelperText,
|
||||
groupsText.customHelperText
|
||||
);
|
||||
cy.verifyElement(groupsSelector.confimButton, groupsText.addButtonText);
|
||||
cy.get(groupsSelector.confimButton).should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.cancelButton, groupsText.cancelButton);
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
};
|
||||
|
||||
const toggleCheckbox = (selector, toastSelector, toastMessage) => {
|
||||
cy.get(selector).should("be.visible").uncheck();
|
||||
cy.verifyToastMessage(toastSelector, toastMessage);
|
||||
cy.get(selector).check();
|
||||
cy.verifyToastMessage(toastSelector, toastMessage);
|
||||
};
|
||||
|
||||
// Permission Modal Verification
|
||||
export const permissionModal = () => {
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionNameLabel,
|
||||
groupsText.permissionNameLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionNameHelperText,
|
||||
groupsText.permissionNameHelperText
|
||||
);
|
||||
|
||||
cy.verifyElement(groupsSelector.permissionLabel, groupsText.permissionLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.editPermissionLabel,
|
||||
groupsText.editPermissionLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.editPermissionHelperText,
|
||||
groupsText.editPermissionHelperText
|
||||
);
|
||||
|
||||
cy.verifyElement(
|
||||
groupsSelector.viewPermissionLabel,
|
||||
groupsText.viewPermissionLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.viewPermissionHelperText,
|
||||
groupsText.viewPermissionHelperText
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.hidePermissionInput).should("be.visible");
|
||||
cy.verifyElement(groupsSelector.resourceLabel, groupsText.resourcesheader);
|
||||
cy.get(groupsSelector.resourceContainer).should("be.visible");
|
||||
cy.get(groupsSelector.allAppsRadio).should("be.visible").and("be.checked");
|
||||
cy.verifyElement(groupsSelector.allAppsLabel, groupsText.allAppsLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.allAppsHelperText,
|
||||
groupsText.allAppsHelperText
|
||||
);
|
||||
};
|
||||
|
||||
export const addAppToGroup = (appName) => {
|
||||
cy.get(groupsSelector.appsLink).click();
|
||||
cy.wait(500);
|
||||
cy.get(groupsSelector.appSearchBox).click();
|
||||
cy.wait(500);
|
||||
cy.get(groupsSelector.searchBoxOptions).contains(appName).click();
|
||||
cy.get(groupsSelector.selectAddButton).click();
|
||||
cy.contains("tr", appName)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get("td input").eq(1).check();
|
||||
});
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
"App permissions updated"
|
||||
);
|
||||
};
|
||||
|
||||
export const createGroup = (groupName) => {
|
||||
export const apiCreateGroup = (groupName) => {
|
||||
return cy.getAuthHeaders().then((headers) => {
|
||||
return cy
|
||||
.request({
|
||||
|
|
@ -650,14 +26,16 @@ export const createGroup = (groupName) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const apiDeleteGroup = (groupId) => {
|
||||
cy.getAuthHeaders().then((headers) => {
|
||||
cy.request({
|
||||
method: "DELETE",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}`,
|
||||
headers: headers,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
export const apiDeleteGroup = (groupName) => {
|
||||
cy.apiGetGroupId(groupName).then((groupId) => {
|
||||
cy.getAuthHeaders().then((headers) => {
|
||||
cy.request({
|
||||
method: "DELETE",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}`,
|
||||
headers: headers,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -669,52 +47,6 @@ export const deleteGroup = (groupName, workspaceId) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const createGroupAddAppAndUserToGroup = (groupName, email) => {
|
||||
cy.getAuthHeaders().then((headers) => {
|
||||
createGroup(groupName).then((groupId) => {
|
||||
// Add app to group
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/granular-permissions/app`,
|
||||
headers: headers,
|
||||
body: {
|
||||
name: "Apps",
|
||||
type: "app",
|
||||
groupId: groupId,
|
||||
isAll: false,
|
||||
createResourcePermissionObject: {
|
||||
canEdit: true,
|
||||
canView: false,
|
||||
hideFromDashboard: false,
|
||||
resourcesToAdd: [{ appId: Cypress.env("appId") }],
|
||||
},
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
});
|
||||
cy.wait(2000);
|
||||
cy.task("dbConnection", {
|
||||
dbconfig: Cypress.env("app_db"),
|
||||
sql: `select id from users where email='${email}';`,
|
||||
}).then((resp) => {
|
||||
const userId = resp.rows[0].id;
|
||||
// Add user to group
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/users`,
|
||||
headers: headers,
|
||||
body: {
|
||||
userIds: [userId],
|
||||
groupId: groupId,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const OpenGroupCardOption = (groupName) => {
|
||||
cy.get(groupsSelector.groupLink(groupName))
|
||||
.trigger("mouseenter")
|
||||
|
|
@ -775,61 +107,6 @@ export const groupPermission = (
|
|||
});
|
||||
};
|
||||
|
||||
export const duplicateGroup = () => {
|
||||
OpenGroupCardOption(groupName);
|
||||
cy.get(groupsSelector.duplicateOption).click();
|
||||
};
|
||||
|
||||
export const updateRoleUI = (user, role, email, message) => {
|
||||
cy.get(groupsSelector.groupLink(user)).click();
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
cy.get(`[data-cy="${email}-user-row"] > :nth-child(3)`).click();
|
||||
cy.get('[data-cy="modal-title"] > .tj-text-md').should(
|
||||
"have.text",
|
||||
"Edit user role"
|
||||
);
|
||||
cy.get('[data-cy="user-email"]').should("have.text", email);
|
||||
cy.get(groupsSelector.userRoleLabel).should("have.text", groupsText.userRole);
|
||||
cy.get(groupsSelector.warningText).should(
|
||||
"have.text",
|
||||
groupsText.warningText
|
||||
);
|
||||
cy.get(groupsSelector.cancelButton)
|
||||
.should("have.text", groupsText.cancelButton)
|
||||
.and("be.enabled");
|
||||
cy.get(groupsSelector.confimButton).should("be.disabled");
|
||||
cy.get(
|
||||
".css-nwhe5y-container > .react-select__control > .react-select__value-container"
|
||||
)
|
||||
.click()
|
||||
.type(`${role}{enter}`);
|
||||
cy.get(groupsSelector.confimButton)
|
||||
.should("be.enabled")
|
||||
.and("have.text", groupsText.continueButtonText)
|
||||
.click();
|
||||
cy.get('[data-cy="modal-body"]').should("have.text", message);
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
cy.get(`[data-cy="${email}-user-row"] > :nth-child(3)`).click();
|
||||
cy.get(
|
||||
".css-nwhe5y-container > .react-select__control > .react-select__value-container"
|
||||
)
|
||||
.click()
|
||||
.type(`${role}{enter}`);
|
||||
cy.get(groupsSelector.confimButton)
|
||||
.should("be.enabled")
|
||||
.and("have.text", groupsText.continueButtonText)
|
||||
.click();
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
if (user != "Admin") {
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.roleUpdateToastMessage
|
||||
);
|
||||
}
|
||||
cy.get(groupsSelector.groupLink(role)).click();
|
||||
cy.get(`[data-cy="${email}-user-row"]`).should("exist");
|
||||
};
|
||||
|
||||
export const updateRole = (user, role, email, message = null) => {
|
||||
cy.get(groupsSelector.groupLink(user)).click();
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
|
|
@ -897,25 +174,6 @@ export const inviteUserBasedOnRole = (firstName, email, role = "end-user") => {
|
|||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
};
|
||||
|
||||
export const verifyBasicPermissions = (canCreate = true) => {
|
||||
cy.get(commonSelectors.dashboardAppCreateButton).should(
|
||||
canCreate ? "be.enabled" : "be.disabled"
|
||||
);
|
||||
cy.get(commonSelectors.createNewFolderButton).should(
|
||||
canCreate ? "exist" : "not.exist"
|
||||
);
|
||||
cy.get('[data-cy="database-icon"]').should(canCreate ? "exist" : "not.exist");
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).should(
|
||||
canCreate ? "exist" : "not.exist"
|
||||
);
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get(commonSelectors.globalDataSourceIcon).should(
|
||||
canCreate ? "exist" : "not.exist"
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const setupWorkspaceAndInviteUser = (
|
||||
workspaceName,
|
||||
workspaceSlug,
|
||||
|
|
@ -934,13 +192,6 @@ export const setupWorkspaceAndInviteUser = (
|
|||
cy.wait(2000);
|
||||
};
|
||||
|
||||
export const verifySettingsAccess = (shouldExist = true) => {
|
||||
cy.get(commonSelectors.settingsIcon).click();
|
||||
cy.get(commonSelectors.workspaceSettings).should(
|
||||
shouldExist ? "exist" : "not.exist"
|
||||
);
|
||||
};
|
||||
|
||||
export const verifyUserPrivileges = (
|
||||
expectedButtonState,
|
||||
shouldHaveWorkspaceSettings
|
||||
|
|
@ -974,3 +225,35 @@ export const verifyUserRole = (userIdAlias, expectedRole, expectedGroups) => {
|
|||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const apiAddUserToGroup = (groupId, email) => {
|
||||
return cy.getAuthHeaders().then((headers) => {
|
||||
return cy
|
||||
.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/organization-users`,
|
||||
headers: headers,
|
||||
log: false,
|
||||
})
|
||||
.then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
const user = response.body.users.find((u) => u.email === email);
|
||||
const userId = user.user_id;
|
||||
return cy
|
||||
.request({
|
||||
method: "POST",
|
||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/users`,
|
||||
headers: headers,
|
||||
body: {
|
||||
userIds: [userId],
|
||||
groupId: groupId,
|
||||
},
|
||||
log: false,
|
||||
})
|
||||
.then((addResponse) => {
|
||||
expect(addResponse.status).to.equal(201);
|
||||
return userId;
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { commonSelectors, cyParamName } from "Selectors/common";
|
||||
import { ssoSelector } from "Selectors/manageSSO";
|
||||
import { ssoText } from "Texts/manageSSO";
|
||||
import * as common from "Support/utils/common";
|
||||
import { commonText } from "Texts/common";
|
||||
import { ssoText } from "Texts/manageSSO";
|
||||
|
||||
export const generalSettings = () => {
|
||||
cy.get(ssoSelector.enableSignUpToggle).check();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { commonSelectors, cyParamName } from "Selectors/common";
|
||||
import { usersText } from "Texts/manageUsers";
|
||||
import { usersSelector } from "Selectors/manageUsers";
|
||||
import { ssoSelector } from "Selectors/manageSSO";
|
||||
import { ssoText } from "Texts/manageSSO";
|
||||
import * as common from "Support/utils/common";
|
||||
import { commonText } from "Texts/common";
|
||||
import { usersSelector } from "Selectors/manageUsers";
|
||||
import { onboardingSelectors } from "Selectors/onboarding";
|
||||
const envVar = Cypress.env("environment");
|
||||
import * as common from "Support/utils/common";
|
||||
import { fillInputField } from "Support/utils/common";
|
||||
import { commonText } from "Texts/common";
|
||||
import { ssoText } from "Texts/manageSSO";
|
||||
import { usersText } from "Texts/manageUsers";
|
||||
const envVar = Cypress.env("environment");
|
||||
|
||||
export const manageUsersElements = () => {
|
||||
cy.get(
|
||||
|
|
@ -480,7 +480,11 @@ export const openEditUserDetails = (
|
|||
|
||||
verifyUserStatusAndMetadata(email, activeStatusText, expectedMetadata);
|
||||
|
||||
cy.contains("td", email)
|
||||
navigateToEditUser(email);
|
||||
};
|
||||
|
||||
export const navigateToEditUser = (email) => {
|
||||
cy.contains("td", email)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[data-cy="user-actions-button"]').click();
|
||||
|
|
@ -489,6 +493,3 @@ export const openEditUserDetails = (
|
|||
.verifyVisibleElement("have.text", "Edit user details")
|
||||
.click();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { commonText } from "Texts/common";
|
||||
import { dashboardText } from "Texts/dashboard";
|
||||
import {
|
||||
verifyandModifyUserRole,
|
||||
verifyandModifySizeOftheCompany,
|
||||
} from "Support/utils/selfHostSignUp";
|
||||
import { navigateToManageUsers, logout } from "Support/utils/common";
|
||||
import { ssoSelector } from "Selectors/manageSSO";
|
||||
import { ssoText } from "Texts/manageSSO";
|
||||
import { onboardingSelectors } from "Selectors/onboarding";
|
||||
import { logout, navigateToManageUsers } from "Support/utils/common";
|
||||
import { fetchAndVisitInviteLink } from "Support/utils/manageUsers";
|
||||
import { commonText } from "Texts/common";
|
||||
import { ssoText } from "Texts/manageSSO";
|
||||
import { onboardingText } from "Texts/onboarding";
|
||||
|
||||
export const verifyConfirmEmailPage = (email) => {
|
||||
|
|
|
|||
173
cypress-tests/cypress/support/utils/platform/customGroups.js
Normal file
173
cypress-tests/cypress/support/utils/platform/customGroups.js
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { commonEeSelectors } from "Selectors/eeCommon";
|
||||
import { cyParamName, groupsSelector } from "Selectors/manageGroups";
|
||||
import { groupsText } from "Texts/manageGroups";
|
||||
|
||||
export const createGroupViaUI = (groupName) => {
|
||||
cy.get(groupsSelector.createNewGroupButton).click();
|
||||
cy.get(groupsSelector.addNewGroupModalTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.cardTitle
|
||||
);
|
||||
cy.clearAndType(groupsSelector.groupNameInput, groupName);
|
||||
cy.get(groupsSelector.createGroupButton).should("be.enabled").click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.groupCreatedToast
|
||||
);
|
||||
};
|
||||
|
||||
export const verifyGroupCreatedInSidebar = (groupName) => {
|
||||
cy.get(groupsSelector.groupLink(groupName))
|
||||
.should("be.visible")
|
||||
.and("contain.text", groupName);
|
||||
};
|
||||
|
||||
export const renameGroupViaUI = (oldName, newName) => {
|
||||
cy.get(groupsSelector.groupLink(oldName)).click();
|
||||
cy.get(groupsSelector.groupNameUpdateLink).should("be.visible").click();
|
||||
cy.clearAndType(groupsSelector.groupNameInput, newName);
|
||||
cy.get(groupsSelector.createGroupButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.groupNameUpdateSucessToast
|
||||
);
|
||||
};
|
||||
|
||||
export const deleteGroupViaUI = (groupName) => {
|
||||
cy.get(groupsSelector.groupLink(groupName)).click();
|
||||
cy.get(groupsSelector.groupLink(groupName)).realHover();
|
||||
cy.wait(2000).then(() => {
|
||||
cy.get(
|
||||
`[data-cy="${cyParamName(groupName)}-list-item"] > :nth-child(2) > .tj-base-btn`
|
||||
).click({ force: true });
|
||||
});
|
||||
cy.get(groupsSelector.deleteGroupOption).click();
|
||||
cy.get(commonSelectors.buttonSelector("Yes")).click();
|
||||
};
|
||||
|
||||
export const verifyGroupRemovedFromSidebar = (groupName) => {
|
||||
cy.get(groupsSelector.groupLink(groupName)).should("not.exist");
|
||||
};
|
||||
|
||||
export const addGranularPermissionViaUI = (permissionName, options = {}) => {
|
||||
const {
|
||||
resourceType = "app",
|
||||
permission = "edit",
|
||||
scope = "all",
|
||||
resources = [],
|
||||
} = options;
|
||||
|
||||
cy.ifEnv("Community", () => {
|
||||
cy.get(groupsSelector.addAppsButton).click();
|
||||
});
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get(groupsSelector.addPermissionButton).click();
|
||||
if (resourceType === "app") {
|
||||
cy.get(groupsSelector.addAppButton).click();
|
||||
} else if (resourceType === "workflow") {
|
||||
cy.get(groupsSelector.addWorkflowButton).click();
|
||||
} else if (resourceType === "datasource") {
|
||||
cy.get(groupsSelector.addDatasourceButton).click();
|
||||
}
|
||||
});
|
||||
|
||||
cy.clearAndType(groupsSelector.permissionNameInput, permissionName);
|
||||
|
||||
if (resourceType === "app") {
|
||||
if (permission === "view") {
|
||||
cy.get(groupsSelector.viewPermissionRadio).check();
|
||||
} else if (permission === "edit") {
|
||||
cy.get(groupsSelector.editPermissionRadio).check();
|
||||
}
|
||||
} else if (resourceType === "workflow") {
|
||||
if (permission === "execute") {
|
||||
cy.get(groupsSelector.executeWorkflowradio).check();
|
||||
} else if (permission === "build") {
|
||||
cy.get(groupsSelector.buildWorkflowradio).check();
|
||||
}
|
||||
} else if (resourceType === "datasource") {
|
||||
if (permission === "buildWith") {
|
||||
cy.get(groupsSelector.buildWithDatasourceRadio).check();
|
||||
} else if (permission === "configure") {
|
||||
cy.get(groupsSelector.configureDatasourceradio).check();
|
||||
}
|
||||
}
|
||||
|
||||
if (scope === "custom") {
|
||||
cy.get(groupsSelector.customRadio).check();
|
||||
if (resources.length > 0) {
|
||||
resources.forEach((resource) => {
|
||||
cy.get(groupsSelector.resourceSelector).click();
|
||||
cy.get(groupsSelector.searchBoxOptions).contains(resource).click();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
cy.get(groupsSelector.allAppsRadio).check();
|
||||
}
|
||||
|
||||
cy.get(groupsSelector.confimButton).click();
|
||||
};
|
||||
|
||||
export const switchBetweenAllAndCustom = (targetScope) => {
|
||||
if (targetScope === "all") {
|
||||
cy.get(groupsSelector.allAppsRadio).check();
|
||||
cy.get(groupsSelector.allAppsRadio).should("be.checked");
|
||||
cy.get(groupsSelector.customRadio).should("not.be.checked");
|
||||
} else if (targetScope === "custom") {
|
||||
cy.get(groupsSelector.customRadio).check();
|
||||
cy.get(groupsSelector.customRadio).should("be.checked");
|
||||
cy.get(groupsSelector.allAppsRadio).should("not.be.checked");
|
||||
cy.get(".css-b62m3t-container").should("be.visible");
|
||||
}
|
||||
};
|
||||
|
||||
export const openGroupThreeDotMenu = (groupName) => {
|
||||
cy.get(groupsSelector.groupLink(groupName)).realHover()
|
||||
cy.get(groupsSelector.groupLink(groupName)).then(() => {
|
||||
cy.get('[datacy="groups-list-option-button"]').click();
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyDuplicateModal = (originalGroupName) => {
|
||||
cy.get('[data-cy="modal-title"]')
|
||||
.should("be.visible")
|
||||
.and("contain.text", "Duplicate group");
|
||||
|
||||
cy.verifyElement(
|
||||
'[data-cy="modal-message"]',
|
||||
"Duplicate the following parts of the group"
|
||||
);
|
||||
cy.verifyElement('[data-cy="users-label"]', "Users");
|
||||
cy.get('[data-cy="users-check-input"]')
|
||||
.should("be.visible")
|
||||
.and("be.checked");
|
||||
|
||||
cy.verifyElement('[data-cy="permissions-label"]', "Permissions");
|
||||
cy.get('[data-cy="permissions-check-input"]')
|
||||
.should("be.visible")
|
||||
.and("be.checked");
|
||||
|
||||
cy.verifyElement('[data-cy="apps-label"]', "Apps");
|
||||
cy.get('[data-cy="apps-check-input"]').should("be.visible").and("be.checked");
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.verifyElement('[data-cy="workflows-label"]', "Workflows");
|
||||
cy.get('[data-cy="workflows-check-input"]')
|
||||
.should("be.visible")
|
||||
.and("be.checked");
|
||||
|
||||
cy.verifyElement('[data-cy="datasources-label"]', "Datasources");
|
||||
cy.get('[data-cy="datasources-check-input"]')
|
||||
.should("be.visible")
|
||||
.and("be.checked");
|
||||
});
|
||||
|
||||
cy.verifyElement(groupsSelector.cancelButton, "Cancel");
|
||||
cy.get(groupsSelector.cancelButton).should("be.visible").and("be.enabled");
|
||||
|
||||
cy.verifyElement(commonEeSelectors.confirmButton, "Duplicate");
|
||||
cy.get(commonEeSelectors.confirmButton)
|
||||
.should("be.visible")
|
||||
.and("be.enabled");
|
||||
};
|
||||
855
cypress-tests/cypress/support/utils/platform/groupsUI.js
Normal file
855
cypress-tests/cypress/support/utils/platform/groupsUI.js
Normal file
|
|
@ -0,0 +1,855 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { groupsSelector } from "Selectors/manageGroups";
|
||||
import { groupsText } from "Texts/manageGroups";
|
||||
|
||||
export const verifyAdminHelperText = (index = 0) => {
|
||||
cy.get(groupsSelector.helperTextAdminAppAccess)
|
||||
.eq(index)
|
||||
.should("be.visible")
|
||||
.and("contain.text", "Admin has all permissions. This is not editable");
|
||||
|
||||
cy.get(groupsSelector.helperTextAdminAppAccess)
|
||||
.eq(index)
|
||||
.find("a")
|
||||
.should("be.visible")
|
||||
.and("have.text", "read documentation")
|
||||
.and("have.attr", "href")
|
||||
.and("include", "docs.tooljet.ai/docs/tutorial/manage-users-groups");
|
||||
};
|
||||
|
||||
export const verifyEditUserRoleModal = (userEmail) => {
|
||||
cy.get('[data-cy="modal-title"]')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get("span").should("be.visible").and("contain.text", "Edit user role");
|
||||
cy.get('[data-cy="user-email"]')
|
||||
.should("be.visible")
|
||||
.and("have.text", userEmail);
|
||||
});
|
||||
|
||||
cy.get(groupsSelector.userRoleLabel)
|
||||
.should("be.visible")
|
||||
.and("have.text", groupsText.userRole);
|
||||
cy.get(groupsSelector.warningText)
|
||||
.should("be.visible")
|
||||
.and("have.text", groupsText.warningText);
|
||||
|
||||
cy.get(".react-select__control").should("be.visible");
|
||||
cy.get(".react-select__placeholder")
|
||||
.should("be.visible")
|
||||
.and("contain.text", "Select new role of user");
|
||||
|
||||
cy.get(groupsSelector.cancelButton)
|
||||
.should("be.visible")
|
||||
.and("have.text", groupsText.cancelButton)
|
||||
.and("be.enabled");
|
||||
|
||||
cy.get(groupsSelector.confimButton)
|
||||
.should("be.visible")
|
||||
.and("have.text", groupsText.continueButtonText)
|
||||
.and("be.disabled");
|
||||
|
||||
cy.get('[data-cy="modal-close-button"]').should("be.visible");
|
||||
};
|
||||
|
||||
export const toggleAllPermissions = (status = ["uncheck", "check"]) => {
|
||||
permissions.forEach((permissionSelector) => {
|
||||
cy.get(permissionSelector).should("be.visible")[status[0]]();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.permissionUpdatedToast
|
||||
);
|
||||
cy.get(permissionSelector).should("be.visible")[status[1]]();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
groupsText.permissionUpdatedToast
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyDeleteConfirmationModal = () => {
|
||||
cy.get(".confirm-dialogue-modal").should("be.visible");
|
||||
cy.verifyElement(groupsSelector.deleteMessage, groupsText.deleteMessage);
|
||||
cy.get(groupsSelector.yesButton).should("be.visible").and("be.enabled");
|
||||
cy.get(groupsSelector.cancelButton).should("be.visible").and("be.enabled");
|
||||
};
|
||||
|
||||
export const verifyGranularEditModal = (role) => {
|
||||
cy.get(groupsSelector.granularAccessPermission).realHover();
|
||||
cy.get('[data-cy="edit-apps-granular-access"]').click();
|
||||
|
||||
cy.get(".modal-base").should("be.visible");
|
||||
|
||||
cy.get(groupsSelector.deletePermissionIcon)
|
||||
.should("be.visible")
|
||||
.and("be.enabled");
|
||||
cy.get(groupsSelector.deletePermissionIcon).click();
|
||||
|
||||
verifyDeleteConfirmationModal();
|
||||
cy.contains("Cancel").click();
|
||||
|
||||
cy.get(groupsSelector.granularAccessPermission)
|
||||
.realHover()
|
||||
.click({ force: true });
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.addEditPermissionModalTitle}:eq(2)`,
|
||||
groupsText.editPermissionModalTitle
|
||||
);
|
||||
permissionModal();
|
||||
|
||||
if (role === "builder" || role === "enduser") {
|
||||
cy.get(groupsSelector.customRadio).should("be.disabled");
|
||||
} else {
|
||||
cy.get(groupsSelector.customRadio).should("be.enabled");
|
||||
}
|
||||
cy.verifyElement(groupsSelector.customLabel, groupsText.customLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.customHelperText,
|
||||
groupsText.customHelperText
|
||||
);
|
||||
|
||||
cy.verifyElement(groupsSelector.confimButton, groupsText.updateButtonText);
|
||||
cy.get(groupsSelector.confimButton).should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.cancelButton, groupsText.cancelButton);
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
};
|
||||
|
||||
export const verifyGranularAddModal = (role) => {
|
||||
cy.ifEnv("Community", () => {
|
||||
cy.get(groupsSelector.addAppsButton)
|
||||
.should("be.visible")
|
||||
.and("be.enabled")
|
||||
.click();
|
||||
});
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get(groupsSelector.addPermissionButton)
|
||||
.should("be.visible")
|
||||
.and("be.enabled")
|
||||
.click();
|
||||
cy.get(groupsSelector.addAppButton).click();
|
||||
});
|
||||
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.addEditPermissionModalTitle}:eq(2)`,
|
||||
groupsText.addPermissionModalTitle
|
||||
);
|
||||
permissionModal();
|
||||
|
||||
if (role === "builder" || role === "enduser") {
|
||||
cy.get(groupsSelector.customRadio).should("be.disabled");
|
||||
} else {
|
||||
cy.get(groupsSelector.customRadio).should("be.enabled");
|
||||
}
|
||||
cy.verifyElement(groupsSelector.customLabel, groupsText.customLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.customHelperText,
|
||||
groupsText.customHelperText
|
||||
);
|
||||
|
||||
cy.verifyElement(groupsSelector.confimButton, groupsText.addButtonText);
|
||||
cy.get(groupsSelector.confimButton).should("be.disabled");
|
||||
cy.verifyElement(groupsSelector.cancelButton, groupsText.cancelButton);
|
||||
cy.get(groupsSelector.cancelButton).click();
|
||||
};
|
||||
|
||||
export const verifyEnduserHelperText = (index = 0) => {
|
||||
cy.get(groupsSelector.helperTextAdminAppAccess)
|
||||
.eq(index)
|
||||
.should("be.visible")
|
||||
.and("contain.text", "End-user can only have permission to view apps");
|
||||
|
||||
cy.get(groupsSelector.helperTextAdminAppAccess)
|
||||
.eq(index)
|
||||
.find("a")
|
||||
.should("be.visible")
|
||||
.and("have.text", "read documentation")
|
||||
.and("have.attr", "href")
|
||||
.and("include", "docs.tooljet.ai/docs/tutorial/manage-users-groups");
|
||||
};
|
||||
|
||||
export const verifyGranularPermissionModalUI = (
|
||||
resourceType,
|
||||
isEdit = false,
|
||||
permissionName = ""
|
||||
) => {
|
||||
// Permission name section
|
||||
cy.get(groupsSelector.permissionNameLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.permissionNameLabel
|
||||
);
|
||||
cy.get(groupsSelector.permissionNameInput).should("be.visible");
|
||||
|
||||
if (isEdit) {
|
||||
cy.get(groupsSelector.permissionNameInput).should(
|
||||
"have.value",
|
||||
permissionName
|
||||
);
|
||||
} else {
|
||||
cy.get(groupsSelector.permissionNameInput).should(
|
||||
"have.attr",
|
||||
"placeholder"
|
||||
);
|
||||
}
|
||||
|
||||
cy.get(groupsSelector.permissionNameHelperText).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.permissionNameHelperText
|
||||
);
|
||||
|
||||
// Permission section
|
||||
cy.get(groupsSelector.permissionLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.permissionLabel
|
||||
);
|
||||
|
||||
if (resourceType === "app") {
|
||||
cy.verifyElement(
|
||||
groupsSelector.editPermissionLabel,
|
||||
groupsText.editPermissionLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.editPermissionHelperText,
|
||||
groupsText.editPermissionHelperText
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.viewPermissionLabel,
|
||||
groupsText.viewPermissionLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.viewPermissionHelperText,
|
||||
groupsText.viewPermissionHelperText
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.hidePermissionInput).should("be.visible");
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHidePermissionModalLabel,
|
||||
groupsText.appHideLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHidePermissionModalHelperText,
|
||||
groupsText.appHideHelperText
|
||||
);
|
||||
}
|
||||
|
||||
if (resourceType === "workflow") {
|
||||
cy.verifyElement(groupsSelector.workflowsBuildLabel, "Build");
|
||||
cy.verifyElement(
|
||||
groupsSelector.workflowsBuildHelperText,
|
||||
"Access to workflow builder"
|
||||
);
|
||||
cy.verifyElement(groupsSelector.workflowsExecuteLabel, "Execute");
|
||||
cy.verifyElement(
|
||||
groupsSelector.workflowsExecuteHelperText,
|
||||
"Only able to execute the workflow"
|
||||
);
|
||||
}
|
||||
|
||||
if (resourceType === "datasource") {
|
||||
cy.verifyElement(groupsSelector.datasourcesConfigureLabel, "Configure");
|
||||
cy.verifyElement(
|
||||
groupsSelector.datasourcesConfigureHelperText,
|
||||
"Access and edit connection detail"
|
||||
);
|
||||
cy.verifyElement(groupsSelector.datasourcesBuildWithLabel, "Build with");
|
||||
cy.verifyElement(
|
||||
groupsSelector.datasourcesBuildWithHelperText,
|
||||
"Use in apps & workflows"
|
||||
);
|
||||
}
|
||||
|
||||
// Resources section
|
||||
cy.get(groupsSelector.resourceLabel).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.resourcesheader
|
||||
);
|
||||
cy.get(groupsSelector.allAppsRadio).should("be.visible");
|
||||
|
||||
if (isEdit) {
|
||||
cy.verifyElement(groupsSelector.allAppsLabel, groupsText.allAppsLabel);
|
||||
} else {
|
||||
cy.verifyElement(groupsSelector.allAppsLabel, groupsText.groupChipText);
|
||||
}
|
||||
|
||||
cy.verifyElement(
|
||||
groupsSelector.allAppsHelperText,
|
||||
groupsText.allAppsHelperText
|
||||
);
|
||||
cy.get(groupsSelector.customRadio).should("be.visible");
|
||||
cy.verifyElement(groupsSelector.customLabel, groupsText.customLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.customHelperText,
|
||||
groupsText.customHelperText
|
||||
);
|
||||
|
||||
cy.verifyElement(
|
||||
groupsSelector.confimButton,
|
||||
isEdit ? groupsText.updateButtonText : groupsText.addButtonText
|
||||
);
|
||||
cy.verifyElement(groupsSelector.cancelButton, groupsText.cancelButton);
|
||||
|
||||
if (isEdit) {
|
||||
cy.get(groupsSelector.deletePermissionIcon).should("be.visible");
|
||||
}
|
||||
};
|
||||
|
||||
export const verifyGranularPermissionModalStates = (
|
||||
resourceType,
|
||||
role,
|
||||
customStateOverride = null
|
||||
) => {
|
||||
const stateConfig = {
|
||||
app: {
|
||||
builder: {
|
||||
editRadio: { checked: true, enabled: true },
|
||||
viewRadio: { checked: false, enabled: true },
|
||||
hideCheckbox: { enabled: false },
|
||||
allAppsRadio: { checked: true, enabled: false },
|
||||
customRadio: { checked: false, enabled: false },
|
||||
},
|
||||
enduser: {
|
||||
editRadio: { checked: false, enabled: false },
|
||||
viewRadio: { checked: true, enabled: false },
|
||||
hideCheckbox: { enabled: true },
|
||||
allAppsRadio: { checked: true, enabled: false },
|
||||
customRadio: { checked: false, enabled: false },
|
||||
},
|
||||
custom: {
|
||||
editRadio: { checked: true, enabled: true },
|
||||
viewRadio: { checked: false, enabled: true },
|
||||
hideCheckbox: { enabled: false },
|
||||
allAppsRadio: { checked: true, enabled: true },
|
||||
customRadio: { checked: false, enabled: true },
|
||||
},
|
||||
},
|
||||
workflow: {
|
||||
builder: {
|
||||
buildRadio: { checked: true, enabled: true },
|
||||
executeRadio: { checked: false, enabled: true },
|
||||
allAppsRadio: { checked: true, enabled: false },
|
||||
customRadio: { checked: false, enabled: false },
|
||||
},
|
||||
enduser: {
|
||||
buildRadio: { checked: false, enabled: false },
|
||||
executeRadio: { checked: true, enabled: false },
|
||||
allAppsRadio: { checked: true, enabled: false },
|
||||
customRadio: { checked: false, enabled: false },
|
||||
},
|
||||
custom: {
|
||||
buildRadio: { checked: true, enabled: true },
|
||||
executeRadio: { checked: false, enabled: true },
|
||||
allAppsRadio: { checked: true, enabled: true },
|
||||
customRadio: { checked: false, enabled: true },
|
||||
},
|
||||
},
|
||||
datasource: {
|
||||
builder: {
|
||||
configureRadio: { checked: true, enabled: true },
|
||||
buildWithRadio: { checked: false, enabled: true },
|
||||
allAppsRadio: { checked: true, enabled: false },
|
||||
customRadio: { checked: false, enabled: false },
|
||||
},
|
||||
custom: {
|
||||
configureRadio: { checked: true, enabled: true },
|
||||
buildWithRadio: { checked: false, enabled: true },
|
||||
allAppsRadio: { checked: true, enabled: true },
|
||||
customRadio: { checked: false, enabled: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Get the base config
|
||||
let config = stateConfig[resourceType][role];
|
||||
|
||||
// If customStateOverride is provided and role is 'custom', merge it with the default
|
||||
if (customStateOverride && role === "custom") {
|
||||
config = { ...config, ...customStateOverride };
|
||||
}
|
||||
|
||||
if (resourceType === "app") {
|
||||
cy.get(groupsSelector.editPermissionRadio)
|
||||
.should("be.visible")
|
||||
.and(config.editRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(config.editRadio.enabled ? "be.enabled" : "be.disabled");
|
||||
|
||||
cy.get(groupsSelector.viewPermissionRadio)
|
||||
.should("be.visible")
|
||||
.and(config.viewRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(config.viewRadio.enabled ? "be.enabled" : "be.disabled");
|
||||
|
||||
cy.get(groupsSelector.hidePermissionInput)
|
||||
.should("be.visible")
|
||||
.and(config.hideCheckbox.enabled ? "be.enabled" : "be.disabled");
|
||||
}
|
||||
|
||||
if (resourceType === "workflow") {
|
||||
cy.get(groupsSelector.buildWorkflowradio)
|
||||
.should("be.visible")
|
||||
.and(config.buildRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(config.buildRadio.enabled ? "be.enabled" : "be.disabled");
|
||||
|
||||
cy.get(groupsSelector.executeWorkflowradio)
|
||||
.should("be.visible")
|
||||
.and(config.executeRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(config.executeRadio.enabled ? "be.enabled" : "be.disabled");
|
||||
}
|
||||
|
||||
if (resourceType === "datasource") {
|
||||
cy.get(groupsSelector.configureDatasourceradio)
|
||||
.should("be.visible")
|
||||
.and(config.configureRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(config.configureRadio.enabled ? "be.enabled" : "be.disabled");
|
||||
|
||||
cy.get(groupsSelector.buildWithDatasourceRadio)
|
||||
.should("be.visible")
|
||||
.and(config.buildWithRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(config.buildWithRadio.enabled ? "be.enabled" : "be.disabled");
|
||||
}
|
||||
|
||||
cy.get(groupsSelector.allAppsRadio)
|
||||
.should("be.visible")
|
||||
.and(config.allAppsRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(config.allAppsRadio.enabled ? "be.enabled" : "be.disabled");
|
||||
|
||||
cy.get(groupsSelector.customRadio)
|
||||
.should("be.visible")
|
||||
.and(config.customRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(config.customRadio.enabled ? "be.enabled" : "be.disabled");
|
||||
};
|
||||
|
||||
export const verifyEmptyStates = (customGroup = false) => {
|
||||
// Users empty state
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
cy.get('[data-cy="user-group-search-btn"]').should("be.visible");
|
||||
cy.verifyElement(
|
||||
groupsSelector.nameTableHeader,
|
||||
groupsText.userNameTableHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.emailTableHeader,
|
||||
groupsText.emailTableHeader
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.userEmptyPageIcon).should("be.visible");
|
||||
cy.get(groupsSelector.userEmptyPageTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.userEmptyPageTitle
|
||||
);
|
||||
cy.get(groupsSelector.userEmptyPageHelperText).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.userEmptyPageHelperText
|
||||
);
|
||||
|
||||
if (customGroup) {
|
||||
// Granular permissions empty state
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
cy.get(groupsSelector.granularEmptyPageIcon).should("be.visible");
|
||||
cy.get(groupsSelector.emptyPagePermissionTitle).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.emptyPagePermissionTitle
|
||||
);
|
||||
cy.get(groupsSelector.emptyPagePermissionHelperText).verifyVisibleElement(
|
||||
"have.text",
|
||||
groupsText.emptyPagePermissionHelperText
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const verifyGroupLinks = () => {
|
||||
const links = [
|
||||
{ selector: groupsSelector.usersLink, text: groupsText.usersLink },
|
||||
{
|
||||
selector: groupsSelector.permissionsLink,
|
||||
text: groupsText.permissionsLink,
|
||||
},
|
||||
{ selector: groupsSelector.granularLink, text: "Granular access" },
|
||||
];
|
||||
|
||||
links.forEach(({ selector, text }) => {
|
||||
cy.get(selector).verifyVisibleElement("have.text", text);
|
||||
});
|
||||
};
|
||||
|
||||
export const commonGroupVerification = () => {
|
||||
cy.verifyElement(
|
||||
groupsSelector.textDefaultGroup,
|
||||
groupsText.textDefaultGroup
|
||||
);
|
||||
cy.verifyElement(groupsSelector.usersLink, groupsText.usersLink);
|
||||
cy.verifyElement(groupsSelector.permissionsLink, groupsText.permissionsLink);
|
||||
cy.verifyElement(groupsSelector.granularLink, "Granular access");
|
||||
|
||||
cy.get(groupsSelector.usersLink).click();
|
||||
cy.verifyElement(
|
||||
groupsSelector.nameTableHeader,
|
||||
groupsText.userNameTableHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.emailTableHeader,
|
||||
groupsText.emailTableHeader
|
||||
);
|
||||
};
|
||||
|
||||
export const permissions =
|
||||
Cypress.env("environment") === "Community"
|
||||
? [
|
||||
groupsSelector.appsCreateCheck,
|
||||
groupsSelector.appsDeleteCheck,
|
||||
groupsSelector.foldersCreateCheck,
|
||||
groupsSelector.workspaceVarCheckbox,
|
||||
]
|
||||
: [
|
||||
groupsSelector.appsCreateCheck,
|
||||
groupsSelector.appsDeleteCheck,
|
||||
groupsSelector.appPromoteCheck,
|
||||
groupsSelector.appReleaseCheck,
|
||||
groupsSelector.workflowsCreateCheck,
|
||||
groupsSelector.workflowsDeleteCheck,
|
||||
groupsSelector.datasourcesCreateCheck,
|
||||
groupsSelector.datasourcesDeleteCheck,
|
||||
groupsSelector.foldersCreateCheck,
|
||||
groupsSelector.workspaceVarCheckbox,
|
||||
];
|
||||
|
||||
export const verifyCheckPermissionStates = (roleType, action = null) => {
|
||||
const roleConfig = {
|
||||
admin: { checked: true, enabled: false },
|
||||
enduser: { checked: false, enabled: false },
|
||||
builder: { checked: true, enabled: true },
|
||||
custom: { checked: false, enabled: true },
|
||||
};
|
||||
|
||||
const config = roleConfig[roleType];
|
||||
|
||||
permissions.forEach((permissionSelector) => {
|
||||
cy.get(permissionSelector)
|
||||
.should("be.visible")
|
||||
.and(config.checked ? "be.checked" : "not.be.checked")
|
||||
.and(config.enabled ? "be.enabled" : "be.disabled");
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyPermissionCheckBoxLabelsAndHelperTexts = () => {
|
||||
const commonPermissions = [
|
||||
{ selector: groupsSelector.resourcesApps, text: groupsText.resourcesApps },
|
||||
{
|
||||
selector: groupsSelector.permissionsTableHeader,
|
||||
text: groupsText.permissionsTableHeader,
|
||||
},
|
||||
{ selector: groupsSelector.appsCreateLabel, text: groupsText.createLabel },
|
||||
{
|
||||
selector: groupsSelector.appCreateHelperText,
|
||||
text: groupsText.appCreateHelperText,
|
||||
},
|
||||
{ selector: groupsSelector.appsDeleteLabel, text: groupsText.deleteLabel },
|
||||
{
|
||||
selector: groupsSelector.appDeleteHelperText,
|
||||
text: groupsText.appDeleteHelperText,
|
||||
},
|
||||
{
|
||||
selector: groupsSelector.resourcesFolders,
|
||||
text: groupsText.resourcesFolders,
|
||||
},
|
||||
{
|
||||
selector: groupsSelector.foldersCreateLabel,
|
||||
text: groupsText.folderCreateLabel,
|
||||
},
|
||||
{
|
||||
selector: groupsSelector.foldersHelperText,
|
||||
text: groupsText.folderHelperText,
|
||||
},
|
||||
{
|
||||
selector: groupsSelector.resourcesWorkspaceVar,
|
||||
text: groupsText.resourcesWorkspaceVar,
|
||||
},
|
||||
{
|
||||
selector: groupsSelector.workspaceCreateLabel,
|
||||
text: groupsText.workspaceCreateLabel,
|
||||
},
|
||||
{
|
||||
selector: groupsSelector.workspaceHelperText,
|
||||
text: groupsText.workspaceHelperText,
|
||||
},
|
||||
];
|
||||
|
||||
commonPermissions.forEach(({ selector, text }) => {
|
||||
cy.verifyElement(selector, text);
|
||||
});
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
const enterprisePermissions = [
|
||||
{ selector: groupsSelector.appPromoteLabel, text: "Promote" },
|
||||
{
|
||||
selector: groupsSelector.appPromoteHelperText,
|
||||
text: "Promote any app in this workspace",
|
||||
},
|
||||
{ selector: groupsSelector.appReleaseLabel, text: "Release" },
|
||||
{
|
||||
selector: groupsSelector.appReleaseHelperText,
|
||||
text: "Release any app in this workspace",
|
||||
},
|
||||
{ selector: groupsSelector.workflowsCreateLabel, text: "Create" },
|
||||
{
|
||||
selector: groupsSelector.workflowsCreateHelperText,
|
||||
text: "Create workflow in this workspace",
|
||||
},
|
||||
{ selector: groupsSelector.workflowsDeleteLabel, text: "Delete" },
|
||||
{
|
||||
selector: groupsSelector.workflowsDeleteHelperText,
|
||||
text: "Delete any workflow in this workspace",
|
||||
},
|
||||
{ selector: groupsSelector.datasourcesCreateLabel, text: "Create" },
|
||||
{
|
||||
selector: groupsSelector.datasourcesCreateHelperText,
|
||||
text: "Create data source connections in this workspace",
|
||||
},
|
||||
{ selector: groupsSelector.datasourcesDeleteLabel, text: "Delete" },
|
||||
{
|
||||
selector: groupsSelector.datasourcesDeleteHelperText,
|
||||
text: "Delete any data source in this workspace",
|
||||
},
|
||||
];
|
||||
|
||||
enterprisePermissions.forEach(({ selector, text }) => {
|
||||
cy.verifyElement(selector, text);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyGranularAccessByRole = (role) => {
|
||||
const roleConfig = {
|
||||
admin: {
|
||||
appEditRadio: { checked: true, enabled: false },
|
||||
appViewRadio: { checked: false, enabled: false },
|
||||
appHideCheckbox: { enabled: false },
|
||||
workflowBuildRadio: { checked: true, enabled: false },
|
||||
workflowExecuteRadio: { checked: false, enabled: false },
|
||||
datasourceConfigureRadio: { checked: true, enabled: false },
|
||||
datasourceBuildWithRadio: { checked: false, enabled: false },
|
||||
addButtonEnabled: false,
|
||||
verifyHelperTexts: true,
|
||||
hasDatasource: true,
|
||||
},
|
||||
builder: {
|
||||
appEditRadio: { checked: true, enabled: true },
|
||||
appViewRadio: { checked: false, enabled: true },
|
||||
appHideCheckbox: { enabled: false },
|
||||
workflowBuildRadio: { checked: true, enabled: true },
|
||||
workflowExecuteRadio: { checked: false, enabled: true },
|
||||
datasourceConfigureRadio: { checked: true, enabled: true },
|
||||
datasourceBuildWithRadio: { checked: false, enabled: true },
|
||||
addButtonEnabled: true,
|
||||
verifyHelperTexts: false,
|
||||
hasDatasource: true,
|
||||
},
|
||||
enduser: {
|
||||
appEditRadio: { checked: false, enabled: false },
|
||||
appViewRadio: { checked: true, enabled: false },
|
||||
appHideCheckbox: { enabled: true },
|
||||
workflowBuildRadio: { checked: false, enabled: false },
|
||||
workflowExecuteRadio: { checked: true, enabled: false },
|
||||
addButtonEnabled: true,
|
||||
verifyHelperTexts: false,
|
||||
hasDatasource: false,
|
||||
},
|
||||
};
|
||||
|
||||
const config = roleConfig[role];
|
||||
|
||||
cy.get(groupsSelector.granularLink).click();
|
||||
|
||||
if (role === "admin") {
|
||||
cy.verifyElement(
|
||||
groupsSelector.nameTableHeader,
|
||||
groupsText.nameTableHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionsTableHeader,
|
||||
groupsText.granularAccessPermissionHeader
|
||||
);
|
||||
cy.verifyElement(
|
||||
`${groupsSelector.resourceHeader}:eq(1)`,
|
||||
groupsText.resourcesTableHeader
|
||||
);
|
||||
}
|
||||
|
||||
cy.verifyElement(groupsSelector.appsText, " Apps");
|
||||
|
||||
cy.get(groupsSelector.appEditRadio)
|
||||
.should("be.visible")
|
||||
.and(config.appEditRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(
|
||||
config.appEditRadio.enabled ? "be.enabled" : "have.attr",
|
||||
config.appEditRadio.enabled ? "" : "disabled"
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.appViewRadio)
|
||||
.should("be.visible")
|
||||
.and(
|
||||
config.appViewRadio.enabled ? "be.enabled" : "have.attr",
|
||||
config.appViewRadio.enabled ? "" : "disabled"
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.appHideCheckbox)
|
||||
.should("be.visible")
|
||||
.and(config.appHideCheckbox.enabled ? "be.enabled" : "be.disabled");
|
||||
|
||||
if (config.verifyHelperTexts) {
|
||||
cy.verifyElement(groupsSelector.appEditLabel, groupsText.appEditLabelText);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appEditHelperText,
|
||||
groupsText.appEditHelperText
|
||||
);
|
||||
cy.verifyElement(groupsSelector.appViewLabel, groupsText.appViewLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appViewHelperText,
|
||||
groupsText.appViewHelperText
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.appHideHelperText,
|
||||
groupsText.appHideHelperText
|
||||
);
|
||||
}
|
||||
|
||||
cy.verifyElement(groupsSelector.groupChip("All apps"), "All apps");
|
||||
|
||||
cy.ifEnv("Community", () => {
|
||||
cy.get(groupsSelector.addAppButton).should(
|
||||
config.addButtonEnabled ? "be.enabled" : "be.disabled"
|
||||
);
|
||||
});
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.verifyElement(groupsSelector.workflowsText, "Workflows");
|
||||
|
||||
cy.get(groupsSelector.workflowsBuildRadio)
|
||||
.should("be.visible")
|
||||
.and(config.workflowBuildRadio.checked ? "be.checked" : "not.be.checked")
|
||||
.and(
|
||||
config.workflowBuildRadio.enabled ? "be.enabled" : "have.attr",
|
||||
config.workflowBuildRadio.enabled ? "" : "disabled"
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.workflowsExecuteRadio)
|
||||
.should("be.visible")
|
||||
.and(
|
||||
config.workflowExecuteRadio.enabled ? "be.enabled" : "have.attr",
|
||||
config.workflowExecuteRadio.enabled ? "" : "disabled"
|
||||
);
|
||||
|
||||
if (config.verifyHelperTexts) {
|
||||
cy.verifyElement(groupsSelector.workflowsBuildLabel, "Build");
|
||||
cy.verifyElement(
|
||||
groupsSelector.workflowsBuildHelperText,
|
||||
"Access to workflow builder"
|
||||
);
|
||||
cy.verifyElement(groupsSelector.workflowsExecuteLabel, "Execute");
|
||||
cy.verifyElement(
|
||||
groupsSelector.workflowsExecuteHelperText,
|
||||
"Only able to execute the workflow"
|
||||
);
|
||||
}
|
||||
|
||||
cy.verifyElement(
|
||||
groupsSelector.groupChip("All workflows"),
|
||||
"All workflows"
|
||||
);
|
||||
|
||||
if (config.hasDatasource) {
|
||||
cy.verifyElement(groupsSelector.datasourcesText, " Data sources");
|
||||
|
||||
cy.get(groupsSelector.datasourcesConfigureRadio)
|
||||
.should("be.visible")
|
||||
.and(
|
||||
config.datasourceConfigureRadio.checked
|
||||
? "be.checked"
|
||||
: "not.be.checked"
|
||||
)
|
||||
.and(
|
||||
config.datasourceConfigureRadio.enabled ? "be.enabled" : "have.attr",
|
||||
config.datasourceConfigureRadio.enabled ? "" : "disabled"
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.datasourcesBuildWithRadio)
|
||||
.should("be.visible")
|
||||
.and(
|
||||
config.datasourceBuildWithRadio.enabled ? "be.enabled" : "have.attr",
|
||||
config.datasourceBuildWithRadio.enabled ? "" : "disabled"
|
||||
);
|
||||
|
||||
if (config.verifyHelperTexts) {
|
||||
cy.verifyElement(groupsSelector.datasourcesConfigureLabel, "Configure");
|
||||
cy.verifyElement(
|
||||
groupsSelector.datasourcesConfigureHelperText,
|
||||
"Access & edit connection details"
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.datasourcesBuildWithLabel,
|
||||
"Build with"
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.datasourcesBuildWithHelperText,
|
||||
"Use in apps & workflows"
|
||||
);
|
||||
}
|
||||
|
||||
cy.verifyElement(
|
||||
groupsSelector.groupChip("All data source"),
|
||||
"All data sources"
|
||||
);
|
||||
}
|
||||
|
||||
cy.verifyElement(groupsSelector.addPermissionButton, "Add permission");
|
||||
cy.get(groupsSelector.addPermissionButton).should(
|
||||
config.addButtonEnabled ? "be.enabled" : "be.disabled"
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const permissionModal = () => {
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionNameLabel,
|
||||
groupsText.permissionNameLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.permissionNameHelperText,
|
||||
groupsText.permissionNameHelperText
|
||||
);
|
||||
|
||||
cy.verifyElement(groupsSelector.permissionLabel, groupsText.permissionLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.editPermissionLabel,
|
||||
groupsText.editPermissionLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.editPermissionHelperText,
|
||||
groupsText.editPermissionHelperText
|
||||
);
|
||||
|
||||
cy.verifyElement(
|
||||
groupsSelector.viewPermissionLabel,
|
||||
groupsText.viewPermissionLabel
|
||||
);
|
||||
cy.verifyElement(
|
||||
groupsSelector.viewPermissionHelperText,
|
||||
groupsText.viewPermissionHelperText
|
||||
);
|
||||
|
||||
cy.get(groupsSelector.hidePermissionInput).should("be.visible");
|
||||
cy.verifyElement(groupsSelector.resourceLabel, groupsText.resourcesheader);
|
||||
cy.get(groupsSelector.resourceContainer).should("be.visible");
|
||||
cy.get(groupsSelector.allAppsRadio).should("be.visible").and("be.checked");
|
||||
cy.verifyElement(groupsSelector.allAppsLabel, groupsText.allAppsLabel);
|
||||
cy.verifyElement(
|
||||
groupsSelector.allAppsHelperText,
|
||||
groupsText.allAppsHelperText
|
||||
);
|
||||
};
|
||||
|
||||
export const verifyUserRow = (name, email) => {
|
||||
cy.get('[data-cy="avatar-image"]').should("be.visible");
|
||||
cy.get('[data-cy="user-name"]')
|
||||
.should("be.visible")
|
||||
.and("contain.text", name);
|
||||
cy.get('[data-cy="user-email"]').should("be.visible").and("have.text", email);
|
||||
};
|
||||
|
|
@ -109,6 +109,7 @@ export const selectEnv = (envName) => {
|
|||
};
|
||||
|
||||
if (isValidEnvName(envName)) {
|
||||
cy.wait(1000)
|
||||
cy.get('[data-cy="list-current-env-name"]').click();
|
||||
cy.wait(500)
|
||||
const envSelector = `${multiEnvSelector.envNameList}:eq(${envIndex})`;
|
||||
|
|
|
|||
206
cypress-tests/cypress/support/utils/uiPermissions.js
Normal file
206
cypress-tests/cypress/support/utils/uiPermissions.js
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { workflowSelector } from "Selectors/workflows";
|
||||
import { deleteFolder } from "Support/utils/common";
|
||||
import {
|
||||
addAndVerifyConstants,
|
||||
deleteConstant,
|
||||
} from "Support/utils/workspaceConstants";
|
||||
import { commonText } from "Texts/common";
|
||||
|
||||
export const uiCreateApp = (appName) => {
|
||||
cy.createApp(appName);
|
||||
cy.wait(2000);
|
||||
cy.go("back");
|
||||
};
|
||||
|
||||
export const uiVerifyAppCreated = (appName, shouldExist = true) => {
|
||||
const assertion = shouldExist ? "exist" : "not.exist";
|
||||
cy.get(commonSelectors.appCard(appName)).should(assertion);
|
||||
};
|
||||
|
||||
export const uiDeleteApp = (appName) => {
|
||||
cy.deleteApp(appName);
|
||||
};
|
||||
|
||||
export const uiVerifyAppDeleted = (appName) => {
|
||||
cy.get(commonSelectors.appCard(appName)).should("not.exist");
|
||||
};
|
||||
|
||||
export const uiVerifyAppCreatePrivilege = (hasPrivilege = true) => {
|
||||
const assertion = hasPrivilege ? "be.enabled" : "be.disabled";
|
||||
cy.get(commonSelectors.dashboardAppCreateButton).should(assertion);
|
||||
};
|
||||
|
||||
export const uiCreateFolder = (folderName) => {
|
||||
cy.get(commonSelectors.createNewFolderButton).click();
|
||||
cy.clearAndType(commonSelectors.folderNameInput, folderName);
|
||||
cy.get(commonSelectors.createFolderButton).click();
|
||||
};
|
||||
|
||||
export const uiVerifyFolderCreated = (folderName) => {
|
||||
cy.get(commonSelectors.folderListcard(folderName)).should("exist");
|
||||
};
|
||||
|
||||
export const uiVerifyFolderDeleted = (folderName) => {
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.folderDeletedToast
|
||||
);
|
||||
cy.get(commonSelectors.folderListcard(folderName)).should("not.exist");
|
||||
};
|
||||
|
||||
export const uiVerifyFolderCreatePrivilege = (hasPrivilege = true) => {
|
||||
const assertion = hasPrivilege ? "exist" : "not.exist";
|
||||
cy.get(commonSelectors.createNewFolderButton).should(assertion);
|
||||
};
|
||||
|
||||
export const uiVerifyWorkspaceConstantCreatePrivilege = (
|
||||
hasPrivilege = true
|
||||
) => {
|
||||
const assertion = hasPrivilege ? "exist" : "not.exist";
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).should(assertion);
|
||||
};
|
||||
|
||||
export const uiCreateDataSource = (
|
||||
datasourceName,
|
||||
datasourceType = "restapi"
|
||||
) => {
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
// cy.get(commonSelectors.addNewDataSourceButton).click();
|
||||
cy.get('[data-cy="rest-api-add-button"]').eq(0).click({ force: true });
|
||||
};
|
||||
|
||||
export const uiVerifyDataSourceCreated = (datasourceName) => {
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "Data Source Added");
|
||||
cy.get('[data-cy="restapi-button"]').should("exist");
|
||||
};
|
||||
|
||||
export const uiDeleteDataSource = (datasourceName) => {
|
||||
cy.get('[data-cy="restapi-delete-button"]').click({ force: true });
|
||||
cy.get(commonSelectors.yesButton).click();
|
||||
};
|
||||
|
||||
export const uiVerifyDataSourceDeleted = (datasourceName) => {
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "Data Source Deleted");
|
||||
cy.get('[data-cy="restapi-button"]').should("not.exist");
|
||||
};
|
||||
|
||||
export const uiVerifyDataSourceCreatePrivilege = (hasPrivilege = true) => {
|
||||
const assertion = hasPrivilege ? "exist" : "not.exist";
|
||||
cy.get(commonSelectors.globalDataSourceIcon).should(assertion);
|
||||
};
|
||||
|
||||
export const uiCreateWorkflow = (workflowName) => {
|
||||
cy.get(workflowSelector.globalWorkFlowsIcon).click();
|
||||
|
||||
cy.get('[data-cy="button-new-workflow-from-scratch"]').click();
|
||||
cy.get(workflowSelector.workFlowNameInputField).type(workflowName);
|
||||
cy.get(workflowSelector.createWorkFlowsButton).click();
|
||||
cy.wait(2000);
|
||||
cy.go("back");
|
||||
};
|
||||
|
||||
export const uiVerifyWorkflowCreated = (workflowName) => {
|
||||
cy.get(commonSelectors.globalWorkFlowsIcon).click();
|
||||
cy.get(`[data-cy="${workflowName.toLowerCase()}-card"]`)
|
||||
.contains(workflowName)
|
||||
.should("exist");
|
||||
};
|
||||
|
||||
export const uiDeleteWorkflow = () => {
|
||||
cy.get(".homepage-app-card .home-app-card-header .menu-ico").then(($el) => {
|
||||
$el[0].style.setProperty("visibility", "visible", "important");
|
||||
});
|
||||
|
||||
cy.get('[data-cy="app-card-menu-icon"]').click();
|
||||
cy.get(workflowSelector.deleteWorkFlowOption).click();
|
||||
cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click();
|
||||
};
|
||||
|
||||
export const uiVerifyWorkflowDeleted = (workflowName) => {
|
||||
cy.get(`[data-cy="${workflowName.toLowerCase()}-card"]`).should("not.exist");
|
||||
};
|
||||
|
||||
export const uiVerifyWorkflowCreatePrivilege = (hasPrivilege = true) => {
|
||||
const assertion = hasPrivilege ? "exist" : "not.exist";
|
||||
cy.get(commonSelectors.globalWorkFlowsIcon).should(assertion);
|
||||
};
|
||||
|
||||
export const uiVerifyAllCreatePrivileges = (
|
||||
hasAppCreate = true,
|
||||
hasFolderCreate = true,
|
||||
hasConstantCreate = true,
|
||||
hasDataSourceCreate = true,
|
||||
hasWorkflowCreate = true
|
||||
) => {
|
||||
uiVerifyAppCreatePrivilege(hasAppCreate);
|
||||
uiVerifyFolderCreatePrivilege(hasFolderCreate);
|
||||
uiVerifyWorkspaceConstantCreatePrivilege(hasConstantCreate);
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
uiVerifyDataSourceCreatePrivilege(hasDataSourceCreate);
|
||||
uiVerifyWorkflowCreatePrivilege(hasWorkflowCreate);
|
||||
});
|
||||
};
|
||||
|
||||
export const uiVerifyBuilderPrivileges = () => {
|
||||
uiVerifyAllCreatePrivileges(true, true, true, true, true);
|
||||
};
|
||||
|
||||
export const uiVerifyAdminPrivileges = () => {
|
||||
uiVerifyAllCreatePrivileges(true, true, true, true, true);
|
||||
cy.get(commonSelectors.settingsIcon).click();
|
||||
cy.get(commonSelectors.workspaceSettings).should("exist");
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
};
|
||||
|
||||
export const uiAppCRUDWorkflow = (appName) => {
|
||||
uiCreateApp(appName);
|
||||
uiVerifyAppCreated(appName, true);
|
||||
|
||||
uiDeleteApp(appName);
|
||||
uiVerifyAppDeleted(appName);
|
||||
};
|
||||
|
||||
export const uiFolderCRUDWorkflow = (folderName) => {
|
||||
uiCreateFolder(folderName);
|
||||
uiVerifyFolderCreated(folderName);
|
||||
|
||||
deleteFolder(folderName);
|
||||
uiVerifyFolderDeleted(folderName);
|
||||
};
|
||||
|
||||
export const uiWorkspaceConstantCRUDWorkflow = (
|
||||
constantName,
|
||||
constantValue
|
||||
) => {
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).click();
|
||||
|
||||
addAndVerifyConstants(constantName, constantValue);
|
||||
deleteConstant(constantName);
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
};
|
||||
|
||||
export const uiDataSourceCRUDWorkflow = (
|
||||
datasourceName,
|
||||
datasourceType = "restapi"
|
||||
) => {
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
uiCreateDataSource(datasourceName, datasourceType);
|
||||
uiVerifyDataSourceCreated(datasourceName);
|
||||
|
||||
uiDeleteDataSource(datasourceName);
|
||||
uiVerifyDataSourceDeleted(datasourceName);
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
});
|
||||
};
|
||||
|
||||
export const uiWorkflowCRUDWorkflow = (workflowName) => {
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
uiCreateWorkflow(workflowName);
|
||||
uiVerifyWorkflowCreated(workflowName);
|
||||
|
||||
uiDeleteWorkflow();
|
||||
uiVerifyWorkflowDeleted(workflowName);
|
||||
});
|
||||
};
|
||||
|
|
@ -1,42 +1,8 @@
|
|||
import { commonSelectors } from "Selectors/common";
|
||||
import { commonText } from "Texts/common";
|
||||
import { workspaceConstantsSelectors } from "Selectors/workspaceConstants";
|
||||
import { createFolder, deleteFolder } from "Support/utils/common";
|
||||
import { addAndVerifyConstants } from "Support/utils/workspaceConstants";
|
||||
|
||||
const appOperations = {
|
||||
createApp: (appName) => {
|
||||
cy.createApp(appName);
|
||||
cy.backToApps();
|
||||
},
|
||||
|
||||
deleteApp: (appName) => {
|
||||
cy.deleteApp(appName);
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.appDeletedToast
|
||||
);
|
||||
},
|
||||
|
||||
cloneApp: (appName) => {
|
||||
cy.get(commonSelectors.appCard(appName))
|
||||
.trigger("mouseover")
|
||||
.find(commonSelectors.cloneButton)
|
||||
.click();
|
||||
},
|
||||
};
|
||||
|
||||
const folderOperations = {
|
||||
createFolder: (folderName) => {
|
||||
createFolder(folderName);
|
||||
},
|
||||
|
||||
deleteFolder: (folderName) => {
|
||||
deleteFolder(folderName);
|
||||
},
|
||||
};
|
||||
|
||||
const constantsOperations = {
|
||||
export const constantsOperations = {
|
||||
createConstant: (name, value) => {
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).click();
|
||||
addAndVerifyConstants(name, value);
|
||||
|
|
@ -49,7 +15,7 @@ const constantsOperations = {
|
|||
};
|
||||
|
||||
// Permission verification helpers
|
||||
const verifyPermissions = {
|
||||
export const verifyPermissions = {
|
||||
checkAppPermissions: (shouldExist = true) => {
|
||||
const assertion = shouldExist ? "exist" : "not.exist";
|
||||
cy.get(commonSelectors.appCreateButton).should(assertion);
|
||||
|
|
@ -73,41 +39,89 @@ const verifyPermissions = {
|
|||
},
|
||||
};
|
||||
|
||||
// Helper function to perform all verifications
|
||||
const verifyAllPermissions = (shouldHaveAccess = true) => {
|
||||
verifyPermissions.checkAppPermissions(shouldHaveAccess);
|
||||
verifyPermissions.checkFolderPermissions(shouldHaveAccess);
|
||||
verifyPermissions.checkConstantsPermissions(shouldHaveAccess);
|
||||
verifyPermissions.checkSettingsAccess(shouldHaveAccess);
|
||||
export const getGroupPermissionInput = (isEnterprise, flag) => {
|
||||
return isEnterprise
|
||||
? {
|
||||
appCreate: flag,
|
||||
appDelete: flag,
|
||||
appPromote: flag,
|
||||
appRelease: flag,
|
||||
workflowCreate: flag,
|
||||
workflowDelete: flag,
|
||||
dataSourceCreate: flag,
|
||||
dataSourceDelete: flag,
|
||||
folderCRUD: flag,
|
||||
orgConstantCRUD: flag,
|
||||
}
|
||||
: {
|
||||
appCreate: flag,
|
||||
appDelete: flag,
|
||||
folderCRUD: flag,
|
||||
orgConstantCRUD: flag,
|
||||
};
|
||||
};
|
||||
|
||||
// Role-based permission sets
|
||||
const rolePermissions = {
|
||||
admin: {
|
||||
name: "Admin",
|
||||
hasFullAccess: true,
|
||||
canManageWorkspace: true,
|
||||
canManageUsers: true,
|
||||
},
|
||||
builder: {
|
||||
name: "Builder",
|
||||
hasFullAccess: true,
|
||||
canManageWorkspace: false,
|
||||
canManageUsers: false,
|
||||
},
|
||||
endUser: {
|
||||
name: "End User",
|
||||
hasFullAccess: false,
|
||||
canManageWorkspace: false,
|
||||
canManageUsers: false,
|
||||
},
|
||||
export const verifyBuilderPermissions = (
|
||||
appName,
|
||||
folderName,
|
||||
constName,
|
||||
constValue,
|
||||
isAdmin = false
|
||||
) => {
|
||||
verifyBasicPermissions(true);
|
||||
|
||||
// App operations
|
||||
cy.apiCreateApp(appName);
|
||||
cy.apiDeleteApp();
|
||||
|
||||
// Folder operations
|
||||
cy.apiCreateFolder(folderName);
|
||||
cy.apiDeleteFolder();
|
||||
|
||||
// Constants management
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).click();
|
||||
addAndVerifyConstants(constName, constValue);
|
||||
cy.get(workspaceConstantsSelectors.constDeleteButton(constName)).click();
|
||||
cy.get(commonSelectors.yesButton).click();
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
appName,
|
||||
"restapi",
|
||||
[{ key: "url", value: "https://jsonplaceholder.typicode.com/users" }]
|
||||
);
|
||||
cy.apiDeleteGDS(appName);
|
||||
|
||||
cy.apiCreateWorkflow(appName);
|
||||
cy.apiDeleteWorkflow(appName);
|
||||
});
|
||||
|
||||
verifySettingsAccess(isAdmin);
|
||||
};
|
||||
|
||||
export {
|
||||
appOperations,
|
||||
folderOperations,
|
||||
constantsOperations,
|
||||
verifyPermissions,
|
||||
verifyAllPermissions,
|
||||
rolePermissions,
|
||||
export const verifyBasicPermissions = (canCreate = true) => {
|
||||
cy.get(commonSelectors.dashboardAppCreateButton).should(
|
||||
canCreate ? "be.enabled" : "be.disabled"
|
||||
);
|
||||
cy.get(commonSelectors.createNewFolderButton).should(
|
||||
canCreate ? "exist" : "not.exist"
|
||||
);
|
||||
cy.get('[data-cy="database-icon"]').should(canCreate ? "exist" : "not.exist");
|
||||
|
||||
cy.ifEnv("Enterprise", () => {
|
||||
cy.get(commonSelectors.globalDataSourceIcon).should(
|
||||
canCreate ? "exist" : "not.exist"
|
||||
);
|
||||
cy.get(commonSelectors.workspaceConstantsIcon).should(
|
||||
canCreate ? "exist" : "not.exist"
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const verifySettingsAccess = (shouldExist = true) => {
|
||||
cy.get(commonSelectors.settingsIcon).click();
|
||||
cy.get(commonSelectors.workspaceSettings).should(
|
||||
shouldExist ? "exist" : "not.exist"
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
import { appVersionText } from "Texts/exportImport";
|
||||
import { appVersionSelectors } from "Selectors/exportImport";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { commonText } from "Texts/common";
|
||||
import { verifyModal, closeModal } from "Support/utils/common";
|
||||
import { appVersionSelectors } from "Selectors/exportImport";
|
||||
import {
|
||||
confirmVersionModalSelectors,
|
||||
editVersionSelectors,
|
||||
} from "Selectors/version";
|
||||
import { closeModal } from "Support/utils/common";
|
||||
import { commonText } from "Texts/common";
|
||||
import { appVersionText } from "Texts/exportImport";
|
||||
import { deleteVersionText, releasedVersionText } from "Texts/version";
|
||||
import { verifyComponent } from "Support/utils/basicComponents";
|
||||
import { appPromote } from "./platform/multiEnv";
|
||||
|
||||
export const navigateToCreateNewVersionModal = (value) => {
|
||||
cy.get(appVersionSelectors.appVersionLabel).click();
|
||||
cy.contains(appVersionText.createNewVersion).should("be.visible");
|
||||
cy.contains(appVersionText.createNewVersion).click();
|
||||
cy.contains(appVersionText.createNewVersion).first().should("be.visible");
|
||||
cy.contains(appVersionText.createNewVersion).first().click();
|
||||
};
|
||||
|
||||
export const navigateToEditVersionModal = (value) => {
|
||||
|
|
@ -48,7 +47,7 @@ export const verifyElementsOfCreateNewVersionModal = (version = []) => {
|
|||
);
|
||||
cy.get(
|
||||
commonSelectors.buttonSelector(appVersionText.createNewVersion)
|
||||
).verifyVisibleElement("have.text", appVersionText.createNewVersion);
|
||||
).first().verifyVisibleElement("have.text", appVersionText.createNewVersion);
|
||||
cy.get(commonSelectors.buttonSelector(commonText.cancelButton))
|
||||
.should("be.visible")
|
||||
.and("have.text", commonText.cancelButton);
|
||||
|
|
@ -113,7 +112,7 @@ export const verifyDuplicateVersion = (newVersion = [], version) => {
|
|||
cy.get(appVersionSelectors.createVersionInputField).click();
|
||||
cy.contains(`[id*="react-select-"]`, version).click();
|
||||
cy.clearAndType(appVersionSelectors.versionNameInputField, newVersion[0]);
|
||||
cy.get(appVersionSelectors.createNewVersionButton).click();
|
||||
cy.get(appVersionSelectors.createNewVersionButton).first().click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
appVersionText.versionNameAlreadyExists
|
||||
|
|
@ -160,4 +159,10 @@ export const switchVersionAndVerify = (currentVersion, newVersion) => {
|
|||
.should("be.visible")
|
||||
.click();
|
||||
cy.get(".app-version-name").contains(newVersion).click();
|
||||
cy.wait('@appDs')
|
||||
|
||||
};
|
||||
|
||||
export const openPreviewSettings = () => {
|
||||
cy.get(commonSelectors.previewSettings).should("be.visible").click();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,23 +40,23 @@ export const revealWorkflowToken = (selectors) => {
|
|||
};
|
||||
|
||||
export const importWorkflowApp = (
|
||||
wfName,
|
||||
workflowName,
|
||||
fixturePath = "cypress/fixtures/exportedApp.json"
|
||||
) => {
|
||||
cy.get(workflowSelector.importWorkFlowsOption).click();
|
||||
cy.get(workflowSelector.importWorkFlowsLabel).click();
|
||||
cy.get('input[type="file"]').first().selectFile(fixturePath, { force: true });
|
||||
cy.wait(2000);
|
||||
cy.get(workflowSelector.workFlowNameInputField).clear().type(wfName);
|
||||
cy.get(workflowSelector.workFlowNameInputField).clear().type(workflowName);
|
||||
cy.get(workflowSelector.importWorkFlowsButton).click();
|
||||
};
|
||||
|
||||
export const deleteAppandWorkflowAfterExecution = (wfName, appName) => {
|
||||
export const deleteAppandWorkflowAfterExecution = (workflowName, appName) => {
|
||||
cy.backToApps();
|
||||
cy.deleteApp(appName);
|
||||
cy.get(workflowSelector.globalWorkFlowsIcon).click();
|
||||
cy.intercept("DELETE", "/api/apps/*").as("appDeleted");
|
||||
cy.get(commonSelectors.appCard(wfName))
|
||||
cy.get(commonSelectors.appCard(workflowName))
|
||||
.realHover()
|
||||
.find(commonSelectors.appCardOptionsButton)
|
||||
.realHover()
|
||||
|
|
|
|||
307
cypress-tests/package-lock.json
generated
307
cypress-tests/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -10,13 +10,14 @@
|
|||
"@cypress/code-coverage": "^3.12.12",
|
||||
"@cypress/webpack-preprocessor": "^5.12.0",
|
||||
"@faker-js/faker": "^7.3.0",
|
||||
"cypress": "^15.0.0",
|
||||
"cypress": "^15.4.0",
|
||||
"cypress-mailhog": "^2.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cypress-real-events": "^1.15.0",
|
||||
"moment": "^2.29.4",
|
||||
"node-xlsx": "^0.4.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"pg": "^8.8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ fi
|
|||
|
||||
TOOLJET_EDTION=ee npm --prefix server run db:setup
|
||||
|
||||
if sudo -E systemctl start nest && && sudo journalctl -u nest -f
|
||||
if sudo -E systemctl start nest && sudo journalctl -u nest -f
|
||||
then
|
||||
echo "The app will be served at ${TOOLJET_HOST}"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -168,6 +168,12 @@ RUN mkdir -p /home/appuser \
|
|||
&& chmod -R g=u /home/appuser \
|
||||
&& npm cache clean --force
|
||||
|
||||
# Create rsyslog directory for audit logs with proper permissions
|
||||
RUN mkdir -p /home/appuser/rsyslog \
|
||||
&& chown -R appuser:0 /home/appuser/rsyslog \
|
||||
&& chmod g+s /home/appuser/rsyslog \
|
||||
&& chmod -R g=u /home/appuser/rsyslog
|
||||
|
||||
# Create directory /tmp/.npm/npm-cache/ and set ownership to appuser
|
||||
RUN mkdir -p /tmp/.npm/npm-cache/ \
|
||||
&& chown -R appuser:0 /tmp/.npm/npm-cache/ \
|
||||
|
|
|
|||
|
|
@ -164,6 +164,12 @@ RUN mkdir -p /home/appuser \
|
|||
&& chmod -R g=u /home/appuser \
|
||||
&& npm cache clean --force
|
||||
|
||||
# Create rsyslog directory for audit logs with proper permissions
|
||||
RUN mkdir -p /home/appuser/rsyslog \
|
||||
&& chown -R appuser:0 /home/appuser/rsyslog \
|
||||
&& chmod g+s /home/appuser/rsyslog \
|
||||
&& chmod -R g=u /home/appuser/rsyslog
|
||||
|
||||
# Create directory /tmp/.npm/npm-cache/ and set ownership to appuser
|
||||
RUN mkdir -p /tmp/.npm/npm-cache/ \
|
||||
&& chown -R appuser:0 /tmp/.npm/npm-cache/ \
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
// storybookDecorators.js
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
|
||||
export function withColorScheme(story, context) {
|
||||
const darkMode = context?.globals?.backgrounds?.value === '#333333'; // Access theme mode from globals
|
||||
const className = darkMode ? 'dark-theme' : '';
|
||||
const darkMode = context?.globals?.backgrounds?.value === "#333333"; // Access theme mode from globals
|
||||
const className = darkMode ? "dark-theme" : "";
|
||||
|
||||
return (
|
||||
<div className={className} style={{ backgroundColor: 'transparent' }}>
|
||||
<div className={className} style={{ backgroundColor: "transparent" }}>
|
||||
{story()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function withRouter(story) {
|
||||
return <MemoryRouter>{story()}</MemoryRouter>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,77 @@
|
|||
import customWebpackConfig from '../webpack.config';
|
||||
import path from 'path';
|
||||
import customWebpackConfig from "../webpack.config";
|
||||
import path from "path";
|
||||
|
||||
const config = {
|
||||
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
||||
|
||||
addons: [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-onboarding",
|
||||
"@storybook/addon-interactions",
|
||||
"@storybook/addon-docs",
|
||||
],
|
||||
|
||||
framework: {
|
||||
name: "@storybook/react-webpack5",
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: "tag",
|
||||
},
|
||||
|
||||
webpackFinal: async (storybookConfig) => {
|
||||
// Filter out the babel-loader rule from custom config to avoid conflicts
|
||||
const customRules = customWebpackConfig.module.rules.filter((rule) => {
|
||||
if (rule.test && rule.test.toString().includes("js|jsx")) {
|
||||
return false; // Skip the babel-loader rule that includes react-refresh
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Add a custom babel-loader rule for JSX files without react-refresh
|
||||
const babelRule = {
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: ["@babel/preset-env", "@babel/preset-react"],
|
||||
plugins: [
|
||||
[
|
||||
"import",
|
||||
{
|
||||
libraryName: "lodash",
|
||||
libraryDirectory: "",
|
||||
camel2DashComponentName: false,
|
||||
},
|
||||
"lodash",
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
...storybookConfig,
|
||||
module: { ...storybookConfig.module, rules: [...storybookConfig.module.rules, ...customWebpackConfig.module.rules] },
|
||||
module: {
|
||||
...storybookConfig.module,
|
||||
rules: [...storybookConfig.module.rules, ...customRules, babelRule],
|
||||
},
|
||||
resolve: {
|
||||
...storybookConfig.resolve,
|
||||
alias: {
|
||||
...storybookConfig.resolve.alias,
|
||||
'@': path.resolve(__dirname, '../src/')
|
||||
}
|
||||
}
|
||||
"@": path.resolve(__dirname, "../src/"),
|
||||
"@ee": path.resolve(__dirname, "../ee/"),
|
||||
"@cloud": path.resolve(__dirname, "../cloud/"),
|
||||
"@assets": path.resolve(__dirname, "../assets/"),
|
||||
"@white-label": path.resolve(
|
||||
__dirname,
|
||||
"../src/_helpers/white-label"
|
||||
),
|
||||
},
|
||||
fallback: {
|
||||
...storybookConfig.resolve.fallback,
|
||||
process: require.resolve("process/browser.js"),
|
||||
path: require.resolve("path-browserify"),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/** @type { import('@storybook/react').Preview } */
|
||||
/** @type { import('@storybook/react-webpack5').Preview } */
|
||||
|
||||
import '../src/_styles/theme.scss';
|
||||
import './preview.scss';
|
||||
import { withColorScheme } from './decorators'; // Import the decorator
|
||||
import "../src/_styles/theme.scss";
|
||||
import "./preview.scss";
|
||||
import { withColorScheme, withRouter } from "./decorators"; // Import the decorators
|
||||
|
||||
const preview = {
|
||||
parameters: {
|
||||
|
|
@ -14,7 +14,7 @@ const preview = {
|
|||
},
|
||||
},
|
||||
},
|
||||
decorators: [withColorScheme], // Adding the decorator to the decorators array
|
||||
decorators: [withRouter, withColorScheme], // Adding the decorators to the decorators array
|
||||
};
|
||||
|
||||
export default preview;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,6 @@
|
|||
@import '~bootstrap/scss/bootstrap';
|
||||
@import '../src/_styles/componentdesign.scss'
|
||||
@import '../src/_styles/componentdesign.scss';
|
||||
|
||||
body {
|
||||
overflow-y: scroll !important;
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
3.20.21-lts
|
||||
3.20.30-lts
|
||||
|
|
|
|||
|
|
@ -1,17 +1,21 @@
|
|||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"style": "new-york",
|
||||
"rsc": false,
|
||||
"tsx": false,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "src/styles/theme.scss",
|
||||
"baseColor": "zinc",
|
||||
"css": "src/styles/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
"prefix": "tw-"
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils"
|
||||
}
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 4c9743f485d77aa186c29729abd743e708536e79
|
||||
Subproject commit 1e40a2e500636d5f900f707cccd0d9ee1f372069
|
||||
|
|
@ -22,11 +22,11 @@
|
|||
"@radix-ui/colors": "^0.1.8",
|
||||
"@radix-ui/react-avatar": "^1.0.4",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-popover": "^1.0.3",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slider": "^1.1.2",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-toggle-group": "^1.0.4",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
|
|
@ -49,8 +49,9 @@
|
|||
"axios": "^1.3.3",
|
||||
"bootstrap": "^5.2.3",
|
||||
"buffer": "^6.0.3",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"classnames": "^2.3.2",
|
||||
"clsx": "^2.1.1",
|
||||
"cron-validator": "^1.3.1",
|
||||
"cronstrue": "^2.51.0",
|
||||
"deep-object-diff": "^1.1.9",
|
||||
|
|
@ -146,11 +147,13 @@
|
|||
"semver": "^7.3.8",
|
||||
"string-hash": "^1.1.3",
|
||||
"superstruct": "^1.0.3",
|
||||
"tailwind-merge": "^2.2.1",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"tw-animate-css": "^1.3.7",
|
||||
"url-join": "^5.0.0",
|
||||
"use-react-router-breadcrumbs": "^4.0.1",
|
||||
"util": "^0.12.5",
|
||||
"uuid": "9.0.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"y-websocket": "^1.4.5",
|
||||
|
|
@ -165,13 +168,10 @@
|
|||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.0",
|
||||
"@storybook/addon-essentials": "^7.2.1",
|
||||
"@storybook/addon-interactions": "^7.2.1",
|
||||
"@storybook/addon-links": "^7.2.1",
|
||||
"@storybook/addon-onboarding": "^1.0.8",
|
||||
"@storybook/blocks": "^7.2.1",
|
||||
"@storybook/react": "^7.2.1",
|
||||
"@storybook/react-webpack5": "^7.2.1",
|
||||
"@storybook/addon-docs": "^9.1.5",
|
||||
"@storybook/addon-links": "^9.1.5",
|
||||
"@storybook/addon-onboarding": "^9.1.5",
|
||||
"@storybook/react-webpack5": "^9.1.5",
|
||||
"@storybook/testing-library": "^0.2.0",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
|
|
@ -193,7 +193,7 @@
|
|||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-storybook": "^0.6.13",
|
||||
"eslint-plugin-storybook": "^9.1.5",
|
||||
"html-loader": "^4.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"jest": "^29.4.2",
|
||||
|
|
@ -204,13 +204,14 @@
|
|||
"react-refresh": "^0.17.0",
|
||||
"sass": "^1.93.2",
|
||||
"sass-loader": "^16.0.5",
|
||||
"storybook": "^7.2.1",
|
||||
"storybook": "^9.1.5",
|
||||
"style-loader": "^3.3.1",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"terser-webpack-plugin": "^5.3.6",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1",
|
||||
"webpack-dev-server": "^5.2.2"
|
||||
"webpack-dev-server": "^5.2.2",
|
||||
"@storybook/addon-docs": "^9.1.5"
|
||||
},
|
||||
"overrides": {
|
||||
"react-dates": {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Suspense } from 'react';
|
||||
import React, { Suspense, useEffect } from 'react';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import useAppData from '@/AppBuilder/_hooks/useAppData';
|
||||
import { TJLoader } from '@/_ui/TJLoader/TJLoader';
|
||||
|
|
@ -16,6 +16,7 @@ import Popups from './Popups';
|
|||
import { ModuleProvider } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
import RightSidebarToggle from '@/AppBuilder/RightSideBar/RightSidebarToggle';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import ArtifactPreview from './ArtifactPreview';
|
||||
|
||||
|
|
@ -30,10 +31,13 @@ export const Editor = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
|
|||
useAppData(appId, moduleId, darkMode);
|
||||
const isEditorLoading = useStore((state) => state.loaderStore.modules[moduleId].isEditorLoading, shallow);
|
||||
const currentMode = useStore((state) => state.modeStore.modules[moduleId].currentMode, shallow);
|
||||
const hasModuleAccess = useStore((state) => state.license.featureAccess?.modulesEnabled);
|
||||
const isModuleEditor = appType === 'module';
|
||||
console.log("isModuleEditor", isModuleEditor);
|
||||
|
||||
const updateIsTJDarkMode = useStore((state) => state.updateIsTJDarkMode, shallow);
|
||||
const appBuilderMode = useStore((state) => state.appStore.modules[moduleId]?.app?.appBuilderMode ?? 'visual');
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isUserInZeroToOneFlow = appBuilderMode === 'ai';
|
||||
|
||||
|
|
@ -42,6 +46,12 @@ export const Editor = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
|
|||
switchDarkMode(newMode);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (hasModuleAccess === false && isModuleEditor) {
|
||||
navigate('/error/restricted');
|
||||
}
|
||||
}, [hasModuleAccess, isModuleEditor]);
|
||||
|
||||
//TODO: This can be added to the mode slice and set based on the mode
|
||||
if (isEditorLoading) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -26,10 +26,14 @@ import PagesSidebarNavigation from '../RightSideBar/PageSettingsTab/PageMenu/Pag
|
|||
import { DragGhostWidget, ResizeGhostWidget } from './GhostWidgets';
|
||||
import AppCanvasBanner from '../../AppBuilder/Header/AppCanvasBanner';
|
||||
import { debounce } from 'lodash';
|
||||
import { RIGHT_SIDE_BAR_TAB } from '../RightSideBar/rightSidebarConstants';
|
||||
|
||||
export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => {
|
||||
const { moduleId, isModuleMode, appType } = useModuleContext();
|
||||
const canvasContainerRef = useRef();
|
||||
const scrollTimeoutRef = useRef(null);
|
||||
const canvasContentRef = useRef(null);
|
||||
const [isScrolling, setIsScrolling] = useState(false);
|
||||
const handleCanvasContainerMouseUp = useStore((state) => state.handleCanvasContainerMouseUp, shallow);
|
||||
const resolveReferences = useStore((state) => state.resolveReferences);
|
||||
const canvasHeight = useStore((state) => state.appStore.modules[moduleId].canvasHeight);
|
||||
|
|
@ -52,6 +56,7 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => {
|
|||
const editorMarginLeft = useSidebarMargin(canvasContainerRef);
|
||||
const getPageId = useStore((state) => state.getCurrentPageId, shallow);
|
||||
const isRightSidebarOpen = useStore((state) => state.isRightSidebarOpen, shallow);
|
||||
const draggingComponentId = useStore((state) => state.draggingComponentId, shallow);
|
||||
const isSidebarOpen = useStore((state) => state.isSidebarOpen, shallow);
|
||||
const selectedSidebarItem = useStore((state) => state.selectedSidebarItem);
|
||||
const currentPageId = useStore((state) => state.modules[moduleId].currentPageId);
|
||||
|
|
@ -71,7 +76,6 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => {
|
|||
}),
|
||||
shallow
|
||||
);
|
||||
|
||||
const showHeader = !globalSettings?.hideHeader;
|
||||
const { definition: { properties = {} } = {} } = pageSettings ?? {};
|
||||
const { position, disableMenu, showOnDesktop } = properties ?? {};
|
||||
|
|
@ -121,8 +125,8 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => {
|
|||
currentMode === 'view'
|
||||
? computeViewerBackgroundColor(isAppDarkMode, canvasBgColor)
|
||||
: !isAppDarkMode
|
||||
? '#EBEBEF'
|
||||
: '#2F3C4C';
|
||||
? '#EBEBEF'
|
||||
: '#2F3C4C';
|
||||
|
||||
if (isModuleMode) {
|
||||
return {
|
||||
|
|
@ -151,6 +155,34 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => {
|
|||
localStorage.setItem('isPagesSidebarPinned', JSON.stringify(newValue));
|
||||
}, [isViewerSidebarPinned]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolling(true);
|
||||
|
||||
if (scrollTimeoutRef.current) {
|
||||
clearTimeout(scrollTimeoutRef.current);
|
||||
}
|
||||
|
||||
scrollTimeoutRef.current = setTimeout(() => {
|
||||
setIsScrolling(false);
|
||||
}, 600);
|
||||
};
|
||||
|
||||
const canvasContent = canvasContentRef.current;
|
||||
if (canvasContent) {
|
||||
canvasContent.addEventListener('scroll', handleScroll);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (canvasContent) {
|
||||
canvasContent.removeEventListener('scroll', handleScroll);
|
||||
}
|
||||
if (scrollTimeoutRef.current) {
|
||||
clearTimeout(scrollTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
function getMinWidth() {
|
||||
if (isModuleMode) return '100%';
|
||||
|
||||
|
|
@ -227,22 +259,29 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => {
|
|||
/>
|
||||
)}
|
||||
<div
|
||||
ref={canvasContentRef}
|
||||
style={{
|
||||
minWidth: getMinWidth(),
|
||||
scrollbarWidth: 'none',
|
||||
overflow: 'auto',
|
||||
width: currentMode === 'view' ? `calc(100% - ${isViewerSidebarPinned ? '0px' : '0px'})` : '100%',
|
||||
...(appType === 'module' && isModuleMode && { height: 'inherit' }),
|
||||
}}
|
||||
className={`app-${appId} _tooljet-page-${getPageId()} canvas-content`}
|
||||
className={cx(`app-${appId} _tooljet-page-${getPageId()} canvas-content scrollbar`, {
|
||||
'scrollbar-hidden': !isScrolling,
|
||||
})}
|
||||
>
|
||||
{currentMode === 'edit' && (
|
||||
<AutoComputeMobileLayoutAlert currentLayout={currentLayout} darkMode={isAppDarkMode} />
|
||||
)}
|
||||
<DeleteWidgetConfirmation darkMode={isAppDarkMode} />
|
||||
<HotkeyProvider mode={currentMode} canvasMaxWidth={canvasMaxWidth} currentLayout={currentLayout}>
|
||||
<HotkeyProvider
|
||||
mode={currentMode}
|
||||
canvasMaxWidth={canvasMaxWidth}
|
||||
currentLayout={currentLayout}
|
||||
isModuleMode={isModuleMode}
|
||||
>
|
||||
{environmentLoadingState !== 'loading' && (
|
||||
<div>
|
||||
<div className={cx({ 'h-100': isModuleMode })}>
|
||||
<Container
|
||||
id={moduleId}
|
||||
gridWidth={gridWidth}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export const ConfigHandle = ({
|
|||
subContainerIndex,
|
||||
}) => {
|
||||
const { moduleId } = useModuleContext();
|
||||
const isLicenseValid = useStore((state) => state.isLicenseValid(), shallow);
|
||||
const isModulesEnabled = useStore((state) => state.license.featureAccess?.modulesEnabled, shallow);
|
||||
const shouldFreeze = useStore((state) => state.getShouldFreeze());
|
||||
const componentName = useStore((state) => state.getComponentDefinition(id, moduleId)?.component?.name || '', shallow);
|
||||
const isMultipleComponentsSelected = useStore(
|
||||
|
|
@ -219,11 +219,11 @@ export const ConfigHandle = ({
|
|||
)}
|
||||
</span>
|
||||
{/* Tooltip for invalid license on ModuleViewer */}
|
||||
{!isLicenseValid && componentType === 'ModuleViewer' && (
|
||||
{(componentType === 'ModuleViewer' || componentType === 'ModuleContainer') && !isModulesEnabled && (
|
||||
<Tooltip
|
||||
id={`invalid-license-modules-${componentName?.toLowerCase()}`}
|
||||
className="tooltip"
|
||||
isOpen={_showHandle && componentType === 'ModuleViewer'}
|
||||
isOpen={_showHandle && (componentType === 'ModuleViewer' || componentType === 'ModuleContainer')}
|
||||
style={{ textAlign: 'center' }}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -76,10 +76,6 @@ const Container = React.memo(
|
|||
|
||||
const setCurrentDragCanvasId = useGridStore((state) => state.actions.setCurrentDragCanvasId);
|
||||
|
||||
const { handleDrop } = useCanvasDropHandler({
|
||||
appType,
|
||||
});
|
||||
|
||||
const [{ isOverCurrent }, drop] = useDrop({
|
||||
accept: 'box',
|
||||
hover: (item, monitor) => {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import { useGroupedTargetsScrollHandler } from './hooks/useGroupedTargetsScrollH
|
|||
import { DROPPABLE_PARENTS, NO_OF_GRIDS, SUBCONTAINER_WIDGETS } from '../appCanvasConstants';
|
||||
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
import { useElementGuidelines } from './hooks/useElementGuidelines';
|
||||
import { RIGHT_SIDE_BAR_TAB } from '../../RightSideBar/rightSidebarConstants';
|
||||
import MentionComponentInChat from '../ConfigHandle/MentionComponentInChat';
|
||||
|
||||
const CANVAS_BOUNDS = { left: 0, top: 0, right: 0, position: 'css' };
|
||||
|
|
@ -79,6 +80,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const virtualTarget = useGridStore((state) => state.virtualTarget, shallow);
|
||||
const currentDragCanvasId = useGridStore((state) => state.currentDragCanvasId, shallow);
|
||||
const groupedTargets = [...findHighestLevelofSelection().map((component) => '.ele-' + component.id)];
|
||||
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab);
|
||||
|
||||
const isWidgetResizable = useMemo(() => {
|
||||
if (virtualTarget) {
|
||||
|
|
@ -339,7 +341,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
if (moveableRef.current) {
|
||||
safeUpdateMoveable();
|
||||
}
|
||||
}, [temporaryHeight, boxList]);
|
||||
}, [temporaryHeight, boxList, selectedComponents]);
|
||||
|
||||
useEffect(() => {
|
||||
reloadGrid();
|
||||
|
|
@ -465,7 +467,8 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const handleDragGroupEnd = (e) => {
|
||||
try {
|
||||
hideGridLines();
|
||||
// setIsGroupDragging(false);
|
||||
handleDeactivateTargets();
|
||||
clearActiveTargetClassNamesAfterSnapping(selectedComponents);
|
||||
const { events, clientX, clientY } = e;
|
||||
const initialParent = events[0].target.closest('.real-canvas');
|
||||
// Get potential new parent using same logic as onDragEnd
|
||||
|
|
@ -775,7 +778,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
onResizeGroup={({ events }) => {
|
||||
const parentElm = events[0].target.closest('.real-canvas');
|
||||
const parentWidth = parentElm?.clientWidth;
|
||||
const parentHeight = parentElm?.clientHeight;
|
||||
const parentHeight = parentElm?.scrollHeight;
|
||||
handleActivateTargets(parentElm?.id?.replace('canvas-', ''));
|
||||
const { posRight, posLeft, posTop, posBottom } = getPositionForGroupDrag(events, parentWidth, parentHeight);
|
||||
events.forEach((ev) => {
|
||||
|
|
@ -783,7 +786,6 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
ev.target.style.height = `${ev.height}px`;
|
||||
ev.target.style.transform = ev.drag.transform;
|
||||
});
|
||||
|
||||
if (!(posLeft < 0 || posTop < 0 || posRight < 0 || posBottom < 0)) {
|
||||
groupResizeDataRef.current = events;
|
||||
}
|
||||
|
|
@ -859,10 +861,12 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
if (e.target.id === 'moveable-virtual-ghost-element') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is to prevent parent component from being dragged and the stop the propagation of the event
|
||||
if (getHoveredComponentForGrid() !== e.target.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
newDragParentId.current = boxList.find((box) => box.id === e.target.id)?.parent;
|
||||
e?.moveable?.controlBox?.removeAttribute('data-off-screen');
|
||||
|
||||
|
|
@ -953,6 +957,10 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
}
|
||||
try {
|
||||
if (isDraggingRef.current) {
|
||||
// setTimeout(() => {
|
||||
// useStore.getState().setRightSidebarOpen(true);
|
||||
// }, 100);
|
||||
|
||||
useStore.getState().setDraggingComponentId(null);
|
||||
isDraggingRef.current = false;
|
||||
}
|
||||
|
|
@ -1012,6 +1020,25 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
hideGridLines();
|
||||
clearActiveTargetClassNamesAfterSnapping(selectedComponents);
|
||||
toggleCanvasUpdater();
|
||||
// Move this to common function
|
||||
const canvas = document.querySelector('.canvas-container');
|
||||
const sidebar = document.querySelector('.editor-sidebar');
|
||||
const droppedElem = document.getElementById(e.target.id);
|
||||
|
||||
if (!canvas || !sidebar || !droppedElem) return;
|
||||
|
||||
const droppedRect = droppedElem.getBoundingClientRect();
|
||||
const sidebarRect = sidebar.getBoundingClientRect();
|
||||
|
||||
const isOverlapping = droppedRect.right > sidebarRect.left && droppedRect.left < sidebarRect.right;
|
||||
|
||||
if (isOverlapping) {
|
||||
const overlap = droppedRect.right - sidebarRect.left;
|
||||
canvas.scrollTo({
|
||||
left: canvas.scrollLeft + overlap + 15,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
}}
|
||||
onDrag={(e) => {
|
||||
if (e.target.id === 'moveable-virtual-ghost-element') {
|
||||
|
|
@ -1088,9 +1115,6 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
onDragGroup={(ev) => {
|
||||
const { events } = ev;
|
||||
const parentElm = events[0]?.target?.closest('.real-canvas');
|
||||
if (parentElm && !parentElm.classList.contains('show-grid')) {
|
||||
parentElm?.classList?.add('show-grid');
|
||||
}
|
||||
|
||||
events.forEach((ev) => {
|
||||
const currentWidget = boxList.find(({ id }) => id === ev.target.id);
|
||||
|
|
@ -1112,8 +1136,6 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
}}
|
||||
onDragGroupEnd={(e) => {
|
||||
handleDragGroupEnd(e);
|
||||
handleDeactivateTargets();
|
||||
clearActiveTargetClassNamesAfterSnapping(selectedComponents);
|
||||
toggleCanvasUpdater();
|
||||
}}
|
||||
onClickGroup={(e) => {
|
||||
|
|
@ -1168,6 +1190,10 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
}
|
||||
}}
|
||||
snapGridAll={true}
|
||||
onClick={(e) => {
|
||||
useStore.getState().setActiveRightSideBarTab(RIGHT_SIDE_BAR_TAB.CONFIGURATION);
|
||||
useStore.getState().setRightSidebarOpen(true);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import useKeyHooks from '@/_hooks/useKeyHooks';
|
|||
import { shallow } from 'zustand/shallow';
|
||||
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
|
||||
export const HotkeyProvider = ({ children, mode, currentLayout, canvasMaxWidth }) => {
|
||||
export const HotkeyProvider = ({ children, mode, currentLayout, canvasMaxWidth, isModuleMode }) => {
|
||||
const { isModuleEditor } = useModuleContext();
|
||||
const canvasRef = useRef(null);
|
||||
const focusedParentId = useStore((state) => state.focusedParentId, shallow);
|
||||
|
|
@ -138,6 +138,7 @@ export const HotkeyProvider = ({ children, mode, currentLayout, canvasMaxWidth }
|
|||
maxWidth: canvasMaxWidth,
|
||||
margin: '0 auto',
|
||||
transform: 'translateZ(0)',
|
||||
...(isModuleMode && { height: '100%' }),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
|||
export const EditorSelecto = () => {
|
||||
const { moduleId } = useModuleContext();
|
||||
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab);
|
||||
const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen);
|
||||
const setSelectedComponents = useStore((state) => state.setSelectedComponents);
|
||||
const getSelectedComponents = useStore((state) => state.getSelectedComponents, shallow);
|
||||
const getComponentDefinition = useStore((state) => state.getComponentDefinition);
|
||||
|
|
@ -119,6 +120,7 @@ export const EditorSelecto = () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
[setSelectedComponents, setActiveRightSideBarTab, getSelectedComponents]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
.canvas-container{
|
||||
&:focus-visible{
|
||||
.canvas-container {
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
|
@ -23,12 +23,12 @@
|
|||
// }
|
||||
// }
|
||||
|
||||
.empty-box-cont{
|
||||
.empty-box-cont {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: unset !important;
|
||||
|
||||
.dotted-cont{
|
||||
.dotted-cont {
|
||||
border: 1px dashed var(--indigo8);
|
||||
border-radius: 6px;
|
||||
margin: 0 10px 0 10px;
|
||||
|
|
@ -38,20 +38,20 @@
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.title-text{
|
||||
.title-text {
|
||||
margin-top: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 600px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.title-desc{
|
||||
.title-desc {
|
||||
font-size: 14px;
|
||||
margin-top: 5px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.box-link{
|
||||
.box-link {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
|
||||
|
|
@ -60,8 +60,8 @@
|
|||
width: auto;
|
||||
}
|
||||
|
||||
.link-but{
|
||||
|
||||
.link-but {
|
||||
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #3E63DD;
|
||||
|
|
@ -70,26 +70,40 @@
|
|||
}
|
||||
|
||||
*:focus-visible {
|
||||
outline: none; // Rewriting it to replace outline coming from browser styles
|
||||
}
|
||||
outline: none; // Rewriting it to replace outline coming from browser styles
|
||||
}
|
||||
|
||||
//[Container-widget]Show scrollbar only on hover
|
||||
.real-canvas:not(.has-no-scroll) {
|
||||
.real-canvas:not(.has-no-scroll) {
|
||||
overflow: hidden auto;
|
||||
scrollbar-width: none;
|
||||
|
||||
&:hover {
|
||||
scrollbar-width: auto;
|
||||
scrollbar-width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is required to maintain the height of the subcontainer when dragging a widget inside it
|
||||
// This is required to maintain the height of the subcontainer when dragging a widget inside it
|
||||
.real-canvas.is-child-being-dragged:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 500%;
|
||||
overflow: hidden;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 500%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
.scrollbar-hidden {
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -612,6 +612,12 @@ export function pasteComponents(targetParentId, copiedComponentObj) {
|
|||
// Adjust width if parent changed
|
||||
let width = component.layouts[currentLayout].width;
|
||||
|
||||
if (targetParentId !== component.component?.parent) {
|
||||
const containerWidth = useGridStore.getState().subContainerWidths[targetParentId || 'canvas'];
|
||||
const oldContainerWidth = useGridStore.getState().subContainerWidths[component?.component?.parent || 'canvas'];
|
||||
width = Math.round((width * oldContainerWidth) / containerWidth);
|
||||
}
|
||||
|
||||
component.layouts[currentLayout] = {
|
||||
...component.layouts[currentLayout],
|
||||
width,
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ const Portal = ({ children, ...restProps }) => {
|
|||
const PopupIcon = ({ callback, icon, tip, position, isMultiEditor = false, isQueryManager = false }) => {
|
||||
const size = 16;
|
||||
const topRef = isNumber(position?.height) ? Math.floor(position?.height) - 30 : 32;
|
||||
let top = isMultiEditor ? 270 : topRef > 32 ? topRef : 0;
|
||||
let top = topRef > 32 ? topRef : 0;
|
||||
// for query manager we allow the height of query manager to be dynamic, so we need to render the popup icon at the bottom of code editor
|
||||
const renderAtBottom = isQueryManager && (isMultiEditor || topRef > 32);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,14 +62,14 @@ const MultiLineCodeEditor = (props) => {
|
|||
const wrapperRef = useRef(null);
|
||||
const getSuggestions = useStore((state) => state.getSuggestions, shallow);
|
||||
const getServerSideGlobalResolveSuggestions = useStore(
|
||||
(state) => state.getServerSideGlobalResolveSuggestions,
|
||||
shallow
|
||||
(state) => state.getServerSideGlobalResolveSuggestions,
|
||||
shallow
|
||||
);
|
||||
|
||||
const isInsideQueryPane = !!document.querySelector('.code-hinter-wrapper')?.closest('.query-details');
|
||||
const isInsideQueryManager = useMemo(
|
||||
() => isInsideParent(wrapperRef?.current, 'query-manager'),
|
||||
[wrapperRef.current]
|
||||
() => isInsideParent(wrapperRef?.current, 'query-manager'),
|
||||
[wrapperRef.current]
|
||||
);
|
||||
|
||||
const context = useContext(CodeHinterContext);
|
||||
|
|
@ -93,22 +93,22 @@ const MultiLineCodeEditor = (props) => {
|
|||
// Intersection observer to detect when current line goes out of view
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.intersectionRatio < 1) {
|
||||
setShowSuggestions(false);
|
||||
isObserverTriggeredRef.current = true;
|
||||
// Close autocomplete dropdown by dispatching a selection change
|
||||
if (editorView) {
|
||||
editorView.dispatch({
|
||||
selection: editorView.state.selection,
|
||||
});
|
||||
([entry]) => {
|
||||
if (entry.intersectionRatio < 1) {
|
||||
setShowSuggestions(false);
|
||||
isObserverTriggeredRef.current = true;
|
||||
// Close autocomplete dropdown by dispatching a selection change
|
||||
if (editorView) {
|
||||
editorView.dispatch({
|
||||
selection: editorView.state.selection,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setShowSuggestions(true);
|
||||
isObserverTriggeredRef.current = false;
|
||||
}
|
||||
} else {
|
||||
setShowSuggestions(true);
|
||||
isObserverTriggeredRef.current = false;
|
||||
}
|
||||
},
|
||||
{ root: null, threshold: [1] }
|
||||
},
|
||||
{ root: null, threshold: [1] }
|
||||
);
|
||||
|
||||
currentLineObserverRef.current = observer;
|
||||
|
|
@ -153,7 +153,7 @@ const MultiLineCodeEditor = (props) => {
|
|||
|
||||
function autoCompleteExtensionConfig(context) {
|
||||
const hasWorkflowSuggestions =
|
||||
workflowSuggestions?.appHints?.length > 0 || workflowSuggestions?.jsHints?.length > 0;
|
||||
workflowSuggestions?.appHints?.length > 0 || workflowSuggestions?.jsHints?.length > 0;
|
||||
const hints = hasWorkflowSuggestions ? workflowSuggestions : getSuggestions();
|
||||
const serverHints = getServerSideGlobalResolveSuggestions(isInsideQueryManager);
|
||||
|
||||
|
|
@ -201,8 +201,8 @@ const MultiLineCodeEditor = (props) => {
|
|||
|
||||
const initialValueWithReplacedIds = useMemo(() => {
|
||||
if (
|
||||
typeof initialValue === 'string' &&
|
||||
(initialValue?.includes('components') || initialValue?.includes('queries'))
|
||||
typeof initialValue === 'string' &&
|
||||
(initialValue?.includes('components') || initialValue?.includes('queries'))
|
||||
) {
|
||||
return replaceIdsWithName(initialValue);
|
||||
}
|
||||
|
|
@ -230,122 +230,123 @@ const MultiLineCodeEditor = (props) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`code-hinter-wrapper position-relative ${isInsideQueryPane ? 'code-editor-query-panel' : ''}`}
|
||||
style={{ width: '100%' }}
|
||||
ref={wrapperRef}
|
||||
>
|
||||
<div className={`${className} ${darkMode && 'cm-codehinter-dark-themed'}`}>
|
||||
<CodeHinterBtns
|
||||
view={editorView}
|
||||
isPanelOpen={isSearchPanelOpen}
|
||||
renderCopilot={() =>
|
||||
renderCopilot?.({
|
||||
darkMode,
|
||||
language: lang,
|
||||
editorRef,
|
||||
onAiSuggestionAccept,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className={`code-hinter-wrapper position-relative ${isInsideQueryPane ? 'code-editor-query-panel' : ''}`}
|
||||
style={{ width: '100%' }}
|
||||
ref={wrapperRef}
|
||||
>
|
||||
<div className={`${className} ${darkMode && 'cm-codehinter-dark-themed'}`}>
|
||||
<CodeHinterBtns
|
||||
view={editorView}
|
||||
isPanelOpen={isSearchPanelOpen}
|
||||
renderCopilot={() =>
|
||||
renderCopilot?.({
|
||||
darkMode,
|
||||
language: lang,
|
||||
editorRef,
|
||||
onAiSuggestionAccept,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<CodeHinter.PopupIcon
|
||||
callback={handleTogglePopupExapand}
|
||||
icon="portal-open"
|
||||
tip="Pop out code editor into a new window"
|
||||
isMultiEditor={true}
|
||||
isQueryManager={isInsideQueryPane}
|
||||
/>
|
||||
<CodeHinter.PopupIcon
|
||||
callback={handleTogglePopupExapand}
|
||||
icon="portal-open"
|
||||
tip="Pop out code editor into a new window"
|
||||
isMultiEditor={true}
|
||||
isQueryManager={isInsideQueryPane}
|
||||
position={{height: height}}
|
||||
/>
|
||||
|
||||
<CodeHinter.Portal
|
||||
isCopilotEnabled={false}
|
||||
isOpen={isOpen}
|
||||
callback={setIsOpen}
|
||||
componentName={componentName}
|
||||
key={componentName}
|
||||
forceUpdate={forceUpdate}
|
||||
optionalProps={{ styles: { height: 300 }, cls: '' }}
|
||||
darkMode={darkMode}
|
||||
selectors={{ className: 'preview-block-portal' }}
|
||||
dragResizePortal={true}
|
||||
callgpt={null}
|
||||
>
|
||||
<ErrorBoundary>
|
||||
<div className="codehinter-container w-100 " data-cy={`${cyLabel}-input-field`} style={{ height: '100%' }}>
|
||||
<CodeMirror
|
||||
ref={editorRef}
|
||||
value={initialValueWithReplacedIds}
|
||||
placeholder={placeholder}
|
||||
height={'100%'}
|
||||
minHeight={heightInPx}
|
||||
{...(isInsideQueryPane ? { maxHeight: '100%' } : {})}
|
||||
width="100%"
|
||||
theme={theme}
|
||||
extensions={[
|
||||
langExtention,
|
||||
search({
|
||||
createPanel: handleSearchPanel,
|
||||
}),
|
||||
javascriptLanguage.data.of({
|
||||
autocomplete: overRideFunction,
|
||||
}),
|
||||
python().language.data.of({
|
||||
autocomplete: overRideFunction,
|
||||
}),
|
||||
sql().language.data.of({
|
||||
autocomplete: overRideFunction,
|
||||
}),
|
||||
sass().language.data.of({
|
||||
autocomplete: sassCompletionSource,
|
||||
}),
|
||||
autocompletion({
|
||||
override: [overRideFunction],
|
||||
activateOnTyping: true,
|
||||
compareCompletions: (a, b) => {
|
||||
return a.section.rank - b.section.rank && a.label.localeCompare(b.label);
|
||||
},
|
||||
}),
|
||||
customTabKeymap,
|
||||
keymap.of([...customKeyMaps]),
|
||||
]}
|
||||
onChange={handleChange}
|
||||
onBlur={handleOnBlur}
|
||||
basicSetup={setupConfig}
|
||||
style={{
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
className={`codehinter-multi-line-input ${isInsideQueryPane ? 'code-editor-query-panel' : ''}`}
|
||||
indentWithTab={false}
|
||||
readOnly={readOnly}
|
||||
editable={editable} //for transformations in query manager
|
||||
onCreateEditor={(view) => {
|
||||
setEditorView(view);
|
||||
if (setCodeEditorView) {
|
||||
setCodeEditorView(view);
|
||||
}
|
||||
}}
|
||||
onUpdate={(view) => {
|
||||
setIsSearchPanelOpen(searchPanelOpen(view.state));
|
||||
updateCurrentLineObserver(view);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{showPreview && (
|
||||
<div className="multiline-previewbox-wrapper">
|
||||
<PreviewBox
|
||||
currentValue={currentValueRef.current}
|
||||
validationSchema={null}
|
||||
setErrorStateActive={() => null}
|
||||
componentId={null}
|
||||
setErrorMessage={() => null}
|
||||
<CodeHinter.Portal
|
||||
isCopilotEnabled={false}
|
||||
isOpen={isOpen}
|
||||
callback={setIsOpen}
|
||||
componentName={componentName}
|
||||
key={componentName}
|
||||
forceUpdate={forceUpdate}
|
||||
optionalProps={{ styles: { height: 300 }, cls: '' }}
|
||||
darkMode={darkMode}
|
||||
selectors={{ className: 'preview-block-portal' }}
|
||||
dragResizePortal={true}
|
||||
callgpt={null}
|
||||
>
|
||||
<ErrorBoundary>
|
||||
<div className="codehinter-container w-100 " data-cy={`${cyLabel}-input-field`} style={{ height: '100%' }}>
|
||||
<CodeMirror
|
||||
ref={editorRef}
|
||||
value={initialValueWithReplacedIds}
|
||||
placeholder={placeholder}
|
||||
height={heightInPx}
|
||||
minHeight={heightInPx}
|
||||
{...(isInsideQueryPane ? { maxHeight: '100%' } : {})}
|
||||
width="100%"
|
||||
theme={theme}
|
||||
extensions={[
|
||||
langExtention,
|
||||
search({
|
||||
createPanel: handleSearchPanel,
|
||||
}),
|
||||
javascriptLanguage.data.of({
|
||||
autocomplete: overRideFunction,
|
||||
}),
|
||||
python().language.data.of({
|
||||
autocomplete: overRideFunction,
|
||||
}),
|
||||
sql().language.data.of({
|
||||
autocomplete: overRideFunction,
|
||||
}),
|
||||
sass().language.data.of({
|
||||
autocomplete: sassCompletionSource,
|
||||
}),
|
||||
autocompletion({
|
||||
override: [overRideFunction],
|
||||
activateOnTyping: true,
|
||||
compareCompletions: (a, b) => {
|
||||
return a.section.rank - b.section.rank && a.label.localeCompare(b.label);
|
||||
},
|
||||
}),
|
||||
customTabKeymap,
|
||||
keymap.of([...customKeyMaps]),
|
||||
]}
|
||||
onChange={handleChange}
|
||||
onBlur={handleOnBlur}
|
||||
basicSetup={setupConfig}
|
||||
style={{
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
className={`codehinter-multi-line-input ${isInsideQueryPane ? 'code-editor-query-panel' : ''}`}
|
||||
indentWithTab={false}
|
||||
readOnly={readOnly}
|
||||
editable={editable} //for transformations in query manager
|
||||
onCreateEditor={(view) => {
|
||||
setEditorView(view);
|
||||
if (setCodeEditorView) {
|
||||
setCodeEditorView(view);
|
||||
}
|
||||
}}
|
||||
onUpdate={(view) => {
|
||||
setIsSearchPanelOpen(searchPanelOpen(view.state));
|
||||
updateCurrentLineObserver(view);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
</CodeHinter.Portal>
|
||||
{showPreview && (
|
||||
<div className="multiline-previewbox-wrapper">
|
||||
<PreviewBox
|
||||
currentValue={currentValueRef.current}
|
||||
validationSchema={null}
|
||||
setErrorStateActive={() => null}
|
||||
componentId={null}
|
||||
setErrorMessage={() => null}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
</CodeHinter.Portal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MultiLineCodeEditor;
|
||||
export default MultiLineCodeEditor;
|
||||
|
|
@ -299,8 +299,8 @@
|
|||
height: 100%;
|
||||
|
||||
.cm-editor {
|
||||
min-height: 300px;
|
||||
height: 300px;
|
||||
// min-height: 300px;
|
||||
// height: 300px;
|
||||
max-height: fit-content !important;
|
||||
|
||||
.cm-gutters {
|
||||
|
|
@ -362,11 +362,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.cm-scroller {
|
||||
overflow-y: auto !important;
|
||||
overflow-x: hidden !important;
|
||||
|
|
@ -390,6 +385,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.codehinter-popup{
|
||||
.cm-editor{
|
||||
border-radius: 0 0 4px 4px !important;
|
||||
//box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.rest-api-tab-content {
|
||||
.fields-container {
|
||||
.rest-api-codehinter-key-field {
|
||||
|
|
@ -413,9 +415,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
.runjs-editor .cm-editor {
|
||||
border: none !important;
|
||||
}
|
||||
//.runjs-editor .cm-editor {
|
||||
// border: none !important;
|
||||
//}
|
||||
|
||||
.preview-alert-banner {
|
||||
height: fit-content;
|
||||
|
|
@ -488,8 +490,6 @@
|
|||
.codehinter-input {
|
||||
height: 100%;
|
||||
border: none !important;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ function EditAppName() {
|
|||
<button
|
||||
className="edit-app-name-button tw-h-8 tw-rounded-lg tw-pr-1 tw-w-auto tw-font-medium tw-cursor-pointer tw-outline-none tw-bg-transparent tw-border tw-border-transparent hover:tw-border-border-strong tw-shadow-none tw-group tw-transition-all tw-duration-300 tw-flex tw-items-center tw-relative"
|
||||
type="button"
|
||||
data-cy="edit-app-name-button"
|
||||
onClick={() => setShowRenameModal(true)}
|
||||
data-cy="editor-app-name-input"
|
||||
>
|
||||
<span className="tw-font-title-large tw-truncate tw-w-full tw-block group-hover:tw-w-[calc(100%-24px)]">
|
||||
{appName}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ const AppExport = ({ darkMode }) => {
|
|||
setIsExportingApp(true);
|
||||
document.getElementById('maintenance-app-modal').click();
|
||||
}}
|
||||
data-cy="button-user-status-change"
|
||||
data-cy="export-app-button"
|
||||
>
|
||||
Export app
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -35,9 +35,8 @@ export const SidebarItem = forwardRef(
|
|||
const content = (
|
||||
<Button
|
||||
{...rest}
|
||||
className={`${className} ${
|
||||
selectedSidebarItem === icon && selectedSidebarItem !== 'comments' && 'sidebar-item--active'
|
||||
} ${icon}-icon`}
|
||||
className={`${className} ${selectedSidebarItem === icon && selectedSidebarItem !== 'comments' && 'sidebar-item--active'
|
||||
} ${icon}-icon`}
|
||||
onClick={onClick && onClick}
|
||||
ref={ref}
|
||||
type="button"
|
||||
|
|
@ -45,6 +44,7 @@ export const SidebarItem = forwardRef(
|
|||
variant="ghost"
|
||||
size="default"
|
||||
iconOnly
|
||||
data-cy={`left-sidebar-${icon?.toLowerCase() || 'unknown'}-button`}
|
||||
>
|
||||
{children && (
|
||||
<div className={'sidebar-svg-icon position-relative'}>
|
||||
|
|
|
|||
|
|
@ -163,6 +163,22 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
|
|||
updateDataQuery(options);
|
||||
};
|
||||
|
||||
let docLinkStatic = '';
|
||||
switch (selectedDataSource?.kind) {
|
||||
case 'restapi':
|
||||
docLinkStatic = `https://docs.tooljet.com/docs/data-sources/restapi/querying-rest-api`;
|
||||
break;
|
||||
case 'tooljetdb':
|
||||
docLinkStatic = `https://docs.tooljet.com/docs/tooljet-db/querying-tooljet-db`;
|
||||
break;
|
||||
case 'runjs':
|
||||
docLinkStatic = `https://docs.tooljet.com/docs/data-sources/run-js`;
|
||||
break;
|
||||
case 'runpy':
|
||||
docLinkStatic = `https://docs.tooljet.com/docs/data-sources/run-py`;
|
||||
break;
|
||||
}
|
||||
|
||||
const renderQueryElement = () => {
|
||||
return (
|
||||
<div
|
||||
|
|
@ -176,14 +192,28 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
|
|||
selectedDataSource?.kind === 'runpy' ||
|
||||
selectedDataSource?.kind === 'tooljetdb' ||
|
||||
(selectedDataSource?.kind === 'restapi' && selectedDataSource?.type !== 'default')) && (
|
||||
<ParameterList
|
||||
parameters={options.parameters}
|
||||
handleAddParameter={handleAddParameter}
|
||||
handleParameterChange={handleParameterChange}
|
||||
handleParameterRemove={handleParameterRemove}
|
||||
darkMode={darkMode}
|
||||
containerRef={paramListContainerRef}
|
||||
/>
|
||||
<>
|
||||
<div style={{ marginBottom: '2px' }}>
|
||||
{`To know more about querying ${selectedDataSource?.kind} data,`}
|
||||
|
||||
<a
|
||||
href={docLinkStatic}
|
||||
target="_blank"
|
||||
style={{ marginLeft: '0px !important', color: 'hsl(226, 70.0%, 55.5%)', textDecoration: 'underline' }}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t('globals.readDocumentation', 'read documentation').toLowerCase()}
|
||||
</a>
|
||||
</div>
|
||||
<ParameterList
|
||||
parameters={options.parameters}
|
||||
handleAddParameter={handleAddParameter}
|
||||
handleParameterChange={handleParameterChange}
|
||||
handleParameterRemove={handleParameterRemove}
|
||||
darkMode={darkMode}
|
||||
containerRef={paramListContainerRef}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<ElementToRender
|
||||
|
|
@ -314,7 +344,7 @@ export const BaseQueryManagerBody = ({ darkMode, activeTab, renderCopilot = () =
|
|||
changeDataQuery(newDataSource);
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<div style={{ marginBottom: '2px' }}>
|
||||
{`To know more about querying ${selectedDataSource?.kind} data,`}
|
||||
|
||||
<a
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const Runjs = (props) => {
|
|||
}, [props.options]);
|
||||
|
||||
return (
|
||||
<Card className="runjs-editor mb-3 !tw-mb-0">
|
||||
<div className="runjs-editor mb-3 !tw-mb-0 ">
|
||||
<CodeHinter
|
||||
renderCopilot={props.renderCopilot}
|
||||
type="multiline"
|
||||
|
|
@ -29,7 +29,7 @@ const Runjs = (props) => {
|
|||
cyLabel={`runjs`}
|
||||
delayOnChange={false}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ import useStore from '@/AppBuilder/_stores/store';
|
|||
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
import usePopoverObserver from '@/AppBuilder/_hooks/usePopoverObserver';
|
||||
import useWorkflowStore from '@/_stores/workflowStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function Workflows({ options, optionsChanged, currentState }) {
|
||||
const { moduleId } = useModuleContext();
|
||||
const { t } = useTranslation();
|
||||
const [workflowOptions, setWorkflowOptions] = useState([]);
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [_selectedWorkflowId, setSelectedWorkflowId] = useState(undefined);
|
||||
|
|
@ -51,6 +53,18 @@ export function Workflows({ options, optionsChanged, currentState }) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div style={{ marginBottom: '2px' }}>
|
||||
{`To know more about querying workflows data,`}
|
||||
|
||||
<a
|
||||
href={'https://docs.tooljet.ai/docs/workflows/how-to/trigger-workflow-from-app'}
|
||||
target="_blank"
|
||||
style={{ marginLeft: '0px !important', color: 'hsl(226, 70.0%, 55.5%)', textDecoration: 'underline' }}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t('globals.readDocumentation', 'read documentation').toLowerCase()}
|
||||
</a>
|
||||
</div>
|
||||
<label className="mb-1">Workflow</label>
|
||||
<div data-cy="workflow-dropdown"></div>
|
||||
<Select
|
||||
|
|
|
|||
|
|
@ -122,11 +122,7 @@ export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
|
|||
</div>
|
||||
<div className="col query-row-query-name">
|
||||
{isRenaming ? (
|
||||
<QueryRenameInput
|
||||
dataQuery={dataQuery}
|
||||
darkMode={darkMode}
|
||||
onUpdate={updateQueryName}
|
||||
/>
|
||||
<QueryRenameInput dataQuery={dataQuery} darkMode={darkMode} onUpdate={updateQueryName} />
|
||||
) : (
|
||||
<div className="query-name" data-cy={`list-query-${dataQuery.name.toLowerCase()}`}>
|
||||
<span
|
||||
|
|
@ -163,18 +159,20 @@ export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!shouldFreeze && <div className={`col-auto query-rename-delete-btn ${isQuerySelected ? 'd-flex' : 'd-none'}`}>
|
||||
<ButtonComponent
|
||||
iconOnly
|
||||
leadingIcon="morevertical01"
|
||||
onClick={(e) => toggleQueryHandlerMenu(true, `query-handler-menu-${dataQuery?.id}`)}
|
||||
size="small"
|
||||
variant="outline"
|
||||
className=""
|
||||
id={`query-handler-menu-${dataQuery?.id}`}
|
||||
data-cy={`delete-query-${dataQuery.name.toLowerCase()}`}
|
||||
/>
|
||||
</div>}
|
||||
{!shouldFreeze && (
|
||||
<div className={`col-auto query-rename-delete-btn ${isQuerySelected ? 'd-flex' : 'd-none'}`}>
|
||||
<ButtonComponent
|
||||
iconOnly
|
||||
leadingIcon="morevertical01"
|
||||
onClick={(e) => toggleQueryHandlerMenu(true, `query-handler-menu-${dataQuery?.id}`)}
|
||||
size="small"
|
||||
variant="outline"
|
||||
className=""
|
||||
id={`query-handler-menu-${dataQuery?.id}`}
|
||||
data-cy={`query-handler-menu-${dataQuery.name.toLowerCase()}`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Confirm
|
||||
show={isDeleting}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ const QueryCardMenu = ({ darkMode }) => {
|
|||
{QUERY_MENU_OPTIONS.map((option) => {
|
||||
const optionBody = (
|
||||
<div
|
||||
data-cy={`component-inspector-${String(option?.value).toLowerCase()}-button`}
|
||||
data-cy={`query-card-${String(option?.value).toLowerCase()}-button`}
|
||||
className="list-item-popover-option"
|
||||
key={option?.value}
|
||||
onClick={(e) => {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,9 @@ const FxSelect = ({
|
|||
/** Remove minFileCount and maxFileCount validations if multiple file selection is disabled */
|
||||
const getValidations = (componentMeta, component) => {
|
||||
const validations = Object.keys(componentMeta.validation || {});
|
||||
const enableMultipleValue = resolveReferences(component.component.definition.properties.enableMultiple?.value ?? false);
|
||||
const enableMultipleValue = resolveReferences(
|
||||
component.component.definition.properties.enableMultiple?.value ?? false
|
||||
);
|
||||
const enableMultipleFxActive = component.component.definition.properties.enableMultiple?.fxActive;
|
||||
|
||||
if (!enableMultipleValue && !enableMultipleFxActive) {
|
||||
|
|
@ -110,10 +112,17 @@ const getPropertiesBySection = (propertiesMeta) => {
|
|||
|
||||
const getConditionalAccordionItems = (component, renderCustomElement) => {
|
||||
const parseContent = resolveReferences(component.component.definition.properties.parseContent?.value ?? false);
|
||||
const parseFileType = resolveReferences(
|
||||
component.component.definition.properties.parseFileType?.value ?? 'auto-detect'
|
||||
);
|
||||
|
||||
const options = ['parseContent'];
|
||||
let renderOptions = options.map((option) => renderCustomElement(option));
|
||||
|
||||
const conditionalOptions = [{ name: 'parseFileType', condition: parseContent }];
|
||||
const conditionalOptions = [
|
||||
{ name: 'parseFileType', condition: parseContent },
|
||||
{ name: 'delimiter', condition: parseContent && parseFileType === 'csv' },
|
||||
];
|
||||
conditionalOptions.forEach(({ name, condition }) => {
|
||||
if (condition) renderOptions.push(renderCustomElement(name));
|
||||
});
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue