Merge branch 'develop' into map-read-only
This commit is contained in:
commit
daf936a79a
1699 changed files with 71992 additions and 72710 deletions
|
|
@ -12,3 +12,10 @@ charset = utf-8
|
|||
[{*.py,*.js,*.vue,*.css,*.scss,*.html}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
max_line_length = 99
|
||||
|
||||
# JSON files - mostly doctype schema files
|
||||
[{*.json}]
|
||||
insert_final_newline = false
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
|
|||
56
.flake8
56
.flake8
|
|
@ -1,37 +1,75 @@
|
|||
[flake8]
|
||||
ignore =
|
||||
B001,
|
||||
B007,
|
||||
B009,
|
||||
B010,
|
||||
B950,
|
||||
E101,
|
||||
E111,
|
||||
E114,
|
||||
E116,
|
||||
E117,
|
||||
E121,
|
||||
E122,
|
||||
E123,
|
||||
E124,
|
||||
E125,
|
||||
E126,
|
||||
E127,
|
||||
E128,
|
||||
E131,
|
||||
E201,
|
||||
E202,
|
||||
E203,
|
||||
E211,
|
||||
E221,
|
||||
E222,
|
||||
E223,
|
||||
E224,
|
||||
E225,
|
||||
E226,
|
||||
E228,
|
||||
E231,
|
||||
E241,
|
||||
E242,
|
||||
E251,
|
||||
E261,
|
||||
E262,
|
||||
E265,
|
||||
E266,
|
||||
E271,
|
||||
E272,
|
||||
E273,
|
||||
E274,
|
||||
E301,
|
||||
E302,
|
||||
E303,
|
||||
E305,
|
||||
E306,
|
||||
E402,
|
||||
E501,
|
||||
E502,
|
||||
E701,
|
||||
E702,
|
||||
E703,
|
||||
E741,
|
||||
F401,
|
||||
F403,
|
||||
F405,
|
||||
W191,
|
||||
W291,
|
||||
W292,
|
||||
W293,
|
||||
W391,
|
||||
W503,
|
||||
W504,
|
||||
F403,
|
||||
B007,
|
||||
B950,
|
||||
W191,
|
||||
E124, # closing bracket, irritating while writing QB code
|
||||
E131, # continuation line unaligned for hanging indent
|
||||
E123, # closing bracket does not match indentation of opening bracket's line
|
||||
E101, # ensured by use of black
|
||||
E711,
|
||||
E129,
|
||||
F841,
|
||||
E713,
|
||||
E712,
|
||||
B028,
|
||||
|
||||
max-line-length = 200
|
||||
exclude=.github/helper/semgrep_rules
|
||||
exclude=,test_*.py
|
||||
|
|
|
|||
|
|
@ -25,3 +25,12 @@ c0c5b2ebdddbe8898ce2d5e5365f4931ff73b6bf
|
|||
|
||||
# update python code to use 3.10 supported features
|
||||
81b37cb7d2160866afa2496873656afe53f0c145
|
||||
|
||||
# mass minified JSON schema
|
||||
85e3ee940353d7b0b517b33815148672e9a8b15b
|
||||
|
||||
# format JS files with pretter
|
||||
40f27f908a3890c9a90d2d96794fc31fcea63c59
|
||||
|
||||
# db.get_all -> get_all
|
||||
2eec621e95564c359ad22da79501a855c1f32b03
|
||||
|
|
|
|||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,5 +1,5 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Community Forum
|
||||
url: https://discuss.erpnext.com/
|
||||
url: https://discuss.frappe.io/c/framework/5
|
||||
about: For general QnA, discussions and community help.
|
||||
|
|
|
|||
109
.github/helper/ci.py
vendored
Normal file
109
.github/helper/ci.py
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See LICENSE
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
STANDARD_INCLUSIONS = ["*.py"]
|
||||
|
||||
STANDARD_EXCLUSIONS = [
|
||||
"*.js",
|
||||
"*.xml",
|
||||
"*.pyc",
|
||||
"*.css",
|
||||
"*.less",
|
||||
"*.scss",
|
||||
"*.vue",
|
||||
"*.html",
|
||||
"*/test_*",
|
||||
"*/node_modules/*",
|
||||
"*/doctype/*/*_dashboard.py",
|
||||
"*/patches/*",
|
||||
".github/*",
|
||||
]
|
||||
|
||||
# tested via commands' test suite
|
||||
TESTED_VIA_CLI = [
|
||||
"*/frappe/installer.py",
|
||||
"*/frappe/utils/install.py",
|
||||
"*/frappe/utils/scheduler.py",
|
||||
"*/frappe/utils/doctor.py",
|
||||
"*/frappe/build.py",
|
||||
"*/frappe/database/__init__.py",
|
||||
"*/frappe/database/db_manager.py",
|
||||
"*/frappe/database/**/setup_db.py",
|
||||
]
|
||||
|
||||
FRAPPE_EXCLUSIONS = [
|
||||
"*/tests/*",
|
||||
"*/commands/*",
|
||||
"*/frappe/change_log/*",
|
||||
"*/frappe/exceptions*",
|
||||
"*/frappe/desk/page/setup_wizard/setup_wizard.py",
|
||||
"*/frappe/coverage.py",
|
||||
"*frappe/setup.py",
|
||||
"*/frappe/hooks.py",
|
||||
"*/doctype/*/*_dashboard.py",
|
||||
"*/patches/*",
|
||||
"*/.github/helper/ci.py",
|
||||
] + TESTED_VIA_CLI
|
||||
|
||||
|
||||
def get_bench_path():
|
||||
return Path(__file__).resolve().parents[4]
|
||||
|
||||
|
||||
class CodeCoverage:
|
||||
def __init__(self, with_coverage, app):
|
||||
self.with_coverage = with_coverage
|
||||
self.app = app or "frappe"
|
||||
|
||||
def __enter__(self):
|
||||
if self.with_coverage:
|
||||
import os
|
||||
|
||||
from coverage import Coverage
|
||||
|
||||
# Generate coverage report only for app that is being tested
|
||||
source_path = os.path.join(get_bench_path(), "apps", self.app)
|
||||
print(f"Source path: {source_path}")
|
||||
omit = STANDARD_EXCLUSIONS[:]
|
||||
|
||||
if self.app == "frappe":
|
||||
omit.extend(FRAPPE_EXCLUSIONS)
|
||||
|
||||
self.coverage = Coverage(source=[source_path], omit=omit, include=STANDARD_INCLUSIONS)
|
||||
self.coverage.start()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
if self.with_coverage:
|
||||
self.coverage.stop()
|
||||
self.coverage.save()
|
||||
self.coverage.xml_report()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = "frappe"
|
||||
site = os.environ.get("SITE") or "test_site"
|
||||
use_orchestrator = bool(os.environ.get("ORCHESTRATOR_URL"))
|
||||
build_number = 1
|
||||
total_builds = 1
|
||||
|
||||
try:
|
||||
build_number = int(os.environ.get("BUILD_NUMBER"))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
total_builds = int(os.environ.get("TOTAL_BUILDS"))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
with CodeCoverage(with_coverage=True, app=app):
|
||||
if use_orchestrator:
|
||||
from frappe.parallel_test_runner import ParallelTestWithOrchestrator
|
||||
|
||||
ParallelTestWithOrchestrator(app, site=site)
|
||||
else:
|
||||
from frappe.parallel_test_runner import ParallelTestRunner
|
||||
|
||||
ParallelTestRunner(app, site=site, build_number=build_number, total_builds=total_builds)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"db_host": "127.0.0.1",
|
||||
"db_port": 3306,
|
||||
"db_name": "test_frappe_consumer",
|
||||
"db_name": "test_frappe",
|
||||
"db_password": "test_frappe",
|
||||
"allow_tests": true,
|
||||
"db_type": "mariadb",
|
||||
|
|
@ -13,5 +13,6 @@
|
|||
"root_login": "root",
|
||||
"root_password": "travis",
|
||||
"host_name": "http://test_site:8000",
|
||||
"monitor": 1,
|
||||
"server_script_enabled": true
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"db_host": "127.0.0.1",
|
||||
"db_port": 5432,
|
||||
"db_name": "test_frappe_consumer",
|
||||
"db_name": "test_frappe",
|
||||
"db_password": "test_frappe",
|
||||
"db_type": "postgres",
|
||||
"allow_tests": true,
|
||||
95
.github/helper/documentation.py
vendored
95
.github/helper/documentation.py
vendored
|
|
@ -3,48 +3,71 @@ import requests
|
|||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
docs_repos = [
|
||||
"frappe_docs",
|
||||
"erpnext_documentation",
|
||||
WEBSITE_REPOS = [
|
||||
"erpnext_com",
|
||||
"frappe_io",
|
||||
]
|
||||
|
||||
DOCUMENTATION_DOMAINS = [
|
||||
"docs.erpnext.com",
|
||||
"frappeframework.com",
|
||||
]
|
||||
|
||||
def uri_validator(x):
|
||||
result = urlparse(x)
|
||||
return all([result.scheme, result.netloc, result.path])
|
||||
|
||||
def docs_link_exists(body):
|
||||
for line in body.splitlines():
|
||||
for word in line.split():
|
||||
if word.startswith('http') and uri_validator(word):
|
||||
parsed_url = urlparse(word)
|
||||
if parsed_url.netloc == "github.com":
|
||||
parts = parsed_url.path.split('/')
|
||||
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in docs_repos:
|
||||
return True
|
||||
if parsed_url.netloc in ["docs.erpnext.com", "frappeframework.com"]:
|
||||
return True
|
||||
def is_valid_url(url: str) -> bool:
|
||||
parts = urlparse(url)
|
||||
return all((parts.scheme, parts.netloc, parts.path))
|
||||
|
||||
|
||||
def is_documentation_link(word: str) -> bool:
|
||||
if not word.startswith("http") or not is_valid_url(word):
|
||||
return False
|
||||
|
||||
parsed_url = urlparse(word)
|
||||
if parsed_url.netloc in DOCUMENTATION_DOMAINS:
|
||||
return True
|
||||
|
||||
if parsed_url.netloc == "github.com":
|
||||
parts = parsed_url.path.split("/")
|
||||
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in WEBSITE_REPOS:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def contains_documentation_link(body: str) -> bool:
|
||||
return any(
|
||||
is_documentation_link(word)
|
||||
for line in body.splitlines()
|
||||
for word in line.split()
|
||||
)
|
||||
|
||||
|
||||
def check_pull_request(number: str) -> "tuple[int, str]":
|
||||
response = requests.get(f"https://api.github.com/repos/frappe/frappe/pulls/{number}")
|
||||
if not response.ok:
|
||||
return 1, "Pull Request Not Found! ⚠️"
|
||||
|
||||
payload = response.json()
|
||||
title = (payload.get("title") or "").lower().strip()
|
||||
head_sha = (payload.get("head") or {}).get("sha")
|
||||
body = (payload.get("body") or "").lower()
|
||||
|
||||
if (
|
||||
not title.startswith("feat")
|
||||
or not head_sha
|
||||
or "no-docs" in body
|
||||
or "backport" in body
|
||||
):
|
||||
return 0, "Skipping documentation checks... 🏃"
|
||||
|
||||
if contains_documentation_link(body):
|
||||
return 0, "Documentation Link Found. You're Awesome! 🎉"
|
||||
|
||||
return 1, "Documentation Link Not Found! ⚠️"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pr = sys.argv[1]
|
||||
response = requests.get("https://api.github.com/repos/frappe/frappe/pulls/{}".format(pr))
|
||||
|
||||
if response.ok:
|
||||
payload = response.json()
|
||||
title = (payload.get("title") or "").lower()
|
||||
head_sha = (payload.get("head") or {}).get("sha")
|
||||
body = (payload.get("body") or "").lower()
|
||||
|
||||
if title.startswith("feat") and head_sha and "no-docs" not in body:
|
||||
if docs_link_exists(body):
|
||||
print("Documentation Link Found. You're Awesome! 🎉")
|
||||
|
||||
else:
|
||||
print("Documentation Link Not Found! ⚠️")
|
||||
sys.exit(1)
|
||||
|
||||
else:
|
||||
print("Skipping documentation checks... 🏃")
|
||||
exit_code, message = check_pull_request(sys.argv[1])
|
||||
print(message)
|
||||
sys.exit(exit_code)
|
||||
|
|
|
|||
75
.github/helper/flake8.conf
vendored
75
.github/helper/flake8.conf
vendored
|
|
@ -1,75 +0,0 @@
|
|||
[flake8]
|
||||
ignore =
|
||||
B001,
|
||||
B007,
|
||||
B009,
|
||||
B010,
|
||||
B950,
|
||||
E101,
|
||||
E111,
|
||||
E114,
|
||||
E116,
|
||||
E117,
|
||||
E121,
|
||||
E122,
|
||||
E123,
|
||||
E124,
|
||||
E125,
|
||||
E126,
|
||||
E127,
|
||||
E128,
|
||||
E131,
|
||||
E201,
|
||||
E202,
|
||||
E203,
|
||||
E211,
|
||||
E221,
|
||||
E222,
|
||||
E223,
|
||||
E224,
|
||||
E225,
|
||||
E226,
|
||||
E228,
|
||||
E231,
|
||||
E241,
|
||||
E242,
|
||||
E251,
|
||||
E261,
|
||||
E262,
|
||||
E265,
|
||||
E266,
|
||||
E271,
|
||||
E272,
|
||||
E273,
|
||||
E274,
|
||||
E301,
|
||||
E302,
|
||||
E303,
|
||||
E305,
|
||||
E306,
|
||||
E402,
|
||||
E501,
|
||||
E502,
|
||||
E701,
|
||||
E702,
|
||||
E703,
|
||||
E741,
|
||||
F401,
|
||||
F403,
|
||||
F405,
|
||||
W191,
|
||||
W291,
|
||||
W292,
|
||||
W293,
|
||||
W391,
|
||||
W503,
|
||||
W504,
|
||||
E711,
|
||||
E129,
|
||||
F841,
|
||||
E713,
|
||||
E712,
|
||||
|
||||
|
||||
max-line-length = 200
|
||||
exclude=.github/helper/semgrep_rules,test_*.py
|
||||
98
.github/helper/install.sh
vendored
98
.github/helper/install.sh
vendored
|
|
@ -1,67 +1,71 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd ~ || exit
|
||||
|
||||
pip install frappe-bench
|
||||
echo "Setting Up Bench..."
|
||||
|
||||
pip install frappe-bench
|
||||
bench -v init frappe-bench --skip-assets --python "$(which python)" --frappe-path "${GITHUB_WORKSPACE}"
|
||||
cd ./frappe-bench || exit
|
||||
|
||||
bench -v setup requirements --dev
|
||||
if [ "$TYPE" == "ui" ]
|
||||
then
|
||||
bench -v setup requirements --node;
|
||||
fi
|
||||
|
||||
echo "Setting Up Sites & Database..."
|
||||
|
||||
mkdir ~/frappe-bench/sites/test_site
|
||||
cp "${GITHUB_WORKSPACE}/.github/helper/consumer_db/$DB.json" ~/frappe-bench/sites/test_site/site_config.json
|
||||
cp "${GITHUB_WORKSPACE}/.github/helper/db/$DB.json" ~/frappe-bench/sites/test_site/site_config.json
|
||||
|
||||
if [ "$TYPE" == "server" ]; then
|
||||
mkdir ~/frappe-bench/sites/test_site_producer;
|
||||
cp "${GITHUB_WORKSPACE}/.github/helper/producer_db/$DB.json" ~/frappe-bench/sites/test_site_producer/site_config.json;
|
||||
if [ "$DB" == "mariadb" ]
|
||||
then
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "SET GLOBAL character_set_server = 'utf8mb4'";
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'";
|
||||
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE DATABASE test_frappe";
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'";
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'";
|
||||
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "FLUSH PRIVILEGES";
|
||||
fi
|
||||
if [ "$DB" == "postgres" ]
|
||||
then
|
||||
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_frappe" -U postgres;
|
||||
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_frappe WITH PASSWORD 'test_frappe'" -U postgres;
|
||||
fi
|
||||
|
||||
if [ "$DB" == "mariadb" ];then
|
||||
curl -LsS -O https://downloads.mariadb.com/MariaDB/mariadb_repo_setup
|
||||
sudo bash mariadb_repo_setup --mariadb-server-version=10.6
|
||||
sudo apt install mariadb-client
|
||||
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "SET GLOBAL character_set_server = 'utf8mb4'";
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'";
|
||||
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE DATABASE test_frappe_consumer";
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE USER 'test_frappe_consumer'@'localhost' IDENTIFIED BY 'test_frappe_consumer'";
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "GRANT ALL PRIVILEGES ON \`test_frappe_consumer\`.* TO 'test_frappe_consumer'@'localhost'";
|
||||
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE DATABASE test_frappe_producer";
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE USER 'test_frappe_producer'@'localhost' IDENTIFIED BY 'test_frappe_producer'";
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "GRANT ALL PRIVILEGES ON \`test_frappe_producer\`.* TO 'test_frappe_producer'@'localhost'";
|
||||
|
||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "FLUSH PRIVILEGES";
|
||||
fi
|
||||
|
||||
if [ "$DB" == "postgres" ];then
|
||||
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_frappe_consumer" -U postgres;
|
||||
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_frappe_consumer WITH PASSWORD 'test_frappe'" -U postgres;
|
||||
|
||||
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_frappe_producer" -U postgres;
|
||||
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_frappe_producer WITH PASSWORD 'test_frappe'" -U postgres;
|
||||
fi
|
||||
|
||||
cd ./frappe-bench || exit
|
||||
echo "Setting Up Procfile..."
|
||||
|
||||
sed -i 's/^watch:/# watch:/g' Procfile
|
||||
sed -i 's/^schedule:/# schedule:/g' Procfile
|
||||
|
||||
if [ "$TYPE" == "server" ]; then sed -i 's/^socketio:/# socketio:/g' Procfile; fi
|
||||
if [ "$TYPE" == "server" ]; then sed -i 's/^redis_socketio:/# redis_socketio:/g' Procfile; fi
|
||||
if [ "$TYPE" == "server" ]
|
||||
then
|
||||
sed -i 's/^socketio:/# socketio:/g' Procfile
|
||||
sed -i 's/^redis_socketio:/# redis_socketio:/g' Procfile
|
||||
fi
|
||||
|
||||
if [ "$TYPE" == "ui" ]; then bench -v setup requirements --node; fi
|
||||
bench -v setup requirements --dev
|
||||
if [ "$TYPE" == "ui" ]
|
||||
then
|
||||
sed -i 's/^web: bench serve/web: bench serve --with-coverage/g' Procfile
|
||||
fi
|
||||
|
||||
if [ "$TYPE" == "ui" ]; then sed -i 's/^web: bench serve/web: bench serve --with-coverage/g' Procfile; fi
|
||||
echo "Starting Bench..."
|
||||
|
||||
# install node-sass which is required for website theme test
|
||||
cd ./apps/frappe || exit
|
||||
yarn add node-sass@4.13.1
|
||||
cd ../..
|
||||
bench start &> bench_start.log &
|
||||
|
||||
if [ "$TYPE" == "server" ]
|
||||
then
|
||||
CI=Yes bench build --app frappe &
|
||||
build_pid=$!
|
||||
fi
|
||||
|
||||
bench start &
|
||||
bench --site test_site reinstall --yes
|
||||
if [ "$TYPE" == "server" ]; then bench --site test_site_producer reinstall --yes; fi
|
||||
if [ "$TYPE" == "server" ]; then CI=Yes bench build --app frappe; fi
|
||||
|
||||
if [ "$TYPE" == "server" ]
|
||||
then
|
||||
# wait till assets are built succesfully
|
||||
wait $build_pid
|
||||
fi
|
||||
|
|
|
|||
22
.github/helper/install_dependencies.sh
vendored
22
.github/helper/install_dependencies.sh
vendored
|
|
@ -1,19 +1,13 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Check for merge conflicts before proceeding
|
||||
python -m compileall -f "${GITHUB_WORKSPACE}"
|
||||
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
|
||||
then echo "Found merge conflicts"
|
||||
exit 1
|
||||
fi
|
||||
echo "Setting Up System Dependencies..."
|
||||
|
||||
# install wkhtmltopdf
|
||||
wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
|
||||
tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
|
||||
sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
||||
sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
||||
sudo apt update
|
||||
sudo apt install libcups2-dev redis-server mariadb-client-10.6
|
||||
|
||||
# install cups
|
||||
sudo apt update && sudo apt install libcups2-dev redis-server
|
||||
install_wkhtmltopdf() {
|
||||
wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
|
||||
sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb
|
||||
}
|
||||
install_wkhtmltopdf &
|
||||
|
|
|
|||
16
.github/helper/producer_db/mariadb.json
vendored
16
.github/helper/producer_db/mariadb.json
vendored
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"db_host": "127.0.0.1",
|
||||
"db_port": 3306,
|
||||
"db_name": "test_frappe_producer",
|
||||
"db_password": "test_frappe",
|
||||
"allow_tests": true,
|
||||
"db_type": "mariadb",
|
||||
"auto_email_id": "test@example.com",
|
||||
"mail_server": "smtp.example.com",
|
||||
"mail_login": "test@example.com",
|
||||
"mail_password": "test",
|
||||
"admin_password": "admin",
|
||||
"root_login": "root",
|
||||
"root_password": "travis",
|
||||
"host_name": "http://test_site_producer:8000"
|
||||
}
|
||||
16
.github/helper/producer_db/postgres.json
vendored
16
.github/helper/producer_db/postgres.json
vendored
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"db_host": "127.0.0.1",
|
||||
"db_port": 5432,
|
||||
"db_name": "test_frappe_producer",
|
||||
"db_password": "test_frappe",
|
||||
"db_type": "postgres",
|
||||
"allow_tests": true,
|
||||
"auto_email_id": "test@example.com",
|
||||
"mail_server": "smtp.example.com",
|
||||
"mail_login": "test@example.com",
|
||||
"mail_password": "test",
|
||||
"admin_password": "admin",
|
||||
"root_login": "postgres",
|
||||
"root_password": "travis",
|
||||
"host_name": "http://test_site_producer:8000"
|
||||
}
|
||||
71
.github/helper/roulette.py
vendored
71
.github/helper/roulette.py
vendored
|
|
@ -4,8 +4,10 @@ import re
|
|||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
from functools import lru_cache
|
||||
from urllib.error import HTTPError
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
|
|
@ -15,41 +17,78 @@ def fetch_pr_data(pr_number, repo, endpoint=""):
|
|||
if endpoint:
|
||||
api_url += f"/{endpoint}"
|
||||
|
||||
req = urllib.request.Request(api_url)
|
||||
res = urllib.request.urlopen(req)
|
||||
return json.loads(res.read().decode('utf8'))
|
||||
res = req(api_url)
|
||||
return json.loads(res.read().decode("utf8"))
|
||||
|
||||
|
||||
def req(url):
|
||||
"Simple resilient request call to handle rate limits."
|
||||
headers = None
|
||||
token = os.environ.get("GITHUB_TOKEN")
|
||||
if token:
|
||||
headers = {"authorization": f"Bearer {token}"}
|
||||
|
||||
retries = 0
|
||||
while True:
|
||||
try:
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
return urllib.request.urlopen(req)
|
||||
except HTTPError as exc:
|
||||
if exc.code == 403 and retries < 5:
|
||||
retries += 1
|
||||
time.sleep(retries)
|
||||
continue
|
||||
raise
|
||||
|
||||
|
||||
def get_files_list(pr_number, repo="frappe/frappe"):
|
||||
return [change["filename"] for change in fetch_pr_data(pr_number, repo, "files")]
|
||||
|
||||
|
||||
def get_output(command, shell=True):
|
||||
print(command)
|
||||
command = shlex.split(command)
|
||||
return subprocess.check_output(command, shell=shell, encoding="utf8").strip()
|
||||
|
||||
|
||||
def has_skip_ci_label(pr_number, repo="frappe/frappe"):
|
||||
return has_label(pr_number, "Skip CI", repo)
|
||||
|
||||
|
||||
def has_run_server_tests_label(pr_number, repo="frappe/frappe"):
|
||||
return has_label(pr_number, "Run Server Tests", repo)
|
||||
|
||||
|
||||
def has_run_ui_tests_label(pr_number, repo="frappe/frappe"):
|
||||
return has_label(pr_number, "Run UI Tests", repo)
|
||||
|
||||
|
||||
def has_label(pr_number, label, repo="frappe/frappe"):
|
||||
return any([fetched_label["name"] for fetched_label in fetch_pr_data(pr_number, repo)["labels"] if fetched_label["name"] == label])
|
||||
return any(
|
||||
[
|
||||
fetched_label["name"]
|
||||
for fetched_label in fetch_pr_data(pr_number, repo)["labels"]
|
||||
if fetched_label["name"] == label
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def is_py(file):
|
||||
return file.endswith("py")
|
||||
|
||||
|
||||
def is_ci(file):
|
||||
return ".github" in file
|
||||
|
||||
|
||||
def is_frontend_code(file):
|
||||
return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html"))
|
||||
return file.lower().endswith(
|
||||
(".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html")
|
||||
)
|
||||
|
||||
|
||||
def is_docs(file):
|
||||
regex = re.compile(r'\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE')
|
||||
regex = re.compile(r"\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE")
|
||||
return bool(regex.search(file))
|
||||
|
||||
|
||||
|
|
@ -61,8 +100,7 @@ if __name__ == "__main__":
|
|||
|
||||
# this is a push build, run all builds
|
||||
if not pr_number:
|
||||
os.system('echo "::set-output name=build::strawberry"')
|
||||
os.system('echo "::set-output name=build-server::strawberry"')
|
||||
os.system('echo "build=strawberry" >> $GITHUB_OUTPUT')
|
||||
sys.exit(0)
|
||||
|
||||
files_list = files_list or get_files_list(pr_number=pr_number, repo=repo)
|
||||
|
|
@ -78,8 +116,13 @@ if __name__ == "__main__":
|
|||
only_py_changed = updated_py_file_count == len(files_list)
|
||||
|
||||
if has_skip_ci_label(pr_number, repo):
|
||||
print("Found `Skip CI` label on pr, stopping build process.")
|
||||
sys.exit(0)
|
||||
if build_type == "ui" and has_run_ui_tests_label(pr_number, repo):
|
||||
print("Running UI tests only.")
|
||||
elif build_type == "server" and has_run_server_tests_label(pr_number, repo):
|
||||
print("Running server tests only.")
|
||||
else:
|
||||
print("Found `Skip CI` label on pr, stopping build process.")
|
||||
sys.exit(0)
|
||||
|
||||
elif ci_files_changed:
|
||||
print("CI related files were updated, running all build processes.")
|
||||
|
|
@ -88,7 +131,11 @@ if __name__ == "__main__":
|
|||
print("Only docs were updated, stopping build process.")
|
||||
sys.exit(0)
|
||||
|
||||
elif only_frontend_code_changed and build_type == "server" and not has_run_server_tests_label(pr_number, repo):
|
||||
elif (
|
||||
only_frontend_code_changed
|
||||
and build_type == "server"
|
||||
and not has_run_server_tests_label(pr_number, repo)
|
||||
):
|
||||
print("Only Frontend code was updated; Stopping Python build process.")
|
||||
sys.exit(0)
|
||||
|
||||
|
|
@ -96,4 +143,4 @@ if __name__ == "__main__":
|
|||
print("Only Python code was updated, stopping Cypress build process.")
|
||||
sys.exit(0)
|
||||
|
||||
os.system('echo "::set-output name=build::strawberry"')
|
||||
os.system('echo "build=strawberry" >> $GITHUB_OUTPUT')
|
||||
|
|
|
|||
15
.github/helper/translation.py
vendored
15
.github/helper/translation.py
vendored
|
|
@ -20,19 +20,12 @@ for _file in files_to_scan:
|
|||
if 'frappe-lint: disable-translate' in line:
|
||||
continue
|
||||
|
||||
start_matches = start_pattern.search(line)
|
||||
if start_matches:
|
||||
starts_with_f = starts_with_f_pattern.search(line)
|
||||
|
||||
if starts_with_f:
|
||||
has_f_string = f_string_pattern.search(line)
|
||||
if has_f_string:
|
||||
if start_matches := start_pattern.search(line):
|
||||
if starts_with_f := starts_with_f_pattern.search(line):
|
||||
if has_f_string := f_string_pattern.search(line):
|
||||
errors_encounter += 1
|
||||
print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}')
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
|
||||
continue
|
||||
match = pattern.search(line)
|
||||
error_found = False
|
||||
|
||||
|
|
|
|||
4
.github/labeler.yml
vendored
Normal file
4
.github/labeler.yml
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Any python files modifed but no test files modified
|
||||
add-test-cases:
|
||||
- any: ['frappe/**/*.py']
|
||||
all: ['!frappe/**/test*.py']
|
||||
30
.github/semantic.yml
vendored
30
.github/semantic.yml
vendored
|
|
@ -1,30 +0,0 @@
|
|||
# Always validate the PR title AND all the commits
|
||||
titleAndCommits: true
|
||||
|
||||
# Allow use of Merge commits (eg on github: "Merge branch 'master' into feature/ride-unicorns")
|
||||
# this is only relevant when using commitsOnly: true (or titleAndCommits: true)
|
||||
allowMergeCommits: true
|
||||
|
||||
# Allow use of Revert commits (eg on github: "Revert "feat: ride unicorns"")
|
||||
# this is only relevant when using commitsOnly: true (or titleAndCommits: true)
|
||||
allowRevertCommits: true
|
||||
|
||||
# For allowed PR types: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json
|
||||
# Tool Reference: https://github.com/zeke/semantic-pull-requests
|
||||
|
||||
# By default types specified in commitizen/conventional-commit-types is used.
|
||||
# See: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json
|
||||
# You can override the valid types
|
||||
types:
|
||||
- BREAKING CHANGE
|
||||
- feat
|
||||
- fix
|
||||
- docs
|
||||
- style
|
||||
- refactor
|
||||
- perf
|
||||
- test
|
||||
- build
|
||||
- ci
|
||||
- chore
|
||||
- revert
|
||||
26
.github/workflows/backport.yml
vendored
Normal file
26
.github/workflows/backport.yml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: "frappe/backport"
|
||||
path: ./actions
|
||||
ref: develop
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: Run backport
|
||||
uses: ./actions/backport
|
||||
with:
|
||||
token: ${{secrets.RELEASE_TOKEN}}
|
||||
labelsToAdd: "backport"
|
||||
title: "{{originalTitle}}"
|
||||
|
|
@ -16,10 +16,10 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Setup Node.js v14
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 14
|
||||
node-version: 18
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
npm install @semantic-release/git @semantic-release/exec --no-save
|
||||
|
|
@ -31,4 +31,4 @@ jobs:
|
|||
GIT_AUTHOR_EMAIL: "developers@frappe.io"
|
||||
GIT_COMMITTER_NAME: "Frappe PR Bot"
|
||||
GIT_COMMITTER_EMAIL: "developers@frappe.io"
|
||||
run: npx semantic-release
|
||||
run: npx semantic-release
|
||||
22
.github/workflows/deps-checker.yml
vendored
22
.github/workflows/deps-checker.yml
vendored
|
|
@ -1,22 +0,0 @@
|
|||
name: 'Python Dependency Check'
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deps-vulnerable-check:
|
||||
name: 'Vulnerable Dependency'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- uses: actions/checkout@v3
|
||||
- run: pip install pip-audit
|
||||
- run: pip-audit ${GITHUB_WORKSPACE}
|
||||
20
.github/workflows/docker-release.yml
vendored
20
.github/workflows/docker-release.yml
vendored
|
|
@ -1,20 +0,0 @@
|
|||
name: 'Trigger Docker build on release'
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
curl:
|
||||
permissions:
|
||||
contents: none
|
||||
name: 'Trigger Docker build on release'
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: alpine:latest
|
||||
steps:
|
||||
- name: curl
|
||||
run: |
|
||||
apk add curl bash
|
||||
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.CI_PAT }}" https://api.github.com/repos/frappe/frappe_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}'
|
||||
28
.github/workflows/docs-checker.yml
vendored
28
.github/workflows/docs-checker.yml
vendored
|
|
@ -1,28 +0,0 @@
|
|||
name: 'Documentation Check'
|
||||
on:
|
||||
pull_request:
|
||||
types: [ opened, synchronize, reopened, edited ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
docs-required:
|
||||
name: 'Documentation Required'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 'Setup Environment'
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: 'Clone repo'
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Validate Docs
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
run: |
|
||||
pip install requests --quiet
|
||||
python $GITHUB_WORKSPACE/.github/helper/documentation.py $PR_NUMBER
|
||||
32
.github/workflows/initiate_release.yml
vendored
Normal file
32
.github/workflows/initiate_release.yml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# This workflow is agnostic to branches. Only maintain on develop branch.
|
||||
# To add/remove versions just modify the matrix.
|
||||
|
||||
name: Create weekly release pull requests
|
||||
on:
|
||||
schedule:
|
||||
# 9:30 UTC => 3 PM IST Tuesday
|
||||
- cron: "30 9 * * 2"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: ["13", "14"]
|
||||
|
||||
steps:
|
||||
- uses: octokit/request-action@v2.x
|
||||
with:
|
||||
route: POST /repos/{owner}/{repo}/pulls
|
||||
owner: frappe
|
||||
repo: frappe
|
||||
title: |-
|
||||
"chore: release v${{ matrix.version }}"
|
||||
body: "Automated weekly release."
|
||||
base: version-${{ matrix.version }}
|
||||
head: version-${{ matrix.version }}-hotfix
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
12
.github/workflows/labeller.yml
vendored
Normal file
12
.github/workflows/labeller.yml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
name: "Pull Request Labeler"
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened]
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v4
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
95
.github/workflows/linters.yml
vendored
95
.github/workflows/linters.yml
vendored
|
|
@ -1,29 +1,100 @@
|
|||
name: Linters
|
||||
|
||||
on:
|
||||
pull_request: { }
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: commitcheck-frappe-${{ github.event_name }}-${{ github.event.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
linters:
|
||||
name: Frappe Linter
|
||||
commit-lint:
|
||||
name: 'Semantic Commits'
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 200
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
check-latest: true
|
||||
|
||||
- name: Set up Python
|
||||
- name: Check commit titles
|
||||
run: |
|
||||
npm install @commitlint/cli @commitlint/config-conventional
|
||||
npx commitlint --verbose --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
docs-required:
|
||||
name: 'Documentation Required'
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
steps:
|
||||
- name: 'Setup Environment'
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.11'
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install and Run Pre-commit
|
||||
uses: pre-commit/action@v3.0.0
|
||||
- name: Validate Docs
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
run: |
|
||||
pip install requests --quiet
|
||||
python $GITHUB_WORKSPACE/.github/helper/documentation.py $PR_NUMBER
|
||||
|
||||
linter:
|
||||
name: 'Frappe Linter'
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
|
||||
- name: Download Semgrep rules
|
||||
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
|
||||
|
||||
- name: Download semgrep
|
||||
run: pip install semgrep==0.97.0
|
||||
|
||||
- name: Run Semgrep rules
|
||||
run: semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness
|
||||
run: |
|
||||
pip install semgrep==0.97.0
|
||||
semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness
|
||||
|
||||
deps-vulnerable-check:
|
||||
name: 'Vulnerable Dependency Check'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Install and run pip-audit
|
||||
run: |
|
||||
pip install pip-audit
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
sed -i '/dropbox/d' pyproject.toml # Remove dropbox temporarily https://github.com/dropbox/dropbox-sdk-python/pull/456
|
||||
pip-audit --desc on .
|
||||
|
|
|
|||
21
.github/workflows/lock.yml
vendored
Normal file
21
.github/workflows/lock.yml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
name: 'Lock threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: 14
|
||||
pr-inactive-days: 14
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
name: 'Frappe Assets'
|
||||
name: 'Release'
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [ created ]
|
||||
types: [released]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
|
@ -16,9 +19,11 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: 'frappe'
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
python-version: '12.x'
|
||||
node-version: 16
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
|
@ -36,7 +41,7 @@ jobs:
|
|||
|
||||
- name: Get release
|
||||
id: get_release
|
||||
uses: bruceadams/get-release@v1.2.3
|
||||
uses: bruceadams/get-release@v1.3.1
|
||||
|
||||
- name: Upload built Assets to Release
|
||||
uses: actions/upload-release-asset@v1.0.2
|
||||
|
|
@ -45,3 +50,16 @@ jobs:
|
|||
asset_path: build/assets.tar.gz
|
||||
asset_name: assets.tar.gz
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
docker-release:
|
||||
name: 'Trigger Docker build on release'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: none
|
||||
container:
|
||||
image: alpine:latest
|
||||
steps:
|
||||
- name: curl
|
||||
run: |
|
||||
apk add curl bash
|
||||
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.CI_PAT }}" https://api.github.com/repos/frappe/frappe_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}'
|
||||
121
.github/workflows/patch-mariadb-tests.yml
vendored
121
.github/workflows/patch-mariadb-tests.yml
vendored
|
|
@ -1,21 +1,45 @@
|
|||
name: Patch
|
||||
|
||||
on: [pull_request, workflow_dispatch]
|
||||
name: Patch (MariaDB)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: patch-mariadb-develop-${{ github.event.number }}
|
||||
group: patch-mariadb-develop-${{ github.event_name }}-${{ github.event.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
# Do not change this as GITHUB_TOKEN is being used by roulette
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
checkrun:
|
||||
name: Build Check
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
name: Patch Test
|
||||
outputs:
|
||||
build: ${{ steps.check-build.outputs.build }}
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check if build should be run
|
||||
id: check-build
|
||||
run: |
|
||||
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
|
||||
env:
|
||||
TYPE: "server"
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
test:
|
||||
name: Patch
|
||||
runs-on: ubuntu-latest
|
||||
needs: checkrun
|
||||
if: ${{ needs.checkrun.outputs.build == 'strawberry' }}
|
||||
timeout-minutes: 60
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
|
|
@ -30,6 +54,13 @@ jobs:
|
|||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check for Merge Conflicts
|
||||
run: |
|
||||
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
|
||||
then echo "Found merge conflicts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Setup Python
|
||||
uses: "gabrielfalcao/pyenv-action@v10"
|
||||
with:
|
||||
|
|
@ -38,24 +69,13 @@ jobs:
|
|||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 14
|
||||
node-version: 16
|
||||
check-latest: true
|
||||
|
||||
- name: Check if build should be run
|
||||
id: check-build
|
||||
run: |
|
||||
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
|
||||
env:
|
||||
TYPE: "server"
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
|
||||
- name: Add to Hosts
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Cache pip
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
|
|
@ -64,26 +84,11 @@ jobs:
|
|||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
|
|
@ -92,25 +97,18 @@ jobs:
|
|||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install Dependencies
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
|
||||
env:
|
||||
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
|
||||
AFTER: ${{ env.GITHUB_EVENT_PATH.after }}
|
||||
TYPE: server
|
||||
|
||||
- name: Install
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: |
|
||||
bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
|
||||
pip install frappe-bench
|
||||
pyenv global $(pyenv versions | grep '3.10')
|
||||
bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||
env:
|
||||
DB: mariadb
|
||||
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
|
||||
AFTER: ${{ env.GITHUB_EVENT_PATH.after }}
|
||||
TYPE: server
|
||||
DB: mariadb
|
||||
|
||||
- name: Run Patch Tests
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: |
|
||||
cd ~/frappe-bench/
|
||||
wget https://frappeframework.com/files/v10-frappe.sql.gz
|
||||
|
|
@ -120,23 +118,28 @@ jobs:
|
|||
cd apps/frappe/
|
||||
git remote set-url upstream https://github.com/frappe/frappe.git
|
||||
|
||||
pyenv global $(pyenv versions | grep '3.7')
|
||||
for version in $(seq 12 13)
|
||||
do
|
||||
echo "Updating to v$version"
|
||||
branch_name="version-$version-hotfix"
|
||||
git fetch --depth 1 upstream $branch_name:$branch_name
|
||||
git checkout -q -f $branch_name
|
||||
pip install -U frappe-bench
|
||||
function update_to_version() {
|
||||
version=$1
|
||||
branch_name="version-$version-hotfix"
|
||||
echo "Updating to v$version"
|
||||
git fetch --depth 1 upstream $branch_name:$branch_name
|
||||
git checkout -q -f $branch_name
|
||||
pip install -U frappe-bench
|
||||
|
||||
rm -rf ~/frappe-bench/env
|
||||
bench -v setup env
|
||||
bench --site test_site migrate
|
||||
done
|
||||
rm -rf ~/frappe-bench/env
|
||||
bench -v setup env
|
||||
bench --site test_site migrate
|
||||
}
|
||||
|
||||
pyenv global $(pyenv versions | grep '3.7')
|
||||
update_to_version 12
|
||||
update_to_version 13
|
||||
|
||||
pyenv global $(pyenv versions | grep '3.10')
|
||||
update_to_version 14
|
||||
|
||||
echo "Updating to last commit"
|
||||
git checkout -q -f "$GITHUB_SHA"
|
||||
pyenv global $(pyenv versions | grep '3.10')
|
||||
rm -rf ~/frappe-bench/env
|
||||
bench -v setup env
|
||||
bench --site test_site migrate
|
||||
|
|
|
|||
5
.github/workflows/publish-assets-develop.yml
vendored
5
.github/workflows/publish-assets-develop.yml
vendored
|
|
@ -1,6 +1,7 @@
|
|||
name: 'Frappe Assets'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
|
||||
|
|
@ -15,10 +16,10 @@ jobs:
|
|||
path: 'frappe'
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 14
|
||||
node-version: 16
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.11'
|
||||
- name: Set up bench and build assets
|
||||
run: |
|
||||
npm install -g yarn
|
||||
|
|
|
|||
38
.github/workflows/release_notes.yml
vendored
Normal file
38
.github/workflows/release_notes.yml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# This action:
|
||||
#
|
||||
# 1. Generates release notes using github API.
|
||||
# 2. Strips unnecessary info like chore/style etc from notes.
|
||||
# 3. Updates release info.
|
||||
|
||||
# This action needs to be maintained on all branches that do releases.
|
||||
|
||||
name: 'Release Notes'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag_name:
|
||||
description: 'Tag of release like v13.0.0'
|
||||
required: true
|
||||
type: string
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
regen-notes:
|
||||
name: 'Regenerate release notes'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Update notes
|
||||
run: |
|
||||
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' )
|
||||
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/tags/$RELEASE_TAG | jq -r '.id')
|
||||
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/$RELEASE_ID -f body=$NEW_NOTES
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}
|
||||
135
.github/workflows/server-mariadb-tests.yml
vendored
135
.github/workflows/server-mariadb-tests.yml
vendored
|
|
@ -1,135 +0,0 @@
|
|||
name: Server
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
|
||||
concurrency:
|
||||
group: server-mariadb-develop-${{ github.event.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
container: [1, 2]
|
||||
|
||||
name: Python Unit Tests (MariaDB)
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:10.6
|
||||
env:
|
||||
MARIADB_ROOT_PASSWORD: travis
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Check if build should be run
|
||||
id: check-build
|
||||
run: |
|
||||
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
|
||||
env:
|
||||
TYPE: "server"
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
with:
|
||||
node-version: 14
|
||||
check-latest: true
|
||||
|
||||
- name: Add to Hosts
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: |
|
||||
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
echo "127.0.0.1 test_site_producer" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Cache pip
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v3
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install Dependencies
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
|
||||
env:
|
||||
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
|
||||
AFTER: ${{ env.GITHUB_EVENT_PATH.after }}
|
||||
TYPE: server
|
||||
|
||||
- name: Install
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||
env:
|
||||
DB: mariadb
|
||||
TYPE: server
|
||||
|
||||
- name: Run Tests
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage
|
||||
env:
|
||||
CI_BUILD_ID: ${{ github.run_id }}
|
||||
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
|
||||
|
||||
- name: Upload coverage data
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: MariaDB
|
||||
fail_ci_if_error: true
|
||||
files: /home/runner/frappe-bench/sites/coverage.xml
|
||||
verbose: true
|
||||
flags: server
|
||||
|
|
@ -7,25 +7,58 @@ on:
|
|||
branches: [ develop ]
|
||||
|
||||
concurrency:
|
||||
group: server-postgres-develop-${{ github.event.number }}
|
||||
group: server-develop-${{ github.event_name }}-${{ github.event.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
|
||||
permissions:
|
||||
# Do not change this as GITHUB_TOKEN is being used by roulette
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
checkrun:
|
||||
name: Build Check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
build: ${{ steps.check-build.outputs.build }}
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check if build should be run
|
||||
id: check-build
|
||||
run: |
|
||||
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
|
||||
env:
|
||||
TYPE: "server"
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
test:
|
||||
name: Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
needs: checkrun
|
||||
if: ${{ needs.checkrun.outputs.build == 'strawberry' }}
|
||||
timeout-minutes: 60
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
db: ["mariadb", "postgres"]
|
||||
container: [1, 2]
|
||||
|
||||
name: Python Unit Tests (Postgres)
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:10.6
|
||||
env:
|
||||
MARIADB_ROOT_PASSWORD: travis
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
|
||||
postgres:
|
||||
image: postgres:12.4
|
||||
env:
|
||||
|
|
@ -45,31 +78,26 @@ jobs:
|
|||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Check if build should be run
|
||||
id: check-build
|
||||
- name: Check for valid Python & Merge Conflicts
|
||||
run: |
|
||||
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
|
||||
env:
|
||||
TYPE: "server"
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
python -m compileall -q -f "${GITHUB_WORKSPACE}"
|
||||
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
|
||||
then echo "Found merge conflicts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
with:
|
||||
node-version: '14'
|
||||
node-version: 16
|
||||
check-latest: true
|
||||
|
||||
- name: Add to Hosts
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: |
|
||||
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
echo "127.0.0.1 test_site_producer" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Cache pip
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
|
|
@ -78,26 +106,11 @@ jobs:
|
|||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
|
|
@ -106,33 +119,45 @@ jobs:
|
|||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install Dependencies
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
|
||||
run: |
|
||||
bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
|
||||
bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||
env:
|
||||
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
|
||||
AFTER: ${{ env.GITHUB_EVENT_PATH.after }}
|
||||
TYPE: server
|
||||
|
||||
- name: Install
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||
env:
|
||||
DB: postgres
|
||||
TYPE: server
|
||||
DB: ${{ matrix.db }}
|
||||
|
||||
- name: Run Tests
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage
|
||||
run: cd ~/frappe-bench/sites && ../env/bin/python3 ../apps/frappe/.github/helper/ci.py
|
||||
env:
|
||||
SITE: test_site
|
||||
CI_BUILD_ID: ${{ github.run_id }}
|
||||
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
|
||||
BUILD_NUMBER: ${{ matrix.container }}
|
||||
TOTAL_BUILDS: 2
|
||||
|
||||
- name: Upload coverage data
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: coverage-${{ matrix.db }}-${{ matrix.container }}
|
||||
path: /home/runner/frappe-bench/sites/coverage.xml
|
||||
|
||||
coverage:
|
||||
name: Coverage Wrap Up
|
||||
needs: [test, checkrun]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.checkrun.outputs.build == 'strawberry' }}
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- name: Upload coverage data
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: Postgres
|
||||
name: Server
|
||||
fail_ci_if_error: true
|
||||
files: /home/runner/frappe-bench/sites/coverage.xml
|
||||
verbose: true
|
||||
flags: server
|
||||
165
.github/workflows/ui-tests.yml
vendored
165
.github/workflows/ui-tests.yml
vendored
|
|
@ -7,21 +7,46 @@ on:
|
|||
branches: [ develop ]
|
||||
|
||||
concurrency:
|
||||
group: ui-develop-${{ github.event.number }}
|
||||
group: ui-develop-${{ github.event_name }}-${{ github.event.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
# Do not change this as GITHUB_TOKEN is being used by roulette
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
checkrun:
|
||||
name: Build Check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
build: ${{ steps.check-build.outputs.build }}
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check if build should be run
|
||||
id: check-build
|
||||
run: |
|
||||
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
|
||||
env:
|
||||
TYPE: "ui"
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: checkrun
|
||||
if: ${{ needs.checkrun.outputs.build == 'strawberry' && github.repository_owner == 'frappe' }}
|
||||
timeout-minutes: 60
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
containers: [1, 2, 3]
|
||||
# Make sure you modify coverage submission file list if changing this
|
||||
container: [1, 2, 3]
|
||||
|
||||
name: UI Tests (Cypress)
|
||||
|
||||
|
|
@ -41,31 +66,26 @@ jobs:
|
|||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Check if build should be run
|
||||
id: check-build
|
||||
- name: Check for valid Python & Merge Conflicts
|
||||
run: |
|
||||
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
|
||||
env:
|
||||
TYPE: "ui"
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
python -m compileall -q -f "${GITHUB_WORKSPACE}"
|
||||
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
|
||||
then echo "Found merge conflicts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
with:
|
||||
node-version: 14
|
||||
node-version: 16
|
||||
check-latest: true
|
||||
|
||||
- name: Add to Hosts
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: |
|
||||
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
echo "127.0.0.1 test_site_producer" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Cache pip
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
|
|
@ -74,104 +94,105 @@ jobs:
|
|||
${{ runner.os }}-pip-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache node modules
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
key: ${{ runner.os }}-yarn-ui-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
${{ runner.os }}-yarn-ui-
|
||||
|
||||
- name: Cache cypress binary
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache
|
||||
key: ${{ runner.os }}-cypress-
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cypress-
|
||||
${{ runner.os }}-
|
||||
path: ~/.cache/Cypress
|
||||
key: ${{ runner.os }}-cypress
|
||||
|
||||
- name: Install Dependencies
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
|
||||
run: |
|
||||
bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
|
||||
bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||
env:
|
||||
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
|
||||
AFTER: ${{ env.GITHUB_EVENT_PATH.after }}
|
||||
TYPE: ui
|
||||
|
||||
- name: Install
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||
env:
|
||||
DB: mariadb
|
||||
TYPE: ui
|
||||
|
||||
- name: Verify yarn.lock
|
||||
run: |
|
||||
cd ~/frappe-bench/apps/frappe
|
||||
git diff --exit-code yarn.lock
|
||||
|
||||
- name: Instrument Source Code
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: cd ~/frappe-bench/apps/frappe/ && npx nyc instrument -x 'frappe/public/dist/**' -x 'frappe/public/js/lib/**' -x '**/*.bundle.js' --compact=false --in-place frappe
|
||||
|
||||
- name: Build
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: cd ~/frappe-bench/ && bench build --apps frappe
|
||||
|
||||
- name: Site Setup
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: cd ~/frappe-bench/ && bench --site test_site execute frappe.utils.install.complete_setup_wizard
|
||||
run: |
|
||||
cd ~/frappe-bench/
|
||||
bench --site test_site execute frappe.utils.install.complete_setup_wizard
|
||||
bench --site test_site execute frappe.tests.ui_test_helpers.create_test_user
|
||||
|
||||
- name: UI Tests
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
|
||||
run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests frappe --with-coverage --headless --parallel --ci-build-id $GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb
|
||||
|
||||
- name: Stop server
|
||||
if: ${{ steps.check-build.outputs.build-server == 'strawberry' }}
|
||||
- name: Stop server and wait for coverage file
|
||||
run: |
|
||||
ps -ef | grep "frappe serve" | awk '{print $2}' | xargs kill -s SIGINT 2> /dev/null || true
|
||||
ps -ef | grep "[f]rappe serve" | awk '{print $2}' | xargs kill -s SIGINT
|
||||
sleep 5
|
||||
( tail -f /home/runner/frappe-bench/sites/coverage.xml & ) | grep -q "\/coverage"
|
||||
|
||||
- name: Check If Coverage Report Exists
|
||||
id: check_coverage
|
||||
uses: andstor/file-existence-action@v1
|
||||
- name: Upload JS coverage data
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
files: "/home/runner/frappe-bench/apps/frappe/.cypress-coverage/clover.xml"
|
||||
name: coverage-js-${{ matrix.container }}
|
||||
path: /home/runner/frappe-bench/apps/frappe/.cypress-coverage/clover.xml
|
||||
|
||||
- name: Upload Coverage Data
|
||||
if: ${{ steps.check-build.outputs.build == 'strawberry' && steps.check_coverage.outputs.files_exists == 'true' }}
|
||||
- name: Upload python coverage data
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: coverage-py-${{ matrix.container }}
|
||||
path: /home/runner/frappe-bench/sites/coverage.xml
|
||||
|
||||
- name: Show bench output
|
||||
if: ${{ always() }}
|
||||
run: cat ~/frappe-bench/bench_start.log || true
|
||||
|
||||
|
||||
coverage:
|
||||
name: Coverage Wrap Up
|
||||
needs: [test, checkrun]
|
||||
if: ${{ needs.checkrun.outputs.build == 'strawberry' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- name: Upload python coverage data
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: UIBackend
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
files: ./coverage-py-1/coverage.xml,./coverage-py-2/coverage.xml,./coverage-py-3/coverage.xml
|
||||
flags: server-ui
|
||||
|
||||
- name: Upload JS coverage data
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: Cypress
|
||||
fail_ci_if_error: true
|
||||
directory: /home/runner/frappe-bench/apps/frappe/.cypress-coverage/
|
||||
files: ./coverage-js-1/clover.xml,./coverage-js-2/clover.xml,./coverage-js-3/clover.xml
|
||||
verbose: true
|
||||
flags: ui-tests
|
||||
|
||||
- name: Upload Server Coverage Data
|
||||
if: ${{ steps.check-build.outputs.build-server == 'strawberry' }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: MariaDB
|
||||
fail_ci_if_error: true
|
||||
files: /home/runner/frappe-bench/sites/coverage.xml
|
||||
verbose: true
|
||||
flags: server
|
||||
|
|
|
|||
37
.mergify.yml
37
.mergify.yml
|
|
@ -4,11 +4,13 @@ pull_request_rules:
|
|||
- and:
|
||||
- and:
|
||||
- author!=surajshetty3416
|
||||
- author!=gavindsouza
|
||||
- author!=deepeshgarg007
|
||||
- author!=ankush
|
||||
- author!=frappe-pr-bot
|
||||
- author!=mergify[bot]
|
||||
- or:
|
||||
- base=version-15
|
||||
- base=version-14
|
||||
- base=version-13
|
||||
- base=version-12
|
||||
actions:
|
||||
|
|
@ -20,15 +22,6 @@ pull_request_rules:
|
|||
|
||||
- name: Automatic merge on CI success and review
|
||||
conditions:
|
||||
- status-success=Sider
|
||||
- status-success=Python Unit Tests (MariaDB) (1)
|
||||
- status-success=Python Unit Tests (MariaDB) (2)
|
||||
- status-success=Python Unit Tests (Postgres) (1)
|
||||
- status-success=Python Unit Tests (Postgres) (2)
|
||||
- status-success=UI Tests (Cypress) (1)
|
||||
- status-success=UI Tests (Cypress) (2)
|
||||
- status-success=UI Tests (Cypress) (3)
|
||||
- status-success=security/snyk (frappe)
|
||||
- label!=dont-merge
|
||||
- label!=squash
|
||||
- "#approved-reviews-by>=1"
|
||||
|
|
@ -37,15 +30,6 @@ pull_request_rules:
|
|||
method: merge
|
||||
- name: Automatic squash on CI success and review
|
||||
conditions:
|
||||
- status-success=Sider
|
||||
- status-success=Python Unit Tests (MariaDB) (1)
|
||||
- status-success=Python Unit Tests (MariaDB) (2)
|
||||
- status-success=Python Unit Tests (Postgres) (1)
|
||||
- status-success=Python Unit Tests (Postgres) (2)
|
||||
- status-success=UI Tests (Cypress) (1)
|
||||
- status-success=UI Tests (Cypress) (2)
|
||||
- status-success=UI Tests (Cypress) (3)
|
||||
- status-success=security/snyk (frappe)
|
||||
- label!=dont-merge
|
||||
- label=squash
|
||||
- "#approved-reviews-by>=1"
|
||||
|
|
@ -77,22 +61,13 @@ pull_request_rules:
|
|||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: backport to version-13-pre-release
|
||||
- name: backport to version-14-hotfix
|
||||
conditions:
|
||||
- label="backport version-13-pre-release"
|
||||
- label="backport version-14-hotfix"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-13-pre-release
|
||||
- version-14-hotfix
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
||||
- name: backport to version-12-hotfix
|
||||
conditions:
|
||||
- label="backport version-12-hotfix"
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- version-12-hotfix
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
|
|
|
|||
|
|
@ -26,23 +26,38 @@ repos:
|
|||
- id: pyupgrade
|
||||
args: ['--py310-plus']
|
||||
|
||||
- repo: https://github.com/adityahase/black
|
||||
rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
|
||||
- repo: https://github.com/frappe/black
|
||||
rev: 951ccf4d5bb0d692b457a5ebc4215d755618eb68
|
||||
hooks:
|
||||
- id: black
|
||||
additional_dependencies: ['click==8.0.4']
|
||||
|
||||
- repo: https://github.com/timothycrosley/isort
|
||||
rev: 5.9.1
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v2.7.1
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or: [javascript]
|
||||
# Ignore any files that might contain jinja / bundles
|
||||
exclude: |
|
||||
(?x)^(
|
||||
frappe/public/dist/.*|
|
||||
.*node_modules.*|
|
||||
.*boilerplate.*|
|
||||
frappe/www/website_script.js|
|
||||
frappe/templates/includes/.*|
|
||||
frappe/public/js/lib/.*
|
||||
)$
|
||||
|
||||
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.9.2
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 5.0.4
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies: ['flake8-bugbear',]
|
||||
args: ['--config', '.github/helper/flake8.conf']
|
||||
|
||||
ci:
|
||||
autoupdate_schedule: weekly
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@
|
|||
[
|
||||
"@semantic-release/git", {
|
||||
"assets": ["frappe/__init__.py"],
|
||||
"message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}"
|
||||
"message": "chore(release): Bumped to Version ${nextRelease.version}"
|
||||
}
|
||||
],
|
||||
"@semantic-release/github"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
101
.snyk
101
.snyk
|
|
@ -1,101 +0,0 @@
|
|||
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
||||
version: v1.19.0
|
||||
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
|
||||
ignore:
|
||||
SNYK-JS-AWESOMPLETE-174474:
|
||||
- awesomplete:
|
||||
reason: No patch available
|
||||
expires: '2019-06-11T14:12:04.995Z'
|
||||
'npm:mem:20180117':
|
||||
- showdown > yargs > os-locale > mem:
|
||||
reason: No patch available
|
||||
expires: '2019-06-11T14:12:04.995Z'
|
||||
SNYK-PYTHON-PYYAML-550022:
|
||||
- '*':
|
||||
reason: Project is not directly dependant on the package
|
||||
expires: 2021-04-01T18:02:21.256Z
|
||||
# patches apply the minimum changes required to fix a vulnerability
|
||||
patch:
|
||||
'npm:extend:20180424':
|
||||
- superagent > extend:
|
||||
patched: '2019-05-09T10:14:19.246Z'
|
||||
SNYK-JS-LODASH-450202:
|
||||
- frappe-datatable > lodash:
|
||||
patched: '2020-01-31T01:33:09.889Z'
|
||||
SNYK-JS-LODASH-567746:
|
||||
- frappe-datatable > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- quagga > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- tailwindcss > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- '@tailwindcss/ui > @tailwindcss/custom-forms > lodash':
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > @snyk/dep-graph > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > inquirer > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > snyk-config > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > snyk-mvn-plugin > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > snyk-nodejs-lockfile-parser > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > snyk-nuget-plugin > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > snyk-go-plugin > graphlib > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > snyk-nodejs-lockfile-parser > graphlib > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > snyk-nuget-plugin > dotnet-deps-parser > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > snyk-php-plugin > @snyk/composer-lockfile-parser > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/ruby-semver > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-04-30T23:02:32.330Z'
|
||||
- quill-image-resize > lodash:
|
||||
patched: '2020-08-24T23:06:37.710Z'
|
||||
- node-sass > lodash:
|
||||
patched: '2020-09-15T23:06:41.931Z'
|
||||
- node-sass > sass-graph > lodash:
|
||||
patched: '2020-09-15T23:06:41.931Z'
|
||||
- node-sass > gaze > globule > lodash:
|
||||
patched: '2020-09-15T23:06:41.931Z'
|
||||
- snyk > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-cpp-plugin > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-go-plugin > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-gradle-plugin > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-docker-plugin > snyk-nodejs-lockfile-parser > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-mvn-plugin > @snyk/java-call-graph-builder > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-php-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-gradle-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-mvn-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > @snyk/dep-graph > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-nodejs-lockfile-parser > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
- snyk > snyk-go-plugin > graphlib > lodash:
|
||||
patched: '2020-09-16T23:06:38.881Z'
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"extends": ["stylelint-config-recommended"],
|
||||
"plugins": ["stylelint-scss"],
|
||||
"rules": {
|
||||
"at-rule-no-unknown": null,
|
||||
"scss/at-rule-no-unknown": true,
|
||||
"no-descending-specificity": null
|
||||
}
|
||||
}
|
||||
|
|
@ -6,13 +6,7 @@
|
|||
* @frappe/frappe-review-team
|
||||
templates/ @surajshetty3416
|
||||
www/ @surajshetty3416
|
||||
patches/ @surajshetty3416 @gavindsouza
|
||||
event_streaming/ @ruchamahabal
|
||||
patches/ @surajshetty3416
|
||||
data_import* @netchampfaris
|
||||
core/ @surajshetty3416
|
||||
database @gavindsouza
|
||||
model @gavindsouza
|
||||
pyproject.toml @gavindsouza
|
||||
query_builder/ @gavindsouza
|
||||
commands/ @gavindsouza
|
||||
workspace @shariquerik
|
||||
|
|
|
|||
23
README.md
23
README.md
|
|
@ -14,25 +14,28 @@
|
|||
</div>
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/frappe/frappe/actions/workflows/server-mariadb-tests.yml">
|
||||
<img src="https://github.com/frappe/frappe/actions/workflows/server-mariadb-tests.yml/badge.svg">
|
||||
<a target="_blank" href="#LICENSE" title="License: MIT">
|
||||
<img src="https://img.shields.io/badge/License-MIT-success.svg">
|
||||
</a>
|
||||
<a target="_blank" href="https://www.python.org/downloads/" title="Python version">
|
||||
<img src="https://img.shields.io/badge/python-%3E=_3.10-success.svg">
|
||||
</a>
|
||||
<a href="https://frappeframework.com/docs">
|
||||
<img src="https://img.shields.io/badge/docs-%F0%9F%93%96-success.svg"/>
|
||||
</a>
|
||||
<a href="https://github.com/frappe/frappe/actions/workflows/server-tests.yml">
|
||||
<img src="https://github.com/frappe/frappe/actions/workflows/server-tests.yml/badge.svg">
|
||||
</a>
|
||||
<a href="https://github.com/frappe/frappe/actions/workflows/ui-tests.yml">
|
||||
<img src="https://github.com/frappe/frappe/actions/workflows/ui-tests.yml/badge.svg?branch=develop">
|
||||
</a>
|
||||
<a href='https://frappeframework.com/docs'>
|
||||
<img src='https://img.shields.io/badge/docs-📖-7575FF.svg?style=flat-square'/>
|
||||
</a>
|
||||
<a href='https://www.codetriage.com/frappe/frappe'>
|
||||
<img src='https://www.codetriage.com/frappe/frappe/badges/users.svg'>
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/frappe/frappe">
|
||||
<img src="https://codecov.io/gh/frappe/frappe/branch/develop/graph/badge.svg?token=XoTa679hIj"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
Full-stack web application framework that uses Python and MariaDB on the server side and a tightly integrated client side library. Built for [ERPNext](https://erpnext.com)
|
||||
Full-stack web application framework that uses Python and MariaDB on the server side and a tightly integrated client side library. Built for [ERPNext](https://erpnext.com).
|
||||
|
||||
<div align="center" style="max-height: 40px;">
|
||||
<a href="https://frappecloud.com/frappe/signup">
|
||||
|
|
@ -72,3 +75,5 @@ Full-stack web application framework that uses Python and MariaDB on the server
|
|||
|
||||
## License
|
||||
This repository has been released under the [MIT License](LICENSE).
|
||||
|
||||
By contributing to Frappe, you agree that your contributions will be licensed under its MIT License.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Security Policy
|
||||
|
||||
The Frappe team and community take security issues in the Frappe Framework seriously. To report a security issue, fill out the form at [https://erpnext.com/security/report](https://erpnext.com/security/report).
|
||||
The Frappe team and community take security issues in the Frappe Framework seriously. To report a security issue, please go through the information mentioned [here](https://frappe.io/security).
|
||||
|
||||
You can help us make Frappe and consequently all Frappe dependent apps like [ERPNext](https://erpnext.com) more secure by following the [Reporting guidelines](https://erpnext.com/security).
|
||||
|
||||
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.
|
||||
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ The following 3rd-party software packages may be used by or distributed with <ht
|
|||
- Leaflet.Locate - (c) 2016 Dominik Moritz
|
||||
- Leaflet.draw - (c) 2012-2017, Jacob Toye, Jon West, Smartrak
|
||||
- Leaflet.EasyButton - MIT License, (C) 2014 Daniel Montague
|
||||
- Identicons - MIT License, (C) 2015, <https://github.com/evuez/identicons>
|
||||
|
||||
### Icon Fonts
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
skips: ['E0203', 'B605', 'B404', 'B603', 'B607']
|
||||
37
codecov.yml
37
codecov.yml
|
|
@ -2,17 +2,16 @@ codecov:
|
|||
require_ci_to_pass: yes
|
||||
|
||||
coverage:
|
||||
range: 60..90
|
||||
status:
|
||||
project:
|
||||
default: false
|
||||
server:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 0.5%
|
||||
flags:
|
||||
- server
|
||||
patch:
|
||||
default: false
|
||||
server:
|
||||
default:
|
||||
target: 85%
|
||||
threshold: 0%
|
||||
only_pulls: true
|
||||
|
|
@ -23,13 +22,39 @@ coverage:
|
|||
comment:
|
||||
layout: "diff, flags"
|
||||
require_changes: true
|
||||
show_critical_paths: true
|
||||
|
||||
flags:
|
||||
server:
|
||||
paths:
|
||||
- ".*\\.py"
|
||||
- "**/*.py"
|
||||
carryforward: true
|
||||
ui-tests:
|
||||
paths:
|
||||
- ".*\\.js"
|
||||
- "**/*.js"
|
||||
carryforward: true
|
||||
server-ui:
|
||||
paths:
|
||||
- "**/*.py"
|
||||
carryforward: true
|
||||
|
||||
profiling:
|
||||
critical_files_paths:
|
||||
- /frappe/api.py
|
||||
- /frappe/app.py
|
||||
- /frappe/auth.py
|
||||
- /frappe/boot.py
|
||||
- /frappe/client.py
|
||||
- /frappe/handler.py
|
||||
- /frappe/migrate.py
|
||||
- /frappe/sessions.py
|
||||
- /frappe/utils/*
|
||||
- /frappe/desk/reportview.py
|
||||
- /frappe/desk/form/*
|
||||
- /frappe/model/*
|
||||
- /frappe/core/doctype/doctype/*
|
||||
- /frappe/core/doctype/data_import/*
|
||||
- /frappe/core/doctype/user/*
|
||||
- /frappe/core/doctype/user/*
|
||||
- /frappe/query_builder/*
|
||||
- /frappe/database/*
|
||||
|
|
|
|||
25
commitlint.config.js
Normal file
25
commitlint.config.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
module.exports = {
|
||||
parserPreset: "conventional-changelog-conventionalcommits",
|
||||
rules: {
|
||||
"subject-empty": [2, "never"],
|
||||
"type-case": [2, "always", "lower-case"],
|
||||
"type-empty": [2, "never"],
|
||||
"type-enum": [
|
||||
2,
|
||||
"always",
|
||||
[
|
||||
"build",
|
||||
"chore",
|
||||
"ci",
|
||||
"docs",
|
||||
"feat",
|
||||
"fix",
|
||||
"perf",
|
||||
"refactor",
|
||||
"revert",
|
||||
"style",
|
||||
"test",
|
||||
],
|
||||
],
|
||||
},
|
||||
};
|
||||
24
cypress.config.js
Normal file
24
cypress.config.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
const { defineConfig } = require("cypress");
|
||||
|
||||
module.exports = defineConfig({
|
||||
projectId: "92odwv",
|
||||
adminPassword: "admin",
|
||||
testUser: "frappe@example.com",
|
||||
defaultCommandTimeout: 20000,
|
||||
pageLoadTimeout: 15000,
|
||||
video: true,
|
||||
videoUploadOnPasses: false,
|
||||
retries: {
|
||||
runMode: 2,
|
||||
openMode: 2,
|
||||
},
|
||||
e2e: {
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
setupNodeEvents(on, config) {
|
||||
return require("./cypress/plugins/index.js")(on, config);
|
||||
},
|
||||
baseUrl: "http://test_site_ui:8000",
|
||||
specPattern: ["./cypress/integration/*.js", "**/ui_test_*.js"],
|
||||
},
|
||||
});
|
||||
15
cypress.json
15
cypress.json
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"baseUrl": "http://test_site_ui:8000",
|
||||
"projectId": "92odwv",
|
||||
"adminPassword": "admin",
|
||||
"defaultCommandTimeout": 20000,
|
||||
"pageLoadTimeout": 15000,
|
||||
"video": true,
|
||||
"videoUploadOnPasses": false,
|
||||
"retries": {
|
||||
"runMode": 2,
|
||||
"openMode": 2
|
||||
},
|
||||
"integrationFolder": ".",
|
||||
"testFiles": ["cypress/integration/*.js", "**/ui_test_*.js"]
|
||||
}
|
||||
|
|
@ -13,8 +13,8 @@ export default {
|
|||
fieldtype: "Data",
|
||||
in_list_view: 1,
|
||||
label: "Title",
|
||||
unique: 1
|
||||
}
|
||||
unique: 1,
|
||||
},
|
||||
],
|
||||
links: [],
|
||||
istable: 1,
|
||||
|
|
@ -24,7 +24,7 @@ export default {
|
|||
naming_rule: "By fieldname",
|
||||
owner: "Administrator",
|
||||
permissions: [],
|
||||
sort_field: 'modified',
|
||||
sort_order: 'ASC',
|
||||
track_changes: 1
|
||||
};
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,38 +12,38 @@ export default {
|
|||
fieldname: "data",
|
||||
fieldtype: "Data",
|
||||
in_list_view: 1,
|
||||
label: "Data"
|
||||
label: "Data",
|
||||
},
|
||||
{
|
||||
fieldname: "barcode",
|
||||
fieldtype: "Barcode",
|
||||
in_list_view: 1,
|
||||
label: "Barcode"
|
||||
label: "Barcode",
|
||||
},
|
||||
{
|
||||
fieldname: "check",
|
||||
fieldtype: "Check",
|
||||
in_list_view: 1,
|
||||
label: "Check"
|
||||
label: "Check",
|
||||
},
|
||||
{
|
||||
fieldname: "rating",
|
||||
fieldtype: "Rating",
|
||||
in_list_view: 1,
|
||||
label: "Rating"
|
||||
label: "Rating",
|
||||
},
|
||||
{
|
||||
fieldname: "duration",
|
||||
fieldtype: "Duration",
|
||||
in_list_view: 1,
|
||||
label: "Duration"
|
||||
label: "Duration",
|
||||
},
|
||||
{
|
||||
fieldname: "date",
|
||||
fieldtype: "Date",
|
||||
in_list_view: 1,
|
||||
label: "Date"
|
||||
}
|
||||
label: "Date",
|
||||
},
|
||||
],
|
||||
links: [],
|
||||
istable: 1,
|
||||
|
|
@ -53,7 +53,7 @@ export default {
|
|||
naming_rule: "By fieldname",
|
||||
owner: "Administrator",
|
||||
permissions: [],
|
||||
sort_field: 'modified',
|
||||
sort_order: 'ASC',
|
||||
track_changes: 1
|
||||
};
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,37 +1,37 @@
|
|||
export default {
|
||||
name: 'Custom Submittable DocType',
|
||||
name: "Custom Submittable DocType",
|
||||
custom: 1,
|
||||
actions: [],
|
||||
is_submittable: 1,
|
||||
creation: '2019-12-10 06:29:07.215072',
|
||||
doctype: 'DocType',
|
||||
creation: "2019-12-10 06:29:07.215072",
|
||||
doctype: "DocType",
|
||||
editable_grid: 1,
|
||||
engine: 'InnoDB',
|
||||
engine: "InnoDB",
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'enabled',
|
||||
fieldtype: 'Check',
|
||||
label: 'Enabled',
|
||||
fieldname: "enabled",
|
||||
fieldtype: "Check",
|
||||
label: "Enabled",
|
||||
allow_on_submit: 1,
|
||||
reqd: 1
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'title',
|
||||
fieldtype: 'Data',
|
||||
label: 'title',
|
||||
reqd: 1
|
||||
fieldname: "title",
|
||||
fieldtype: "Data",
|
||||
label: "title",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'description',
|
||||
fieldtype: 'Text Editor',
|
||||
label: 'Description'
|
||||
}
|
||||
fieldname: "description",
|
||||
fieldtype: "Text Editor",
|
||||
label: "Description",
|
||||
},
|
||||
],
|
||||
links: [],
|
||||
modified: '2019-12-10 14:40:53.127615',
|
||||
modified_by: 'Administrator',
|
||||
module: 'Custom',
|
||||
owner: 'Administrator',
|
||||
modified: "2019-12-10 14:40:53.127615",
|
||||
modified_by: "Administrator",
|
||||
module: "Custom",
|
||||
owner: "Administrator",
|
||||
permissions: [
|
||||
{
|
||||
create: 1,
|
||||
|
|
@ -39,15 +39,15 @@ export default {
|
|||
email: 1,
|
||||
print: 1,
|
||||
read: 1,
|
||||
role: 'System Manager',
|
||||
role: "System Manager",
|
||||
share: 1,
|
||||
write: 1,
|
||||
submit: 1,
|
||||
cancel: 1
|
||||
}
|
||||
cancel: 1,
|
||||
},
|
||||
],
|
||||
quick_entry: 1,
|
||||
sort_field: 'modified',
|
||||
sort_order: 'ASC',
|
||||
track_changes: 1
|
||||
};
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,51 +1,51 @@
|
|||
export default {
|
||||
name: 'Validation Test',
|
||||
name: "Validation Test",
|
||||
custom: 1,
|
||||
actions: [],
|
||||
creation: '2019-03-15 06:29:07.215072',
|
||||
doctype: 'DocType',
|
||||
creation: "2019-03-15 06:29:07.215072",
|
||||
doctype: "DocType",
|
||||
editable_grid: 1,
|
||||
engine: 'InnoDB',
|
||||
engine: "InnoDB",
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'email',
|
||||
fieldtype: 'Data',
|
||||
label: 'Email',
|
||||
options: 'Email'
|
||||
fieldname: "email",
|
||||
fieldtype: "Data",
|
||||
label: "Email",
|
||||
options: "Email",
|
||||
},
|
||||
{
|
||||
fieldname: 'URL',
|
||||
fieldtype: 'Data',
|
||||
label: 'URL',
|
||||
options: 'URL'
|
||||
fieldname: "URL",
|
||||
fieldtype: "Data",
|
||||
label: "URL",
|
||||
options: "URL",
|
||||
},
|
||||
{
|
||||
fieldname: 'Phone',
|
||||
fieldtype: 'Data',
|
||||
label: 'Phone',
|
||||
options: 'Phone'
|
||||
fieldname: "Phone",
|
||||
fieldtype: "Data",
|
||||
label: "Phone",
|
||||
options: "Phone",
|
||||
},
|
||||
{
|
||||
fieldname: 'person_name',
|
||||
fieldtype: 'Data',
|
||||
label: 'Person Name',
|
||||
options: 'Name'
|
||||
fieldname: "person_name",
|
||||
fieldtype: "Data",
|
||||
label: "Person Name",
|
||||
options: "Name",
|
||||
},
|
||||
{
|
||||
fieldname: 'read_only_url',
|
||||
fieldtype: 'Data',
|
||||
label: 'Read Only URL',
|
||||
options: 'URL',
|
||||
read_only: '1',
|
||||
default: 'https://frappe.io'
|
||||
}
|
||||
fieldname: "read_only_url",
|
||||
fieldtype: "Data",
|
||||
label: "Read Only URL",
|
||||
options: "URL",
|
||||
read_only: "1",
|
||||
default: "https://frappe.io",
|
||||
},
|
||||
],
|
||||
issingle: 1,
|
||||
links: [],
|
||||
modified: '2021-04-19 14:40:53.127615',
|
||||
modified_by: 'Administrator',
|
||||
module: 'Custom',
|
||||
owner: 'Administrator',
|
||||
modified: "2021-04-19 14:40:53.127615",
|
||||
modified_by: "Administrator",
|
||||
module: "Custom",
|
||||
owner: "Administrator",
|
||||
permissions: [
|
||||
{
|
||||
create: 1,
|
||||
|
|
@ -53,13 +53,13 @@ export default {
|
|||
email: 1,
|
||||
print: 1,
|
||||
read: 1,
|
||||
role: 'System Manager',
|
||||
role: "System Manager",
|
||||
share: 1,
|
||||
write: 1
|
||||
}
|
||||
write: 1,
|
||||
},
|
||||
],
|
||||
quick_entry: 1,
|
||||
sort_field: 'modified',
|
||||
sort_order: 'ASC',
|
||||
track_changes: 1
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
export default {
|
||||
name: 'DateTime Test',
|
||||
name: "DateTime Test",
|
||||
custom: 1,
|
||||
actions: [],
|
||||
creation: '2019-03-15 06:29:07.215072',
|
||||
doctype: 'DocType',
|
||||
creation: "2019-03-15 06:29:07.215072",
|
||||
doctype: "DocType",
|
||||
editable_grid: 1,
|
||||
engine: 'InnoDB',
|
||||
engine: "InnoDB",
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'date',
|
||||
fieldtype: 'Date',
|
||||
label: 'Date'
|
||||
fieldname: "date",
|
||||
fieldtype: "Date",
|
||||
label: "Date",
|
||||
},
|
||||
{
|
||||
fieldname: 'time',
|
||||
fieldtype: 'Time',
|
||||
label: 'Time'
|
||||
fieldname: "time",
|
||||
fieldtype: "Time",
|
||||
label: "Time",
|
||||
},
|
||||
{
|
||||
fieldname: 'datetime',
|
||||
fieldtype: 'Datetime',
|
||||
label: 'Datetime'
|
||||
}
|
||||
fieldname: "datetime",
|
||||
fieldtype: "Datetime",
|
||||
label: "Datetime",
|
||||
},
|
||||
],
|
||||
issingle: 1,
|
||||
links: [],
|
||||
modified: '2019-12-09 14:40:53.127615',
|
||||
modified_by: 'Administrator',
|
||||
module: 'Custom',
|
||||
owner: 'Administrator',
|
||||
modified: "2019-12-09 14:40:53.127615",
|
||||
modified_by: "Administrator",
|
||||
module: "Custom",
|
||||
owner: "Administrator",
|
||||
permissions: [
|
||||
{
|
||||
create: 1,
|
||||
|
|
@ -36,13 +36,13 @@ export default {
|
|||
email: 1,
|
||||
print: 1,
|
||||
read: 1,
|
||||
role: 'System Manager',
|
||||
role: "System Manager",
|
||||
share: 1,
|
||||
write: 1
|
||||
}
|
||||
write: 1,
|
||||
},
|
||||
],
|
||||
quick_entry: 1,
|
||||
sort_field: 'modified',
|
||||
sort_order: 'ASC',
|
||||
track_changes: 1
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,18 +10,18 @@ export default {
|
|||
engine: "InnoDB",
|
||||
fields: [
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"label": "Title",
|
||||
"unique": 1
|
||||
}
|
||||
fieldname: "title",
|
||||
fieldtype: "Data",
|
||||
label: "Title",
|
||||
unique: 1,
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
"group": "Child Doctype",
|
||||
"link_doctype": "Doctype With Child Table",
|
||||
"link_fieldname": "title"
|
||||
}
|
||||
group: "Child Doctype",
|
||||
link_doctype: "Doctype With Child Table",
|
||||
link_fieldname: "title",
|
||||
},
|
||||
],
|
||||
modified: "2022-02-10 12:03:12.603763",
|
||||
modified_by: "Administrator",
|
||||
|
|
@ -34,12 +34,12 @@ export default {
|
|||
email: 1,
|
||||
print: 1,
|
||||
read: 1,
|
||||
role: 'System Manager',
|
||||
role: "System Manager",
|
||||
share: 1,
|
||||
write: 1
|
||||
}
|
||||
write: 1,
|
||||
},
|
||||
],
|
||||
sort_field: 'modified',
|
||||
sort_order: 'ASC',
|
||||
track_changes: 1
|
||||
};
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,21 +12,21 @@ export default {
|
|||
fieldname: "title",
|
||||
fieldtype: "Data",
|
||||
label: "Title",
|
||||
unique: 1
|
||||
unique: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "child_table",
|
||||
fieldtype: "Table",
|
||||
label: "Child Table",
|
||||
options: "Child Table Doctype",
|
||||
reqd: 1
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "child_table_1",
|
||||
fieldtype: "Table",
|
||||
label: "Child Table 1",
|
||||
options: "Child Table Doctype 1"
|
||||
}
|
||||
options: "Child Table Doctype 1",
|
||||
},
|
||||
],
|
||||
links: [],
|
||||
modified: "2022-02-10 12:03:12.603763",
|
||||
|
|
@ -41,12 +41,12 @@ export default {
|
|||
email: 1,
|
||||
print: 1,
|
||||
read: 1,
|
||||
role: 'System Manager',
|
||||
role: "System Manager",
|
||||
share: 1,
|
||||
write: 1
|
||||
}
|
||||
write: 1,
|
||||
},
|
||||
],
|
||||
sort_field: 'modified',
|
||||
sort_order: 'ASC',
|
||||
track_changes: 1
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,29 +4,28 @@ export default {
|
|||
custom: 1,
|
||||
is_submittable: 1,
|
||||
autoname: "field:title",
|
||||
creation: '2022-03-30 06:29:07.215072',
|
||||
doctype: 'DocType',
|
||||
engine: 'InnoDB',
|
||||
creation: "2022-03-30 06:29:07.215072",
|
||||
doctype: "DocType",
|
||||
engine: "InnoDB",
|
||||
fields: [
|
||||
|
||||
{
|
||||
fieldname: 'title',
|
||||
fieldtype: 'Data',
|
||||
label: 'title',
|
||||
fieldname: "title",
|
||||
fieldtype: "Data",
|
||||
label: "title",
|
||||
unique: 1,
|
||||
},
|
||||
{
|
||||
fieldname: 'phone',
|
||||
fieldtype: 'Phone',
|
||||
label: 'Phone'
|
||||
}
|
||||
fieldname: "phone",
|
||||
fieldtype: "Phone",
|
||||
label: "Phone",
|
||||
},
|
||||
],
|
||||
links: [],
|
||||
modified: '2019-03-30 14:40:53.127615',
|
||||
modified_by: 'Administrator',
|
||||
modified: "2019-03-30 14:40:53.127615",
|
||||
modified_by: "Administrator",
|
||||
naming_rule: "By fieldname",
|
||||
module: 'Custom',
|
||||
owner: 'Administrator',
|
||||
module: "Custom",
|
||||
owner: "Administrator",
|
||||
permissions: [
|
||||
{
|
||||
create: 1,
|
||||
|
|
@ -34,14 +33,14 @@ export default {
|
|||
email: 1,
|
||||
print: 1,
|
||||
read: 1,
|
||||
role: 'System Manager',
|
||||
role: "System Manager",
|
||||
share: 1,
|
||||
write: 1,
|
||||
submit: 1,
|
||||
cancel: 1
|
||||
}
|
||||
cancel: 1,
|
||||
},
|
||||
],
|
||||
sort_field: 'modified',
|
||||
sort_order: 'ASC',
|
||||
track_changes: 1
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,39 +1,39 @@
|
|||
export default {
|
||||
name: 'Form With Tab Break',
|
||||
name: "Form With Tab Break",
|
||||
custom: 1,
|
||||
actions: [],
|
||||
doctype: 'DocType',
|
||||
engine: 'InnoDB',
|
||||
doctype: "DocType",
|
||||
engine: "InnoDB",
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'username',
|
||||
fieldtype: 'Data',
|
||||
label: 'Name',
|
||||
options: 'Name'
|
||||
fieldname: "username",
|
||||
fieldtype: "Data",
|
||||
label: "Name",
|
||||
options: "Name",
|
||||
},
|
||||
{
|
||||
fieldname: 'tab',
|
||||
fieldtype: 'Tab Break',
|
||||
label: 'Tab 2',
|
||||
fieldname: "tab",
|
||||
fieldtype: "Tab Break",
|
||||
label: "Tab 2",
|
||||
},
|
||||
{
|
||||
fieldname: 'Phone',
|
||||
fieldtype: 'Data',
|
||||
label: 'Phone',
|
||||
options: 'Phone',
|
||||
reqd: 1
|
||||
fieldname: "Phone",
|
||||
fieldtype: "Data",
|
||||
label: "Phone",
|
||||
options: "Phone",
|
||||
reqd: 1,
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
"group": "Profile",
|
||||
"link_doctype": "Contact",
|
||||
"link_fieldname": "user"
|
||||
group: "Profile",
|
||||
link_doctype: "Contact",
|
||||
link_fieldname: "user",
|
||||
},
|
||||
],
|
||||
modified_by: 'Administrator',
|
||||
module: 'Custom',
|
||||
owner: 'Administrator',
|
||||
modified_by: "Administrator",
|
||||
module: "Custom",
|
||||
owner: "Administrator",
|
||||
permissions: [
|
||||
{
|
||||
create: 1,
|
||||
|
|
@ -41,14 +41,14 @@ export default {
|
|||
email: 1,
|
||||
print: 1,
|
||||
read: 1,
|
||||
role: 'System Manager',
|
||||
role: "System Manager",
|
||||
share: 1,
|
||||
write: 1
|
||||
}
|
||||
write: 1,
|
||||
},
|
||||
],
|
||||
quick_entry: 1,
|
||||
autoname: "format: Test-{####}",
|
||||
sort_field: 'modified',
|
||||
sort_order: 'ASC',
|
||||
track_changes: 1
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
|
|||
65
cypress/fixtures/form_builder_doctype.js
Normal file
65
cypress/fixtures/form_builder_doctype.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
export default {
|
||||
name: "Form Builder Doctype",
|
||||
custom: 1,
|
||||
actions: [],
|
||||
doctype: "DocType",
|
||||
engine: "InnoDB",
|
||||
fields: [
|
||||
{
|
||||
fieldname: "data3",
|
||||
fieldtype: "Data",
|
||||
label: "Data 3",
|
||||
},
|
||||
{
|
||||
fieldname: "tab",
|
||||
fieldtype: "Tab Break",
|
||||
label: "Tab 2",
|
||||
},
|
||||
{
|
||||
fieldname: "data",
|
||||
fieldtype: "Data",
|
||||
label: "Data",
|
||||
},
|
||||
{
|
||||
fieldname: "check",
|
||||
fieldtype: "Check",
|
||||
label: "Check",
|
||||
},
|
||||
{
|
||||
fieldname: "column_1",
|
||||
fieldtype: "Column Break",
|
||||
},
|
||||
{
|
||||
fieldname: "data1",
|
||||
fieldtype: "Data",
|
||||
label: "Data 1",
|
||||
},
|
||||
{
|
||||
fieldname: "section_1",
|
||||
fieldtype: "Section Break",
|
||||
},
|
||||
{
|
||||
fieldname: "data2",
|
||||
fieldtype: "Data",
|
||||
label: "Data 2",
|
||||
},
|
||||
],
|
||||
modified_by: "Administrator",
|
||||
module: "Custom",
|
||||
owner: "Administrator",
|
||||
permissions: [
|
||||
{
|
||||
create: 1,
|
||||
delete: 1,
|
||||
email: 1,
|
||||
print: 1,
|
||||
read: 1,
|
||||
role: "System Manager",
|
||||
share: 1,
|
||||
write: 1,
|
||||
},
|
||||
],
|
||||
sort_field: "modified",
|
||||
sort_order: "ASC",
|
||||
track_changes: 1,
|
||||
};
|
||||
|
|
@ -1,42 +1,43 @@
|
|||
context('API Resources', () => {
|
||||
context("API Resources", () => {
|
||||
before(() => {
|
||||
cy.visit('/login');
|
||||
cy.visit("/login");
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
it('Creates two Comments', () => {
|
||||
cy.insert_doc('Comment', { comment_type: 'Comment', content: "hello" });
|
||||
cy.insert_doc('Comment', { comment_type: 'Comment', content: "world" });
|
||||
it("Creates two Comments", () => {
|
||||
cy.insert_doc("Comment", { comment_type: "Comment", content: "hello" });
|
||||
cy.insert_doc("Comment", { comment_type: "Comment", content: "world" });
|
||||
});
|
||||
|
||||
it('Lists the Comments', () => {
|
||||
cy.get_list('Comment')
|
||||
.its('data')
|
||||
.then(data => expect(data.length).to.be.at.least(2));
|
||||
it("Lists the Comments", () => {
|
||||
cy.get_list("Comment")
|
||||
.its("data")
|
||||
.then((data) => expect(data.length).to.be.at.least(2));
|
||||
|
||||
cy.get_list('Comment', ['name', 'content'], [['content', '=', 'hello']])
|
||||
.then(body => {
|
||||
expect(body).to.have.property('data');
|
||||
expect(body.data).to.have.lengthOf(1);
|
||||
expect(body.data[0]).to.have.property('content');
|
||||
expect(body.data[0]).to.have.property('name');
|
||||
});
|
||||
cy.get_list("Comment", ["name", "content"], [["content", "=", "hello"]]).then((body) => {
|
||||
expect(body).to.have.property("data");
|
||||
expect(body.data).to.have.lengthOf(1);
|
||||
expect(body.data[0]).to.have.property("content");
|
||||
expect(body.data[0]).to.have.property("name");
|
||||
});
|
||||
});
|
||||
|
||||
it('Gets each Comment', () => {
|
||||
cy.get_list('Comment').then(body => body.data.forEach(comment => {
|
||||
cy.get_doc('Comment', comment.name);
|
||||
}));
|
||||
it("Gets each Comment", () => {
|
||||
cy.get_list("Comment").then((body) =>
|
||||
body.data.forEach((comment) => {
|
||||
cy.get_doc("Comment", comment.name);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('Removes the Comments', () => {
|
||||
cy.get_list('Comment').then(body => {
|
||||
it("Removes the Comments", () => {
|
||||
cy.get_list("Comment").then((body) => {
|
||||
let comment_names = [];
|
||||
body.data.map(comment => comment_names.push(comment.name));
|
||||
body.data.map((comment) => comment_names.push(comment.name));
|
||||
comment_names = [...new Set(comment_names)]; // remove duplicates
|
||||
comment_names.forEach((comment_name) => {
|
||||
cy.remove_doc('Comment', comment_name);
|
||||
cy.remove_doc("Comment", comment_name);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
16
cypress/integration/assignment_rule.js
Normal file
16
cypress/integration/assignment_rule.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
context("Assignment Rule", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it("Custom grid buttons work", () => {
|
||||
cy.new_form("Assignment Rule");
|
||||
cy.findByRole("button", { name: "All Days" }).should("be.visible").click();
|
||||
cy.wait(2000);
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
expect(frm.doc.assignment_days.length).to.equal(7);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,48 +1,57 @@
|
|||
context('Awesome Bar', () => {
|
||||
context("Awesome Bar", () => {
|
||||
before(() => {
|
||||
cy.visit('/login');
|
||||
cy.visit("/login");
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.get('.navbar .navbar-home').click();
|
||||
cy.findByPlaceholderText('Search or type a command (Ctrl + G)').clear();
|
||||
cy.get(".navbar .navbar-home").click();
|
||||
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").clear();
|
||||
});
|
||||
|
||||
it('navigates to doctype list', () => {
|
||||
cy.findByPlaceholderText('Search or type a command (Ctrl + G)').type('todo', { delay: 700 });
|
||||
cy.get('.awesomplete').findByRole('listbox').should('be.visible');
|
||||
cy.findByPlaceholderText('Search or type a command (Ctrl + G)').type('{enter}', { delay: 700 });
|
||||
it("navigates to doctype list", () => {
|
||||
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type("todo", {
|
||||
delay: 700,
|
||||
});
|
||||
cy.get(".awesomplete").findByRole("listbox").should("be.visible");
|
||||
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type("{enter}", {
|
||||
delay: 700,
|
||||
});
|
||||
|
||||
cy.get('.title-text').should('contain', 'To Do');
|
||||
cy.get(".title-text").should("contain", "To Do");
|
||||
|
||||
cy.location('pathname').should('eq', '/app/todo');
|
||||
cy.location("pathname").should("eq", "/app/todo");
|
||||
});
|
||||
|
||||
it('find text in doctype list', () => {
|
||||
cy.findByPlaceholderText('Search or type a command (Ctrl + G)')
|
||||
.type('test in todo{enter}', { delay: 700 });
|
||||
it("find text in doctype list", () => {
|
||||
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type(
|
||||
"test in todo{enter}",
|
||||
{ delay: 700 }
|
||||
);
|
||||
|
||||
cy.get('.title-text').should('contain', 'To Do');
|
||||
cy.get(".title-text").should("contain", "To Do");
|
||||
|
||||
cy.findByPlaceholderText('ID')
|
||||
.should('have.value', '%test%');
|
||||
cy.findByPlaceholderText("ID").should("have.value", "%test%");
|
||||
cy.clear_filters();
|
||||
});
|
||||
|
||||
it('navigates to new form', () => {
|
||||
cy.findByPlaceholderText('Search or type a command (Ctrl + G)')
|
||||
.type('new blog post{enter}', { delay: 700 });
|
||||
it("navigates to new form", () => {
|
||||
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type(
|
||||
"new blog post{enter}",
|
||||
{ delay: 700 }
|
||||
);
|
||||
|
||||
cy.get('.title-text:visible').should('have.text', 'New Blog Post');
|
||||
cy.get(".title-text:visible").should("have.text", "New Blog Post");
|
||||
});
|
||||
|
||||
it('calculates math expressions', () => {
|
||||
cy.findByPlaceholderText('Search or type a command (Ctrl + G)')
|
||||
.type('55 + 32{downarrow}{enter}', { delay: 700 });
|
||||
it("calculates math expressions", () => {
|
||||
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type(
|
||||
"55 + 32{downarrow}{enter}",
|
||||
{ delay: 700 }
|
||||
);
|
||||
|
||||
cy.get('.modal-title').should('contain', 'Result');
|
||||
cy.get('.msgprint').should('contain', '55 + 32 = 87');
|
||||
cy.get(".modal-title").should("contain", "Result");
|
||||
cy.get(".msgprint").should("contain", "55 + 32 = 87");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,90 +1,95 @@
|
|||
context('Attach Control', () => {
|
||||
context("Attach Control", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/doctype');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.xcall('frappe.tests.ui_test_helpers.create_doctype', {
|
||||
name: 'Test Attach Control',
|
||||
fields: [
|
||||
{
|
||||
"label": "Attach File or Image",
|
||||
"fieldname": "attach",
|
||||
"fieldtype": "Attach",
|
||||
"in_list_view": 1,
|
||||
},
|
||||
]
|
||||
cy.visit("/app/doctype");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.xcall("frappe.tests.ui_test_helpers.create_doctype", {
|
||||
name: "Test Attach Control",
|
||||
fields: [
|
||||
{
|
||||
label: "Attach File or Image",
|
||||
fieldname: "attach",
|
||||
fieldtype: "Attach",
|
||||
in_list_view: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('Checking functionality for "Link" button in the "Attach" fieldtype', () => {
|
||||
//Navigating to the new form for the newly created doctype
|
||||
cy.new_form('Test Attach Control');
|
||||
cy.new_form("Test Attach Control");
|
||||
|
||||
//Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype
|
||||
cy.findByRole('button', {name: 'Attach'}).click();
|
||||
cy.findByRole("button", { name: "Attach" }).click();
|
||||
|
||||
//Clicking on "Link" button to attach a file using the "Link" button
|
||||
cy.findByRole('button', {name: 'Link'}).click();
|
||||
cy.findByPlaceholderText('Attach a web link').type('https://wallpaperplay.com/walls/full/8/2/b/72402.jpg');
|
||||
|
||||
cy.findByRole("button", { name: "Link" }).click();
|
||||
cy.findByPlaceholderText("Attach a web link").type(
|
||||
"https://wallpaperplay.com/walls/full/8/2/b/72402.jpg"
|
||||
);
|
||||
|
||||
//Clicking on the Upload button to upload the file
|
||||
cy.intercept("POST", "/api/method/upload_file").as("upload_image");
|
||||
cy.get('.modal-footer').findByRole("button", {name: "Upload"}).click({delay: 500});
|
||||
cy.get(".modal-footer").findByRole("button", { name: "Upload" }).click({ delay: 500 });
|
||||
cy.wait("@upload_image");
|
||||
cy.findByRole('button', {name: 'Save'}).click();
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
|
||||
//Checking if the URL of the attached image is getting displayed in the field of the newly created doctype
|
||||
cy.get('.attached-file > .ellipsis > .attached-file-link')
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', 'https://wallpaperplay.com/walls/full/8/2/b/72402.jpg');
|
||||
cy.get(".attached-file > .ellipsis > .attached-file-link")
|
||||
.should("have.attr", "href")
|
||||
.and("equal", "https://wallpaperplay.com/walls/full/8/2/b/72402.jpg");
|
||||
|
||||
//Clicking on the "Clear" button
|
||||
cy.get('[data-action="clear_attachment"]').click();
|
||||
|
||||
//Checking if clicking on the clear button clears the field of the doctype form and again displays the attach button
|
||||
cy.get('.control-input > .btn-sm').should('contain', 'Attach');
|
||||
cy.get(".control-input > .btn-sm").should("contain", "Attach");
|
||||
|
||||
//Deleting the doc
|
||||
cy.go_to_list('Test Attach Control');
|
||||
cy.get('.list-row-checkbox').eq(0).click();
|
||||
cy.get('.actions-btn-group > .btn').contains('Actions').click();
|
||||
cy.go_to_list("Test Attach Control");
|
||||
cy.get(".list-row-checkbox").eq(0).click();
|
||||
cy.get(".actions-btn-group > .btn").contains("Actions").click();
|
||||
cy.get('.actions-btn-group > .dropdown-menu [data-label="Delete"]').click();
|
||||
cy.click_modal_primary_button('Yes');
|
||||
cy.click_modal_primary_button("Yes");
|
||||
});
|
||||
|
||||
it('Checking functionality for "Library" button in the "Attach" fieldtype', () => {
|
||||
//Navigating to the new form for the newly created doctype
|
||||
cy.new_form('Test Attach Control');
|
||||
cy.new_form("Test Attach Control");
|
||||
|
||||
//Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype
|
||||
cy.findByRole('button', {name: 'Attach'}).click();
|
||||
cy.findByRole("button", { name: "Attach" }).click();
|
||||
|
||||
//Clicking on "Library" button to attach a file using the "Library" button
|
||||
cy.findByRole('button', {name: 'Library'}).click();
|
||||
cy.contains('72402.jpg').click();
|
||||
cy.findByRole("button", { name: "Library" }).click();
|
||||
cy.contains("72402.jpg").click();
|
||||
|
||||
//Clicking on the Upload button to upload the file
|
||||
cy.intercept("POST", "/api/method/upload_file").as("upload_image");
|
||||
cy.get('.modal-footer').findByRole("button", {name: "Upload"}).click({delay: 500});
|
||||
cy.get(".modal-footer").findByRole("button", { name: "Upload" }).click({ delay: 500 });
|
||||
cy.wait("@upload_image");
|
||||
cy.findByRole('button', {name: 'Save'}).click();
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
|
||||
//Checking if the URL of the attached image is getting displayed in the field of the newly created doctype
|
||||
cy.get('.attached-file > .ellipsis > .attached-file-link')
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', 'https://wallpaperplay.com/walls/full/8/2/b/72402.jpg');
|
||||
cy.get(".attached-file > .ellipsis > .attached-file-link")
|
||||
.should("have.attr", "href")
|
||||
.and("equal", "https://wallpaperplay.com/walls/full/8/2/b/72402.jpg");
|
||||
|
||||
//Clicking on the "Clear" button
|
||||
cy.get('[data-action="clear_attachment"]').click();
|
||||
|
||||
//Checking if clicking on the clear button clears the field of the doctype form and again displays the attach button
|
||||
cy.get('.control-input > .btn-sm').should('contain', 'Attach');
|
||||
cy.get(".control-input > .btn-sm").should("contain", "Attach");
|
||||
|
||||
//Deleting the doc
|
||||
cy.go_to_list('Test Attach Control');
|
||||
cy.get('.list-row-checkbox').eq(0).click();
|
||||
cy.get('.actions-btn-group > .btn').contains('Actions').click();
|
||||
cy.go_to_list("Test Attach Control");
|
||||
cy.get(".list-row-checkbox").eq(0).click();
|
||||
cy.get(".actions-btn-group > .btn").contains("Actions").click();
|
||||
cy.get('.actions-btn-group > .dropdown-menu [data-label="Delete"]').click();
|
||||
cy.click_modal_primary_button('Yes');
|
||||
cy.click_modal_primary_button("Yes");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,57 +1,64 @@
|
|||
context('Control Autocomplete', () => {
|
||||
context("Control Autocomplete", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_autocomplete(options) {
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
return cy.dialog({
|
||||
title: 'Autocomplete',
|
||||
title: "Autocomplete",
|
||||
fields: [
|
||||
{
|
||||
'label': 'Select an option',
|
||||
'fieldname': 'autocomplete',
|
||||
'fieldtype': 'Autocomplete',
|
||||
'options': options || ['Option 1', 'Option 2', 'Option 3'],
|
||||
}
|
||||
]
|
||||
label: "Select an option",
|
||||
fieldname: "autocomplete",
|
||||
fieldtype: "Autocomplete",
|
||||
options: options || ["Option 1", "Option 2", "Option 3"],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('should set the valid value', () => {
|
||||
get_dialog_with_autocomplete().as('dialog');
|
||||
it("should set the valid value", () => {
|
||||
get_dialog_with_autocomplete().as("dialog");
|
||||
|
||||
cy.get('.frappe-control[data-fieldname=autocomplete] input').focus().as('input');
|
||||
cy.get(".frappe-control[data-fieldname=autocomplete] input").focus().as("input");
|
||||
cy.wait(1000);
|
||||
cy.get('@input').type('2', { delay: 300 });
|
||||
cy.get('.frappe-control[data-fieldname=autocomplete]').findByRole('listbox').should('be.visible');
|
||||
cy.get('.frappe-control[data-fieldname=autocomplete] input').type('{enter}', { delay: 300 });
|
||||
cy.get('.frappe-control[data-fieldname=autocomplete] input').blur();
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('autocomplete');
|
||||
expect(value).to.eq('Option 2');
|
||||
cy.get("@input").type("2", { delay: 300 });
|
||||
cy.get(".frappe-control[data-fieldname=autocomplete]")
|
||||
.findByRole("listbox")
|
||||
.should("be.visible");
|
||||
cy.get(".frappe-control[data-fieldname=autocomplete] input").type("{enter}", {
|
||||
delay: 300,
|
||||
});
|
||||
cy.get(".frappe-control[data-fieldname=autocomplete] input").blur();
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("autocomplete");
|
||||
expect(value).to.eq("Option 2");
|
||||
dialog.clear();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the valid value with different label', () => {
|
||||
it("should set the valid value with different label", () => {
|
||||
const options_with_label = [
|
||||
{ label: "Option 1", value: "option_1" },
|
||||
{ label: "Option 2", value: "option_2" }
|
||||
{ label: "Option 2", value: "option_2" },
|
||||
];
|
||||
get_dialog_with_autocomplete(options_with_label).as('dialog');
|
||||
get_dialog_with_autocomplete(options_with_label).as("dialog");
|
||||
|
||||
cy.get('.frappe-control[data-fieldname=autocomplete] input').focus().as('input');
|
||||
cy.get('.frappe-control[data-fieldname=autocomplete]').findByRole('listbox').should('be.visible');
|
||||
cy.get('@input').type('2', { delay: 300 });
|
||||
cy.get('.frappe-control[data-fieldname=autocomplete] input').type('{enter}', { delay: 300 });
|
||||
cy.get('.frappe-control[data-fieldname=autocomplete] input').blur();
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('autocomplete');
|
||||
expect(value).to.eq('option_2');
|
||||
cy.get(".frappe-control[data-fieldname=autocomplete] input").focus().as("input");
|
||||
cy.get(".frappe-control[data-fieldname=autocomplete]")
|
||||
.findByRole("listbox")
|
||||
.should("be.visible");
|
||||
cy.get("@input").type("2", { delay: 300 });
|
||||
cy.get(".frappe-control[data-fieldname=autocomplete] input").type("{enter}", {
|
||||
delay: 300,
|
||||
});
|
||||
cy.get(".frappe-control[data-fieldname=autocomplete] input").blur();
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("autocomplete");
|
||||
expect(value).to.eq("option_2");
|
||||
dialog.clear();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,55 +1,57 @@
|
|||
context('Control Barcode', () => {
|
||||
context("Control Barcode", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_barcode() {
|
||||
return cy.dialog({
|
||||
title: 'Barcode',
|
||||
title: "Barcode",
|
||||
fields: [
|
||||
{
|
||||
label: 'Barcode',
|
||||
fieldname: 'barcode',
|
||||
fieldtype: 'Barcode'
|
||||
}
|
||||
]
|
||||
label: "Barcode",
|
||||
fieldname: "barcode",
|
||||
fieldtype: "Barcode",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('should generate barcode on setting a value', () => {
|
||||
get_dialog_with_barcode().as('dialog');
|
||||
it("should generate barcode on setting a value", () => {
|
||||
get_dialog_with_barcode().as("dialog");
|
||||
|
||||
cy.focused().blur();
|
||||
cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox')
|
||||
.type('123456789')
|
||||
cy.get(".frappe-control[data-fieldname=barcode]")
|
||||
.findByRole("textbox")
|
||||
.type("123456789")
|
||||
.blur();
|
||||
cy.get('.frappe-control[data-fieldname=barcode] svg[data-barcode-value="123456789"]')
|
||||
.should('exist');
|
||||
cy.get(
|
||||
'.frappe-control[data-fieldname=barcode] svg[data-barcode-value="123456789"]'
|
||||
).should("exist");
|
||||
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('barcode');
|
||||
expect(value).to.contain('<svg');
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("barcode");
|
||||
expect(value).to.contain("<svg");
|
||||
expect(value).to.contain('data-barcode-value="123456789"');
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset when input is cleared', () => {
|
||||
get_dialog_with_barcode().as('dialog');
|
||||
it("should reset when input is cleared", () => {
|
||||
get_dialog_with_barcode().as("dialog");
|
||||
|
||||
cy.focused().blur();
|
||||
cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox')
|
||||
.type('123456789')
|
||||
cy.get(".frappe-control[data-fieldname=barcode]")
|
||||
.findByRole("textbox")
|
||||
.type("123456789")
|
||||
.blur();
|
||||
cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox')
|
||||
.clear()
|
||||
.blur();
|
||||
cy.get('.frappe-control[data-fieldname=barcode] svg[data-barcode-value="123456789"]')
|
||||
.should('not.exist');
|
||||
cy.get(".frappe-control[data-fieldname=barcode]").findByRole("textbox").clear().blur();
|
||||
cy.get(
|
||||
'.frappe-control[data-fieldname=barcode] svg[data-barcode-value="123456789"]'
|
||||
).should("not.exist");
|
||||
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('barcode');
|
||||
expect(value).to.equal('');
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("barcode");
|
||||
expect(value).to.equal("");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,77 +1,80 @@
|
|||
context('Control Color', () => {
|
||||
context("Control Color", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_color() {
|
||||
return cy.dialog({
|
||||
title: 'Color',
|
||||
fields: [{
|
||||
label: 'Color',
|
||||
fieldname: 'color',
|
||||
fieldtype: 'Color'
|
||||
}]
|
||||
title: "Color",
|
||||
fields: [
|
||||
{
|
||||
label: "Color",
|
||||
fieldname: "color",
|
||||
fieldtype: "Color",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('Verifying if the color control is selecting correct', () => {
|
||||
get_dialog_with_color().as('dialog');
|
||||
cy.findByPlaceholderText('Choose a color').click();
|
||||
it("Verifying if the color control is selecting correct", () => {
|
||||
get_dialog_with_color().as("dialog");
|
||||
cy.findByPlaceholderText("Choose a color").click();
|
||||
|
||||
///Selecting a color from the color palette
|
||||
cy.get('[style="background-color: rgb(79, 157, 217);"]').click();
|
||||
|
||||
//Checking if the css attribute is correct
|
||||
cy.get('.color-map').should('have.css', 'color', 'rgb(79, 157, 217)');
|
||||
cy.get('.hue-map').should('have.css', 'color', 'rgb(0, 145, 255)');
|
||||
cy.get(".color-map").should("have.css", "color", "rgb(79, 157, 217)");
|
||||
cy.get(".hue-map").should("have.css", "color", "rgb(0, 144, 255)");
|
||||
|
||||
//Checking if the correct color is being selected
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('color');
|
||||
expect(value).to.equal('#4F9DD9');
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("color");
|
||||
expect(value).to.equal("#4F9DD9");
|
||||
});
|
||||
|
||||
//Selecting a color
|
||||
cy.get('[style="background-color: rgb(203, 41, 41);"]').click();
|
||||
|
||||
//Checking if the correct css is being selected
|
||||
cy.get('.color-map').should('have.css', 'color', 'rgb(203, 41, 41)');
|
||||
cy.get('.hue-map').should('have.css', 'color', 'rgb(255, 0, 0)');
|
||||
cy.get(".color-map").should("have.css", "color", "rgb(203, 41, 41)");
|
||||
cy.get(".hue-map").should("have.css", "color", "rgb(255, 0, 0)");
|
||||
|
||||
//Checking if the correct color is being selected
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('color');
|
||||
expect(value).to.equal('#CB2929');
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("color");
|
||||
expect(value).to.equal("#CB2929");
|
||||
});
|
||||
|
||||
//Selecting color from the palette
|
||||
cy.get('.color-map > .color-selector').click(65, 87, {force: true});
|
||||
cy.get('.color-map').should('have.css', 'color', 'rgb(56, 0, 0)');
|
||||
cy.get(".color-map > .color-selector").click(65, 87, { force: true });
|
||||
cy.get(".color-map").should("have.css", "color", "rgb(56, 0, 0)");
|
||||
|
||||
//Checking if the expected color is selected and getting displayed
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('color');
|
||||
expect(value).to.equal('#380000');
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("color");
|
||||
expect(value).to.equal("#380000");
|
||||
});
|
||||
|
||||
//Selecting the color from the hue map
|
||||
cy.get('.hue-map > .hue-selector').click(35, -1, {force: true});
|
||||
cy.get('.color-map').should('have.css', 'color', 'rgb(56, 45, 0)');
|
||||
cy.get('.hue-map').should('have.css', 'color', 'rgb(255, 204, 0)');
|
||||
cy.get('.color-map > .color-selector').click(55, 12, {force: true});
|
||||
cy.get('.color-map').should('have.css', 'color', 'rgb(46, 37, 0)');
|
||||
cy.get(".hue-map > .hue-selector").click(35, -1, { force: true });
|
||||
cy.get(".color-map").should("have.css", "color", "rgb(56, 45, 0)");
|
||||
cy.get(".hue-map").should("have.css", "color", "rgb(255, 204, 0)");
|
||||
cy.get(".color-map > .color-selector").click(55, 12, { force: true });
|
||||
cy.get(".color-map").should("have.css", "color", "rgb(46, 37, 0)");
|
||||
|
||||
//Checking if the correct color is being displayed
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('color');
|
||||
expect(value).to.equal('#2e2500');
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("color");
|
||||
expect(value).to.equal("#2e2500");
|
||||
});
|
||||
|
||||
//Clearing the field and checking if the field contains the placeholder "Choose a color"
|
||||
cy.get('.input-with-feedback').click({force: true});
|
||||
cy.get_field('color', 'Color').type('{selectall}').clear();
|
||||
cy.get_field('color', 'Color').invoke('attr', 'placeholder').should('contain', 'Choose a color');
|
||||
|
||||
cy.get(".input-with-feedback").click({ force: true });
|
||||
cy.get_field("color", "Color").type("{selectall}").clear();
|
||||
cy.get_field("color", "Color")
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", "Choose a color");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,134 +1,145 @@
|
|||
context('Data Control', () => {
|
||||
context("Data Control", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/doctype');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.xcall('frappe.tests.ui_test_helpers.create_doctype', {
|
||||
name: 'Test Data Control',
|
||||
fields: [
|
||||
{
|
||||
"label": "Name",
|
||||
"fieldname": "name1",
|
||||
"fieldtype": "Data",
|
||||
"options": "Name",
|
||||
"in_list_view": 1,
|
||||
"reqd": 1,
|
||||
},
|
||||
{
|
||||
"label": "Email-ID",
|
||||
"fieldname": "email",
|
||||
"fieldtype": "Data",
|
||||
"options": "Email",
|
||||
"in_list_view": 1,
|
||||
"reqd": 1,
|
||||
},
|
||||
{
|
||||
"label": "Phone No.",
|
||||
"fieldname": "phone",
|
||||
"fieldtype": "Data",
|
||||
"options": "Phone",
|
||||
"in_list_view": 1,
|
||||
"reqd": 1,
|
||||
},
|
||||
]
|
||||
cy.visit("/app/doctype");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.xcall("frappe.tests.ui_test_helpers.create_doctype", {
|
||||
name: "Test Data Control",
|
||||
fields: [
|
||||
{
|
||||
label: "Name",
|
||||
fieldname: "name1",
|
||||
fieldtype: "Data",
|
||||
options: "Name",
|
||||
in_list_view: 1,
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
label: "Email-ID",
|
||||
fieldname: "email",
|
||||
fieldtype: "Data",
|
||||
options: "Email",
|
||||
in_list_view: 1,
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
label: "Phone No.",
|
||||
fieldname: "phone",
|
||||
fieldtype: "Data",
|
||||
options: "Phone",
|
||||
in_list_view: 1,
|
||||
reqd: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('check custom formatters', () => {
|
||||
it("check custom formatters", () => {
|
||||
cy.visit(`/app/doctype/User`);
|
||||
cy.get('[data-fieldname="fields"] .grid-row[data-idx="2"] [data-fieldname="fieldtype"] .static-area').should('have.text', '🔵 Section Break');
|
||||
cy.get(
|
||||
'[data-fieldname="fields"] .grid-row[data-idx="3"] [data-fieldname="fieldtype"] .static-area'
|
||||
).should("have.text", "Section Break");
|
||||
});
|
||||
|
||||
it('Verifying data control by inputting different patterns for "Name" field', () => {
|
||||
cy.new_form('Test Data Control');
|
||||
cy.new_form("Test Data Control");
|
||||
|
||||
//Checking the URL for the new form of the doctype
|
||||
cy.location("pathname").should('eq', '/app/test-data-control/new-test-data-control-1');
|
||||
cy.get('.title-text').should('have.text', 'New Test Data Control');
|
||||
cy.get('.frappe-control[data-fieldname="name1"]').find('label').should('have.class', 'reqd');
|
||||
cy.get('.frappe-control[data-fieldname="email"]').find('label').should('have.class', 'reqd');
|
||||
cy.get('.frappe-control[data-fieldname="phone"]').find('label').should('have.class', 'reqd');
|
||||
cy.location("pathname").should("eq", "/app/test-data-control/new-test-data-control-1");
|
||||
cy.get(".title-text").should("have.text", "New Test Data Control");
|
||||
cy.get('.frappe-control[data-fieldname="name1"]')
|
||||
.find("label")
|
||||
.should("have.class", "reqd");
|
||||
cy.get('.frappe-control[data-fieldname="email"]')
|
||||
.find("label")
|
||||
.should("have.class", "reqd");
|
||||
cy.get('.frappe-control[data-fieldname="phone"]')
|
||||
.find("label")
|
||||
.should("have.class", "reqd");
|
||||
|
||||
//Checking if the status is "Not Saved" initially
|
||||
cy.get('.indicator-pill').should('have.text', 'Not Saved');
|
||||
cy.get(".indicator-pill").should("have.text", "Not Saved");
|
||||
|
||||
//Inputting data in the field
|
||||
cy.fill_field('name1', '@@###', 'Data');
|
||||
cy.fill_field('email', 'test@example.com', 'Data');
|
||||
cy.fill_field('phone', '9834280031', 'Data');
|
||||
cy.fill_field("name1", "@@###", "Data");
|
||||
cy.fill_field("email", "test@example.com", "Data");
|
||||
cy.fill_field("phone", "9834280031", "Data");
|
||||
|
||||
//Checking if the border color of the field changes to red
|
||||
cy.get('.frappe-control[data-fieldname="name1"]').should('have.class', 'has-error');
|
||||
cy.get('.frappe-control[data-fieldname="name1"]').should("have.class", "has-error");
|
||||
cy.save();
|
||||
|
||||
//Checking for the error message
|
||||
cy.get('.modal-title').should('have.text', 'Message');
|
||||
cy.get('.msgprint').should('have.text', '@@### is not a valid Name');
|
||||
cy.get(".modal-title").should("have.text", "Message");
|
||||
cy.get(".msgprint").should("have.text", "@@### is not a valid Name");
|
||||
cy.hide_dialog();
|
||||
|
||||
cy.get_field('name1', 'Data').clear({force: true});
|
||||
cy.fill_field('name1', 'Komal{}/!', 'Data');
|
||||
cy.get('.frappe-control[data-fieldname="name1"]').should('have.class', 'has-error');
|
||||
cy.get_field("name1", "Data").clear({ force: true });
|
||||
cy.fill_field("name1", "Komal{}/!", "Data");
|
||||
cy.get('.frappe-control[data-fieldname="name1"]').should("have.class", "has-error");
|
||||
cy.save();
|
||||
cy.get('.modal-title').should('have.text', 'Message');
|
||||
cy.get('.msgprint').should('have.text', 'Komal{}/! is not a valid Name');
|
||||
cy.get(".modal-title").should("have.text", "Message");
|
||||
cy.get(".msgprint").should("have.text", "Komal{}/! is not a valid Name");
|
||||
cy.hide_dialog();
|
||||
});
|
||||
|
||||
it('Verifying data control by inputting different patterns for "Email" field', () => {
|
||||
cy.get_field('name1', 'Data').clear({force: true});
|
||||
cy.fill_field('name1', 'Komal', 'Data');
|
||||
cy.get_field('email', 'Data').clear({force: true});
|
||||
cy.fill_field('email', 'komal', 'Data');
|
||||
cy.get('.frappe-control[data-fieldname="email"]').should('have.class', 'has-error');
|
||||
cy.get_field("name1", "Data").clear({ force: true });
|
||||
cy.fill_field("name1", "Komal", "Data");
|
||||
cy.get_field("email", "Data").clear({ force: true });
|
||||
cy.fill_field("email", "komal", "Data");
|
||||
cy.get('.frappe-control[data-fieldname="email"]').should("have.class", "has-error");
|
||||
cy.save();
|
||||
cy.get('.modal-title').should('have.text', 'Message');
|
||||
cy.get('.msgprint').should('have.text', 'komal is not a valid Email Address');
|
||||
cy.get(".modal-title").should("have.text", "Message");
|
||||
cy.get(".msgprint").should("have.text", "komal is not a valid Email Address");
|
||||
cy.hide_dialog();
|
||||
cy.get_field('email', 'Data').clear({force: true});
|
||||
cy.fill_field('email', 'komal@test', 'Data');
|
||||
cy.get('.frappe-control[data-fieldname="email"]').should('have.class', 'has-error');
|
||||
cy.get_field("email", "Data").clear({ force: true });
|
||||
cy.fill_field("email", "komal@test", "Data");
|
||||
cy.get('.frappe-control[data-fieldname="email"]').should("have.class", "has-error");
|
||||
cy.save();
|
||||
cy.get('.modal-title').should('have.text', 'Message');
|
||||
cy.get('.msgprint').should('have.text', 'komal@test is not a valid Email Address');
|
||||
cy.get(".modal-title").should("have.text", "Message");
|
||||
cy.get(".msgprint").should("have.text", "komal@test is not a valid Email Address");
|
||||
cy.hide_dialog();
|
||||
});
|
||||
|
||||
it('Verifying data control by inputting different patterns for "Phone" field', () => {
|
||||
cy.get_field('email', 'Data').clear({force: true});
|
||||
cy.fill_field('email', 'komal@test.com', 'Data');
|
||||
cy.get_field('phone', 'Data').clear({force: true});
|
||||
cy.fill_field('phone', 'komal', 'Data');
|
||||
cy.get('.frappe-control[data-fieldname="phone"]').should('have.class', 'has-error');
|
||||
cy.findByRole('button', {name: 'Save'}).click({force: true});
|
||||
cy.get('.modal-title').should('have.text', 'Message');
|
||||
cy.get('.msgprint').should('have.text', 'komal is not a valid Phone Number');
|
||||
cy.get_field("email", "Data").clear({ force: true });
|
||||
cy.fill_field("email", "komal@test.com", "Data");
|
||||
cy.get_field("phone", "Data").clear({ force: true });
|
||||
cy.fill_field("phone", "komal", "Data");
|
||||
cy.get('.frappe-control[data-fieldname="phone"]').should("have.class", "has-error");
|
||||
cy.findByRole("button", { name: "Save" }).click({ force: true });
|
||||
cy.get(".modal-title").should("have.text", "Message");
|
||||
cy.get(".msgprint").should("have.text", "komal is not a valid Phone Number");
|
||||
cy.hide_dialog();
|
||||
});
|
||||
|
||||
it('Inputting correct data and saving the doc', () => {
|
||||
it("Inputting correct data and saving the doc", () => {
|
||||
//Inputting the data as expected and saving the document
|
||||
cy.get_field('name1', 'Data').clear({force: true});
|
||||
cy.get_field('email', 'Data').clear({force: true});
|
||||
cy.get_field('phone', 'Data').clear({force: true});
|
||||
cy.fill_field('name1', 'Komal', 'Data');
|
||||
cy.fill_field('email', 'komal@test.com', 'Data');
|
||||
cy.fill_field('phone', '9432380001', 'Data');
|
||||
cy.findByRole('button', {name: 'Save'}).click({force: true});
|
||||
cy.get_field("name1", "Data").clear({ force: true });
|
||||
cy.get_field("email", "Data").clear({ force: true });
|
||||
cy.get_field("phone", "Data").clear({ force: true });
|
||||
cy.fill_field("name1", "Komal", "Data");
|
||||
cy.fill_field("email", "komal@test.com", "Data");
|
||||
cy.fill_field("phone", "9432380001", "Data");
|
||||
cy.findByRole("button", { name: "Save" }).click({ force: true });
|
||||
//Checking if the fields contains the data which has been filled in
|
||||
cy.location("pathname").should('not.be', '/app/test-data-control/new-test-data-control-1');
|
||||
cy.get_field('name1').should('have.value', 'Komal');
|
||||
cy.get_field('email').should('have.value', 'komal@test.com');
|
||||
cy.get_field('phone').should('have.value', '9432380001');
|
||||
cy.location("pathname").should("not.be", "/app/test-data-control/new-test-data-control-1");
|
||||
cy.get_field("name1").should("have.value", "Komal");
|
||||
cy.get_field("email").should("have.value", "komal@test.com");
|
||||
cy.get_field("phone").should("have.value", "9432380001");
|
||||
});
|
||||
|
||||
it('Deleting the doc', () => {
|
||||
it("Deleting the doc", () => {
|
||||
//Deleting the inserted document
|
||||
cy.go_to_list('Test Data Control');
|
||||
cy.get('.list-row-checkbox').eq(0).click({force: true});
|
||||
cy.get('.actions-btn-group > .btn').contains('Actions').click();
|
||||
cy.go_to_list("Test Data Control");
|
||||
cy.get(".list-row-checkbox").eq(0).click({ force: true });
|
||||
cy.get(".actions-btn-group > .btn").contains("Actions").click();
|
||||
cy.get('.actions-btn-group > .dropdown-menu [data-label="Delete"]').click();
|
||||
cy.click_modal_primary_button('Yes');
|
||||
cy.click_modal_primary_button("Yes");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,82 +1,83 @@
|
|||
context('Date Control', () => {
|
||||
context("Date Control", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app');
|
||||
cy.visit("/app");
|
||||
});
|
||||
|
||||
function get_dialog(date_field_options) {
|
||||
return cy.dialog({
|
||||
title: 'Date',
|
||||
fields: [{
|
||||
"label": "Date",
|
||||
"fieldname": "date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
...date_field_options
|
||||
}]
|
||||
title: "Date",
|
||||
fields: [
|
||||
{
|
||||
label: "Date",
|
||||
fieldname: "date",
|
||||
fieldtype: "Date",
|
||||
in_list_view: 1,
|
||||
...date_field_options,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('Selecting a date from the datepicker', () => {
|
||||
it("Selecting a date from the datepicker & check prev & next button", () => {
|
||||
cy.clear_dialogs();
|
||||
cy.clear_datepickers();
|
||||
|
||||
get_dialog().as('dialog');
|
||||
cy.get_field('date', 'Date').click();
|
||||
cy.get('.datepicker--nav-title').click();
|
||||
cy.get('.datepicker--nav-title').click({force: true});
|
||||
|
||||
get_dialog().as("dialog");
|
||||
cy.get_field("date", "Date").click();
|
||||
cy.get(".datepicker--nav-title").click();
|
||||
cy.get(".datepicker--nav-title").click({ force: true });
|
||||
|
||||
//Inputing values in the date field
|
||||
cy.get('.datepicker--years > .datepicker--cells > .datepicker--cell[data-year=2020]').click();
|
||||
cy.get('.datepicker--months > .datepicker--cells > .datepicker--cell[data-month=0]').click();
|
||||
cy.get('.datepicker--days > .datepicker--cells > .datepicker--cell[data-date=15]').click();
|
||||
cy.get(
|
||||
".datepicker--years > .datepicker--cells > .datepicker--cell[data-year=2020]"
|
||||
).click();
|
||||
cy.get(
|
||||
".datepicker--months > .datepicker--cells > .datepicker--cell[data-month=0]"
|
||||
).click();
|
||||
cy.get(".datepicker--days > .datepicker--cells > .datepicker--cell[data-date=15]").click();
|
||||
|
||||
// Verify if the selected date is set the date field
|
||||
cy.window().its('cur_dialog.fields_dict.date.value').should('be.equal', '2020-01-15');
|
||||
});
|
||||
cy.window().its("cur_dialog.fields_dict.date.value").should("be.equal", "2020-01-15");
|
||||
|
||||
it('Checking next and previous button', () => {
|
||||
cy.clear_dialogs();
|
||||
cy.clear_datepickers();
|
||||
|
||||
get_dialog({ default: '2020-01-15' }).as('dialog');
|
||||
cy.get_field('date', 'Date').click();
|
||||
cy.get_field("date", "Date").click();
|
||||
|
||||
//Clicking on the next button in the datepicker
|
||||
cy.get('.datepicker--nav-action[data-action=next]').click();
|
||||
cy.get(".datepicker--nav-action[data-action=next]").click();
|
||||
|
||||
//Selecting a date from the datepicker
|
||||
cy.get('.datepicker--cell[data-date=15]').click({force: true});
|
||||
cy.get(".datepicker--cell[data-date=15]").click({ force: true });
|
||||
|
||||
//Verifying if the selected date has been displayed in the date field
|
||||
cy.window().its('cur_dialog.fields_dict.date.value').should('be.equal', '2020-02-15');
|
||||
cy.window().its("cur_dialog.fields_dict.date.value").should("be.equal", "2020-02-15");
|
||||
cy.wait(500);
|
||||
cy.get_field('date', 'Date').click();
|
||||
cy.get_field("date", "Date").click();
|
||||
|
||||
//Clicking on the previous button in the datepicker
|
||||
cy.get('.datepicker--nav-action[data-action=prev]').click();
|
||||
cy.get(".datepicker--nav-action[data-action=prev]").click();
|
||||
|
||||
//Selecting a date from the datepicker
|
||||
cy.get('.datepicker--cell[data-date=15]').click({force: true});
|
||||
cy.get(".datepicker--cell[data-date=15]").click({ force: true });
|
||||
|
||||
//Verifying if the selected date has been displayed in the date field
|
||||
cy.window().its('cur_dialog.fields_dict.date.value').should('be.equal', '2020-01-15');
|
||||
cy.window().its("cur_dialog.fields_dict.date.value").should("be.equal", "2020-01-15");
|
||||
});
|
||||
|
||||
it('Clicking on "Today" button gives todays date', () => {
|
||||
cy.clear_dialogs();
|
||||
cy.clear_datepickers();
|
||||
|
||||
get_dialog().as('dialog');
|
||||
cy.get_field('date', 'Date').click();
|
||||
get_dialog().as("dialog");
|
||||
cy.get_field("date", "Date").click();
|
||||
|
||||
//Clicking on "Today" button
|
||||
cy.get('.datepicker--button').click();
|
||||
cy.get(".datepicker--button").click();
|
||||
|
||||
//Verifying if clicking on "Today" button matches today's date
|
||||
cy.window().then(win => {
|
||||
expect(win.cur_dialog.fields_dict.date.value).to.be.equal(win.frappe.datetime.get_today());
|
||||
cy.window().then((win) => {
|
||||
expect(win.cur_dialog.fields_dict.date.value).to.be.equal(
|
||||
win.frappe.datetime.get_today()
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,42 +1,48 @@
|
|||
context('Date Range Control', () => {
|
||||
context("Date Range Control", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app');
|
||||
cy.visit("/app");
|
||||
});
|
||||
|
||||
function get_dialog() {
|
||||
return cy.dialog({
|
||||
title: 'Date Range',
|
||||
fields: [{
|
||||
"label": "Date Range",
|
||||
"fieldname": "date_range",
|
||||
"fieldtype": "Date Range",
|
||||
}]
|
||||
title: "Date Range",
|
||||
fields: [
|
||||
{
|
||||
label: "Date Range",
|
||||
fieldname: "date_range",
|
||||
fieldtype: "Date Range",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('Selecting a date range from the datepicker', () => {
|
||||
it("Selecting a date range from the datepicker", () => {
|
||||
cy.clear_dialogs();
|
||||
cy.clear_datepickers();
|
||||
|
||||
get_dialog().as('dialog');
|
||||
cy.get_field('date_range', 'Date Range').click();
|
||||
cy.get('.datepicker--nav-title').click();
|
||||
cy.get('.datepicker--nav-title').click({force: true});
|
||||
get_dialog().as("dialog");
|
||||
cy.get_field("date_range", "Date Range").click();
|
||||
cy.get(".datepicker--nav-title").click();
|
||||
cy.get(".datepicker--nav-title").click({ force: true });
|
||||
|
||||
//Inputing date range values in the date range field
|
||||
cy.get('.datepicker--years > .datepicker--cells > .datepicker--cell[data-year=2020]').click();
|
||||
cy.get('.datepicker--months > .datepicker--cells > .datepicker--cell[data-month=0]').click();
|
||||
cy.get('.datepicker--cell[data-date=1]:first').click({force: true});
|
||||
cy.get('.datepicker--cell[data-date=15]:first').click({force: true});
|
||||
cy.get(
|
||||
".datepicker--years > .datepicker--cells > .datepicker--cell[data-year=2020]"
|
||||
).click();
|
||||
cy.get(
|
||||
".datepicker--months > .datepicker--cells > .datepicker--cell[data-month=0]"
|
||||
).click();
|
||||
cy.get(".datepicker--cell[data-date=1]:first").click({ force: true });
|
||||
cy.get(".datepicker--cell[data-date=15]:first").click({ force: true });
|
||||
|
||||
// Verify if the selected date range values is set in the date range field
|
||||
cy.window()
|
||||
.its('cur_dialog')
|
||||
.then(dialog => {
|
||||
.its("cur_dialog")
|
||||
.then((dialog) => {
|
||||
let date_range = dialog.get_value("date_range");
|
||||
expect(date_range[0]).to.equal('2020-01-01');
|
||||
expect(date_range[1]).to.equal('2020-01-15');
|
||||
expect(date_range[0]).to.equal("2020-01-01");
|
||||
expect(date_range[1]).to.equal("2020-01-15");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,46 +1,46 @@
|
|||
context('Control Duration', () => {
|
||||
context("Control Duration", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_duration(hide_days = 0, hide_seconds = 0) {
|
||||
return cy.dialog({
|
||||
title: 'Duration',
|
||||
fields: [{
|
||||
'fieldname': 'duration',
|
||||
'fieldtype': 'Duration',
|
||||
'hide_days': hide_days,
|
||||
'hide_seconds': hide_seconds
|
||||
}]
|
||||
title: "Duration",
|
||||
fields: [
|
||||
{
|
||||
fieldname: "duration",
|
||||
fieldtype: "Duration",
|
||||
hide_days: hide_days,
|
||||
hide_seconds: hide_seconds,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('should set duration', () => {
|
||||
get_dialog_with_duration().as('dialog');
|
||||
cy.get('.frappe-control[data-fieldname=duration] input')
|
||||
.first()
|
||||
.click();
|
||||
cy.get('.duration-input[data-duration=days]')
|
||||
it("should set duration", () => {
|
||||
get_dialog_with_duration().as("dialog");
|
||||
cy.get(".frappe-control[data-fieldname=duration] input").first().click();
|
||||
cy.get(".duration-input[data-duration=days]")
|
||||
.type(45, { force: true })
|
||||
.blur({ force: true });
|
||||
cy.get('.duration-input[data-duration=minutes]')
|
||||
.type(30)
|
||||
.blur({ force: true });
|
||||
cy.get('.frappe-control[data-fieldname=duration] input').first().should('have.value', '45d 30m');
|
||||
cy.get('.frappe-control[data-fieldname=duration] input').first().blur();
|
||||
cy.get('.duration-picker').should('not.be.visible');
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('duration');
|
||||
cy.get(".duration-input[data-duration=minutes]").type(30).blur({ force: true });
|
||||
cy.get(".frappe-control[data-fieldname=duration] input")
|
||||
.first()
|
||||
.should("have.value", "45d 30m");
|
||||
cy.get(".frappe-control[data-fieldname=duration] input").first().blur();
|
||||
cy.get(".duration-picker").should("not.be.visible");
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("duration");
|
||||
expect(value).to.equal(3889800);
|
||||
cy.hide_dialog();
|
||||
});
|
||||
});
|
||||
|
||||
it('should hide days or seconds according to duration options', () => {
|
||||
get_dialog_with_duration(1, 1).as('dialog');
|
||||
cy.get('.frappe-control[data-fieldname=duration] input').first();
|
||||
cy.get('.duration-input[data-duration=days]').should('not.be.visible');
|
||||
cy.get('.duration-input[data-duration=seconds]').should('not.be.visible');
|
||||
it("should hide days or seconds according to duration options", () => {
|
||||
get_dialog_with_duration(1, 1).as("dialog");
|
||||
cy.get(".frappe-control[data-fieldname=duration] input").first();
|
||||
cy.get(".duration-input[data-duration=days]").should("not.be.visible");
|
||||
cy.get(".duration-input[data-duration=seconds]").should("not.be.visible");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,133 +1,159 @@
|
|||
context('Dynamic Link', () => {
|
||||
context("Dynamic Link", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/doctype');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.xcall('frappe.tests.ui_test_helpers.create_doctype', {
|
||||
name: 'Test Dynamic Link',
|
||||
fields: [
|
||||
{
|
||||
"label": "Document Type",
|
||||
"fieldname": "doc_type",
|
||||
"fieldtype": "Link",
|
||||
"options": "DocType",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
},
|
||||
{
|
||||
"label": "Document ID",
|
||||
"fieldname": "doc_id",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "doc_type",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
},
|
||||
]
|
||||
cy.visit("/app/doctype");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.xcall("frappe.tests.ui_test_helpers.create_doctype", {
|
||||
name: "Test Dynamic Link",
|
||||
fields: [
|
||||
{
|
||||
label: "Document Type",
|
||||
fieldname: "doc_type",
|
||||
fieldtype: "Link",
|
||||
options: "DocType",
|
||||
in_list_view: 1,
|
||||
in_standard_filter: 1,
|
||||
},
|
||||
{
|
||||
label: "Document ID",
|
||||
fieldname: "doc_id",
|
||||
fieldtype: "Dynamic Link",
|
||||
options: "doc_type",
|
||||
in_list_view: 1,
|
||||
in_standard_filter: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function get_dialog_with_dynamic_link() {
|
||||
return cy.dialog({
|
||||
title: 'Dynamic Link',
|
||||
fields: [{
|
||||
"label": "Document Type",
|
||||
"fieldname": "doc_type",
|
||||
"fieldtype": "Link",
|
||||
"options": "DocType",
|
||||
"in_list_view": 1,
|
||||
},
|
||||
{
|
||||
"label": "Document ID",
|
||||
"fieldname": "doc_id",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "doc_type",
|
||||
"in_list_view": 1,
|
||||
}]
|
||||
title: "Dynamic Link",
|
||||
fields: [
|
||||
{
|
||||
label: "Document Type",
|
||||
fieldname: "doc_type",
|
||||
fieldtype: "Link",
|
||||
options: "DocType",
|
||||
in_list_view: 1,
|
||||
},
|
||||
{
|
||||
label: "Document ID",
|
||||
fieldname: "doc_id",
|
||||
fieldtype: "Dynamic Link",
|
||||
options: "doc_type",
|
||||
in_list_view: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function get_dialog_with_dynamic_link_option() {
|
||||
return cy.dialog({
|
||||
title: 'Dynamic Link',
|
||||
fields: [{
|
||||
"label": "Document Type",
|
||||
"fieldname": "doc_type",
|
||||
"fieldtype": "Link",
|
||||
"options": "DocType",
|
||||
"in_list_view": 1,
|
||||
},
|
||||
{
|
||||
"label": "Document ID",
|
||||
"fieldname": "doc_id",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"get_options": () => {
|
||||
return "User";
|
||||
title: "Dynamic Link",
|
||||
fields: [
|
||||
{
|
||||
label: "Document Type",
|
||||
fieldname: "doc_type",
|
||||
fieldtype: "Link",
|
||||
options: "DocType",
|
||||
in_list_view: 1,
|
||||
},
|
||||
"in_list_view": 1,
|
||||
}]
|
||||
{
|
||||
label: "Document ID",
|
||||
fieldname: "doc_id",
|
||||
fieldtype: "Dynamic Link",
|
||||
get_options: () => {
|
||||
return "User";
|
||||
},
|
||||
in_list_view: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('Creating a dynamic link by passing option as function and verifying it in a dialog', () => {
|
||||
get_dialog_with_dynamic_link_option().as('dialog');
|
||||
cy.get_field('doc_type').clear();
|
||||
cy.fill_field('doc_type', 'User', 'Link');
|
||||
cy.get_field('doc_id').click();
|
||||
it("Creating a dynamic link by passing option as function and verifying it in a dialog", () => {
|
||||
get_dialog_with_dynamic_link_option().as("dialog");
|
||||
cy.get_field("doc_type").clear();
|
||||
cy.fill_field("doc_type", "User", "Link");
|
||||
cy.get_field("doc_id").click();
|
||||
|
||||
//Checking if the listbox have length greater than 0
|
||||
cy.get('[data-fieldname="doc_id"]').find('.awesomplete').find("li").its('length').should('be.gte', 0);
|
||||
cy.get('.btn-modal-close').click({force: true});
|
||||
cy.get('[data-fieldname="doc_id"]')
|
||||
.find(".awesomplete")
|
||||
.find("li")
|
||||
.its("length")
|
||||
.should("be.gte", 0);
|
||||
cy.get(".btn-modal-close").click({ force: true });
|
||||
});
|
||||
|
||||
it('Creating a dynamic link and verifying it in a dialog', () => {
|
||||
get_dialog_with_dynamic_link().as('dialog');
|
||||
cy.get_field('doc_type').clear();
|
||||
cy.fill_field('doc_type', 'User', 'Link');
|
||||
cy.get_field('doc_id').click();
|
||||
it("Creating a dynamic link and verifying it in a dialog", () => {
|
||||
get_dialog_with_dynamic_link().as("dialog");
|
||||
cy.get_field("doc_type").clear();
|
||||
cy.fill_field("doc_type", "User", "Link");
|
||||
cy.get_field("doc_id").click();
|
||||
|
||||
//Checking if the listbox have length greater than 0
|
||||
cy.get('[data-fieldname="doc_id"]').find('.awesomplete').find("li").its('length').should('be.gte', 0);
|
||||
cy.get('.btn-modal-close').click({force: true, multiple: true});
|
||||
cy.get('[data-fieldname="doc_id"]')
|
||||
.find(".awesomplete")
|
||||
.find("li")
|
||||
.its("length")
|
||||
.should("be.gte", 0);
|
||||
cy.get(".btn-modal-close").click({ force: true, multiple: true });
|
||||
});
|
||||
|
||||
it('Creating a dynamic link and verifying it', () => {
|
||||
cy.visit('/app/test-dynamic-link');
|
||||
it("Creating a dynamic link and verifying it", () => {
|
||||
cy.visit("/app/test-dynamic-link");
|
||||
|
||||
//Clicking on the Document ID field
|
||||
cy.get_field('doc_type').clear();
|
||||
cy.get_field("doc_type").clear();
|
||||
|
||||
//Entering User in the Doctype field
|
||||
cy.fill_field('doc_type', 'User', 'Link', {delay: 500});
|
||||
cy.get_field('doc_id').click();
|
||||
cy.fill_field("doc_type", "User", "Link", { delay: 500 });
|
||||
cy.get_field("doc_id").click();
|
||||
|
||||
//Checking if the listbox have length greater than 0
|
||||
cy.get('[data-fieldname="doc_id"]').find('.awesomplete').find("li").its('length').should('be.gte', 0);
|
||||
cy.get('[data-fieldname="doc_id"]')
|
||||
.find(".awesomplete")
|
||||
.find("li")
|
||||
.its("length")
|
||||
.should("be.gte", 0);
|
||||
|
||||
//Opening a new form for dynamic link doctype
|
||||
cy.new_form('Test Dynamic Link');
|
||||
cy.get_field('doc_type').clear();
|
||||
cy.new_form("Test Dynamic Link");
|
||||
cy.get_field("doc_type").clear();
|
||||
|
||||
//Entering User in the Doctype field
|
||||
cy.fill_field('doc_type', 'User', 'Link', {delay: 500});
|
||||
cy.get_field('doc_id').click();
|
||||
cy.fill_field("doc_type", "User", "Link", { delay: 500 });
|
||||
cy.get_field("doc_id").click();
|
||||
|
||||
//Checking if the listbox have length greater than 0
|
||||
cy.get('[data-fieldname="doc_id"]').find('.awesomplete').find("li").its('length').should('be.gte', 0);
|
||||
cy.get_field('doc_type').clear();
|
||||
cy.get('[data-fieldname="doc_id"]')
|
||||
.find(".awesomplete")
|
||||
.find("li")
|
||||
.its("length")
|
||||
.should("be.gte", 0);
|
||||
cy.get_field("doc_type").clear();
|
||||
|
||||
//Entering System Settings in the Doctype field
|
||||
cy.intercept('/api/method/frappe.desk.search.search_link').as('search_query');
|
||||
cy.fill_field('doc_type', 'System Settings', 'Link', {delay: 500});
|
||||
cy.wait('@search_query');
|
||||
cy.get(`[data-fieldname="doc_type"] ul:visible li:first-child`)
|
||||
.click({scrollBehavior: false});
|
||||
cy.intercept("/api/method/frappe.desk.search.search_link").as("search_query");
|
||||
cy.fill_field("doc_type", "System Settings", "Link", { delay: 500 });
|
||||
cy.wait("@search_query");
|
||||
cy.get(`[data-fieldname="doc_type"] ul:visible li:first-child`).click({
|
||||
scrollBehavior: false,
|
||||
});
|
||||
|
||||
cy.get_field('doc_id').click();
|
||||
cy.get_field("doc_id").click();
|
||||
|
||||
//Checking if the system throws error
|
||||
cy.get('.modal-title').should('have.text', 'Error');
|
||||
cy.get('.msgprint').should('have.text', 'System Settings is not a valid DocType for Dynamic Link');
|
||||
cy.get(".modal-title").should("have.text", "Error");
|
||||
cy.get(".msgprint").should(
|
||||
"have.text",
|
||||
"System Settings is not a valid DocType for Dynamic Link"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ context("Control Float", () => {
|
|||
{
|
||||
fieldname: "float_number",
|
||||
fieldtype: "Float",
|
||||
Label: "Float"
|
||||
}
|
||||
]
|
||||
Label: "Float",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -21,27 +21,22 @@ context("Control Float", () => {
|
|||
get_dialog_with_float().as("dialog");
|
||||
|
||||
let data = get_data();
|
||||
data.forEach(x => {
|
||||
data.forEach((x) => {
|
||||
cy.window()
|
||||
.its("frappe")
|
||||
.then(frappe => {
|
||||
.then((frappe) => {
|
||||
frappe.boot.sysdefaults.number_format = x.number_format;
|
||||
});
|
||||
x.values.forEach(d => {
|
||||
x.values.forEach((d) => {
|
||||
cy.get_field("float_number", "Float").clear();
|
||||
cy.wait(200);
|
||||
cy.fill_field("float_number", d.input, "Float").blur();
|
||||
cy.get_field("float_number", "Float").should(
|
||||
"have.value",
|
||||
d.blur_expected
|
||||
);
|
||||
cy.get_field("float_number", "Float").should("have.value", d.blur_expected);
|
||||
|
||||
cy.get_field("float_number", "Float").focus();
|
||||
cy.get_field("float_number", "Float").blur();
|
||||
cy.get_field("float_number", "Float").focus();
|
||||
cy.get_field("float_number", "Float").should(
|
||||
"have.value",
|
||||
d.focus_expected
|
||||
);
|
||||
cy.get_field("float_number", "Float").should("have.value", d.focus_expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -54,19 +49,19 @@ context("Control Float", () => {
|
|||
{
|
||||
input: "364.87,334",
|
||||
blur_expected: "36.487,334",
|
||||
focus_expected: "36487.334"
|
||||
focus_expected: "36487.334",
|
||||
},
|
||||
{
|
||||
input: "36487,334",
|
||||
blur_expected: "36.487,334",
|
||||
focus_expected: "36487.334"
|
||||
focus_expected: "36487.334",
|
||||
},
|
||||
{
|
||||
input: "100",
|
||||
blur_expected: "100,000",
|
||||
focus_expected: "100"
|
||||
}
|
||||
]
|
||||
focus_expected: "100",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
number_format: "#,###.##",
|
||||
|
|
@ -74,20 +69,20 @@ context("Control Float", () => {
|
|||
{
|
||||
input: "364,87.334",
|
||||
blur_expected: "36,487.334",
|
||||
focus_expected: "36487.334"
|
||||
focus_expected: "36487.334",
|
||||
},
|
||||
{
|
||||
input: "36487.334",
|
||||
blur_expected: "36,487.334",
|
||||
focus_expected: "36487.334"
|
||||
focus_expected: "36487.334",
|
||||
},
|
||||
{
|
||||
input: "100",
|
||||
blur_expected: "100.000",
|
||||
focus_expected: "100"
|
||||
}
|
||||
]
|
||||
}
|
||||
focus_expected: "100",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,50 +1,55 @@
|
|||
context('Control Icon', () => {
|
||||
context("Control Icon", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_icon() {
|
||||
return cy.dialog({
|
||||
title: 'Icon',
|
||||
fields: [{
|
||||
label: 'Icon',
|
||||
fieldname: 'icon',
|
||||
fieldtype: 'Icon'
|
||||
}]
|
||||
title: "Icon",
|
||||
fields: [
|
||||
{
|
||||
label: "Icon",
|
||||
fieldname: "icon",
|
||||
fieldtype: "Icon",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('should set icon', () => {
|
||||
get_dialog_with_icon().as('dialog');
|
||||
cy.get('.frappe-control[data-fieldname=icon]').findByRole('textbox').click();
|
||||
it("should set icon", () => {
|
||||
get_dialog_with_icon().as("dialog");
|
||||
cy.get(".frappe-control[data-fieldname=icon]").findByRole("textbox").click();
|
||||
|
||||
cy.get('.icon-picker .icon-wrapper[id=heart-active]').first().click();
|
||||
cy.get('.frappe-control[data-fieldname=icon]').findByRole('textbox').should('have.value', 'heart-active');
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('icon');
|
||||
expect(value).to.equal('heart-active');
|
||||
cy.get(".icon-picker .icon-wrapper[id=heart-active]").first().click();
|
||||
cy.get(".frappe-control[data-fieldname=icon]")
|
||||
.findByRole("textbox")
|
||||
.should("have.value", "heart-active");
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("icon");
|
||||
expect(value).to.equal("heart-active");
|
||||
});
|
||||
|
||||
cy.get('.icon-picker .icon-wrapper[id=heart]').first().click();
|
||||
cy.get('.frappe-control[data-fieldname=icon]').findByRole('textbox').should('have.value', 'heart');
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let value = dialog.get_value('icon');
|
||||
expect(value).to.equal('heart');
|
||||
cy.get(".icon-picker .icon-wrapper[id=heart]").first().click();
|
||||
cy.get(".frappe-control[data-fieldname=icon]")
|
||||
.findByRole("textbox")
|
||||
.should("have.value", "heart");
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("icon");
|
||||
expect(value).to.equal("heart");
|
||||
});
|
||||
});
|
||||
|
||||
it('search for icon and clear search input', () => {
|
||||
let search_text = 'ed';
|
||||
cy.get('.icon-picker').findByRole('searchbox').click().type(search_text);
|
||||
cy.get('.icon-section .icon-wrapper:not(.hidden)').then(i => {
|
||||
cy.get(`.icon-section .icon-wrapper[id*='${search_text}']`).then(icons => {
|
||||
it("search for icon and clear search input", () => {
|
||||
let search_text = "ed";
|
||||
cy.get(".icon-picker").findByRole("searchbox").click().type(search_text);
|
||||
cy.get(".icon-section .icon-wrapper:not(.hidden)").then((i) => {
|
||||
cy.get(`.icon-section .icon-wrapper[id*='${search_text}']`).then((icons) => {
|
||||
expect(i.length).to.equal(icons.length);
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('.icon-picker').findByRole('searchbox').clear().blur();
|
||||
cy.get('.icon-section .icon-wrapper').should('not.have.class', 'hidden');
|
||||
cy.get(".icon-picker").findByRole("searchbox").clear().blur();
|
||||
cy.get(".icon-section .icon-wrapper").should("not.have.class", "hidden");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,93 +1,88 @@
|
|||
context('Control Link', () => {
|
||||
context("Control Link", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
cy.create_records({
|
||||
doctype: 'ToDo',
|
||||
description: 'this is a test todo for link'
|
||||
}).as('todos');
|
||||
doctype: "ToDo",
|
||||
description: "this is a test todo for link",
|
||||
}).as("todos");
|
||||
});
|
||||
|
||||
function get_dialog_with_link() {
|
||||
return cy.dialog({
|
||||
title: 'Link',
|
||||
title: "Link",
|
||||
fields: [
|
||||
{
|
||||
'label': 'Select ToDo',
|
||||
'fieldname': 'link',
|
||||
'fieldtype': 'Link',
|
||||
'options': 'ToDo',
|
||||
}
|
||||
]
|
||||
label: "Select ToDo",
|
||||
fieldname: "link",
|
||||
fieldtype: "Link",
|
||||
options: "ToDo",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function get_dialog_with_user_link() {
|
||||
function get_dialog_with_gender_link() {
|
||||
return cy.dialog({
|
||||
title: 'Link',
|
||||
title: "Link",
|
||||
fields: [
|
||||
{
|
||||
'label': 'Select User',
|
||||
'fieldname': 'link',
|
||||
'fieldtype': 'Link',
|
||||
'options': 'User',
|
||||
}
|
||||
]
|
||||
label: "Select Gender",
|
||||
fieldname: "link",
|
||||
fieldtype: "Link",
|
||||
options: "Gender",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('should set the valid value', () => {
|
||||
get_dialog_with_link().as('dialog');
|
||||
it("should set the valid value", () => {
|
||||
get_dialog_with_link().as("dialog");
|
||||
|
||||
cy.insert_doc("Property Setter", {
|
||||
"doctype": "Property Setter",
|
||||
"doc_type": "User",
|
||||
"property": "translate_link_fields",
|
||||
"property_type": "Check",
|
||||
"doctype_or_field": "DocType",
|
||||
"value": "0"
|
||||
}, true);
|
||||
cy.insert_doc(
|
||||
"Property Setter",
|
||||
{
|
||||
doctype: "Property Setter",
|
||||
doc_type: "ToDo",
|
||||
property: "show_title_field_in_link",
|
||||
property_type: "Check",
|
||||
doctype_or_field: "DocType",
|
||||
value: "0",
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
cy.insert_doc("Property Setter", {
|
||||
"doctype": "Property Setter",
|
||||
"doc_type": "ToDo",
|
||||
"property": "show_title_field_in_link",
|
||||
"property_type": "Check",
|
||||
"doctype_or_field": "DocType",
|
||||
"value": "0"
|
||||
}, true);
|
||||
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
|
||||
|
||||
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
|
||||
|
||||
cy.get('.frappe-control[data-fieldname=link] input').focus().as('input');
|
||||
cy.wait('@search_link');
|
||||
cy.get('@input').type('todo for link', { delay: 200 });
|
||||
cy.wait('@search_link');
|
||||
cy.get('.frappe-control[data-fieldname=link]').findByRole('listbox').should('be.visible');
|
||||
cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 });
|
||||
cy.get('.frappe-control[data-fieldname=link] input').blur();
|
||||
cy.get('@dialog').then(dialog => {
|
||||
cy.get('@todos').then(todos => {
|
||||
let value = dialog.get_value('link');
|
||||
cy.get(".frappe-control[data-fieldname=link] input").focus().as("input");
|
||||
cy.wait("@search_link");
|
||||
cy.get("@input").type("todo for link", { delay: 200 });
|
||||
cy.wait("@search_link");
|
||||
cy.get(".frappe-control[data-fieldname=link]").findByRole("listbox").should("be.visible");
|
||||
cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 });
|
||||
cy.get(".frappe-control[data-fieldname=link] input").blur();
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
cy.get("@todos").then((todos) => {
|
||||
let value = dialog.get_value("link");
|
||||
expect(value).to.eq(todos[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should unset invalid value', () => {
|
||||
get_dialog_with_link().as('dialog');
|
||||
it("should unset invalid value", () => {
|
||||
get_dialog_with_link().as("dialog");
|
||||
|
||||
cy.intercept('POST', '/api/method/frappe.client.validate_link').as('validate_link');
|
||||
cy.intercept("POST", "/api/method/frappe.client.validate_link").as("validate_link");
|
||||
|
||||
cy.get('.frappe-control[data-fieldname=link] input')
|
||||
.type('invalid value', { delay: 100 })
|
||||
cy.get(".frappe-control[data-fieldname=link] input")
|
||||
.type("invalid value", { delay: 100 })
|
||||
.blur();
|
||||
cy.wait('@validate_link');
|
||||
cy.get('.frappe-control[data-fieldname=link] input').should('have.value', '');
|
||||
cy.wait("@validate_link");
|
||||
cy.get(".frappe-control[data-fieldname=link] input").should("have.value", "");
|
||||
});
|
||||
|
||||
it("should be possible set empty value explicitly", () => {
|
||||
|
|
@ -95,295 +90,246 @@ context('Control Link', () => {
|
|||
|
||||
cy.intercept("POST", "/api/method/frappe.client.validate_link").as("validate_link");
|
||||
|
||||
cy.get(".frappe-control[data-fieldname=link] input")
|
||||
.type(" ", { delay: 100 })
|
||||
.blur();
|
||||
cy.get(".frappe-control[data-fieldname=link] input").type(" ", { delay: 100 }).blur();
|
||||
cy.wait("@validate_link");
|
||||
cy.get(".frappe-control[data-fieldname=link] input").should("have.value", "");
|
||||
cy.window()
|
||||
.its("cur_dialog")
|
||||
.then((dialog) => {
|
||||
expect(dialog.get_value("link")).to.equal('');
|
||||
expect(dialog.get_value("link")).to.equal("");
|
||||
});
|
||||
});
|
||||
|
||||
it('should route to form on arrow click', () => {
|
||||
get_dialog_with_link().as('dialog');
|
||||
it("should route to form on arrow click", () => {
|
||||
get_dialog_with_link().as("dialog");
|
||||
|
||||
cy.intercept('POST', '/api/method/frappe.client.validate_link').as('validate_link');
|
||||
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
|
||||
cy.intercept("POST", "/api/method/frappe.client.validate_link").as("validate_link");
|
||||
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
|
||||
|
||||
cy.get('@todos').then(todos => {
|
||||
cy.get('.frappe-control[data-fieldname=link] input').as('input');
|
||||
cy.get('@input').focus();
|
||||
cy.wait('@search_link');
|
||||
cy.get('@input').type(todos[0]).blur();
|
||||
cy.wait('@validate_link');
|
||||
cy.get('@input').focus();
|
||||
cy.get("@todos").then((todos) => {
|
||||
cy.get(".frappe-control[data-fieldname=link] input").as("input");
|
||||
cy.get("@input").focus();
|
||||
cy.wait("@search_link");
|
||||
cy.get("@input").type(todos[0]).blur();
|
||||
cy.wait("@validate_link");
|
||||
cy.get("@input").focus();
|
||||
cy.wait(500); // wait for arrow to show
|
||||
cy.get('.frappe-control[data-fieldname=link] .btn-open')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.location('pathname').should('eq', `/app/todo/${todos[0]}`);
|
||||
cy.get(".frappe-control[data-fieldname=link] .btn-open").should("be.visible").click();
|
||||
cy.location("pathname").should("eq", `/app/todo/${todos[0]}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('show title field in link', () => {
|
||||
|
||||
cy.insert_doc("Property Setter", {
|
||||
"doctype": "Property Setter",
|
||||
"doc_type": "User",
|
||||
"property": "translate_link_fields",
|
||||
"property_type": "Check",
|
||||
"doctype_or_field": "DocType",
|
||||
"value": "0"
|
||||
}, true);
|
||||
|
||||
cy.insert_doc("Property Setter", {
|
||||
"doctype": "Property Setter",
|
||||
"doc_type": "ToDo",
|
||||
"property": "show_title_field_in_link",
|
||||
"property_type": "Check",
|
||||
"doctype_or_field": "DocType",
|
||||
"value": "1"
|
||||
}, true);
|
||||
it("show title field in link", () => {
|
||||
cy.insert_doc(
|
||||
"Property Setter",
|
||||
{
|
||||
doctype: "Property Setter",
|
||||
doc_type: "ToDo",
|
||||
property: "show_title_field_in_link",
|
||||
property_type: "Check",
|
||||
doctype_or_field: "DocType",
|
||||
value: "1",
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
cy.clear_cache();
|
||||
cy.wait(500);
|
||||
|
||||
get_dialog_with_link().as('dialog');
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
if (!frappe.boot) {
|
||||
frappe.boot = {
|
||||
link_title_doctypes: ['ToDo']
|
||||
};
|
||||
} else {
|
||||
frappe.boot.link_title_doctypes = ['ToDo'];
|
||||
}
|
||||
});
|
||||
get_dialog_with_link().as("dialog");
|
||||
cy.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
if (!frappe.boot) {
|
||||
frappe.boot = {
|
||||
link_title_doctypes: ["ToDo"],
|
||||
};
|
||||
} else {
|
||||
frappe.boot.link_title_doctypes = ["ToDo"];
|
||||
}
|
||||
});
|
||||
|
||||
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
|
||||
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
|
||||
|
||||
cy.get('.frappe-control[data-fieldname=link] input').focus().as('input');
|
||||
cy.wait('@search_link');
|
||||
cy.get('@input').type('todo for link');
|
||||
cy.wait('@search_link');
|
||||
cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible');
|
||||
cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 });
|
||||
cy.get('.frappe-control[data-fieldname=link] input').blur();
|
||||
cy.get('@dialog').then(dialog => {
|
||||
cy.get('@todos').then(todos => {
|
||||
let field = dialog.get_field('link');
|
||||
cy.get(".frappe-control[data-fieldname=link] input").focus().as("input");
|
||||
cy.wait("@search_link");
|
||||
cy.get("@input").type("todo for link");
|
||||
cy.wait("@search_link");
|
||||
cy.get(".frappe-control[data-fieldname=link] ul").should("be.visible");
|
||||
cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 });
|
||||
cy.get(".frappe-control[data-fieldname=link] input").blur();
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
cy.get("@todos").then((todos) => {
|
||||
let field = dialog.get_field("link");
|
||||
let value = field.get_value();
|
||||
let label = field.get_label_value();
|
||||
|
||||
expect(value).to.eq(todos[0]);
|
||||
expect(label).to.eq('this is a test todo for link');
|
||||
expect(label).to.eq("this is a test todo for link");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should update dependant fields (via fetch_from)', () => {
|
||||
cy.get('@todos').then(todos => {
|
||||
it("should update dependant fields (via fetch_from)", () => {
|
||||
cy.get("@todos").then((todos) => {
|
||||
cy.visit(`/app/todo/${todos[0]}`);
|
||||
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
|
||||
cy.intercept('POST', '/api/method/frappe.client.validate_link').as('validate_link');
|
||||
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
|
||||
cy.intercept("POST", "/api/method/frappe.client.validate_link").as("validate_link");
|
||||
|
||||
cy.get('.frappe-control[data-fieldname=assigned_by] input').focus().as('input');
|
||||
cy.get('@input').type('Administrator', {delay: 100}).blur();
|
||||
cy.wait('@validate_link');
|
||||
cy.get('.frappe-control[data-fieldname=assigned_by_full_name] .control-value').should(
|
||||
'contain', 'Administrator'
|
||||
cy.get(".frappe-control[data-fieldname=assigned_by] input").focus().as("input");
|
||||
cy.get("@input").type(cy.config("testUser"), { delay: 100 }).blur();
|
||||
cy.wait("@validate_link");
|
||||
cy.get(".frappe-control[data-fieldname=assigned_by_full_name] .control-value").should(
|
||||
"contain",
|
||||
"Frappe"
|
||||
);
|
||||
|
||||
cy.window()
|
||||
.its("cur_frm.doc.assigned_by")
|
||||
.should("eq", "Administrator");
|
||||
cy.window().its("cur_frm.doc.assigned_by").should("eq", cy.config("testUser"));
|
||||
|
||||
// invalid input
|
||||
cy.get('@input').clear().type('invalid input', {delay: 100}).blur();
|
||||
cy.get('.frappe-control[data-fieldname=assigned_by_full_name] .control-value').should(
|
||||
'contain', ''
|
||||
cy.get("@input").clear().type("invalid input", { delay: 100 }).blur();
|
||||
cy.get(".frappe-control[data-fieldname=assigned_by_full_name] .control-value").should(
|
||||
"contain",
|
||||
""
|
||||
);
|
||||
|
||||
cy.window()
|
||||
.its("cur_frm.doc.assigned_by")
|
||||
.should("eq", null);
|
||||
cy.window().its("cur_frm.doc.assigned_by").should("eq", null);
|
||||
|
||||
// set valid value again
|
||||
cy.get('@input').clear().focus();
|
||||
cy.wait('@search_link');
|
||||
cy.get('@input').type('Administrator', {delay: 100}).blur();
|
||||
cy.wait('@validate_link');
|
||||
cy.get("@input").clear().focus();
|
||||
cy.wait("@search_link");
|
||||
cy.get("@input").type(cy.config("testUser"), { delay: 100 }).blur();
|
||||
cy.wait("@validate_link");
|
||||
|
||||
cy.window()
|
||||
.its("cur_frm.doc.assigned_by")
|
||||
.should("eq", "Administrator");
|
||||
cy.window().its("cur_frm.doc.assigned_by").should("eq", cy.config("testUser"));
|
||||
|
||||
// clear input
|
||||
cy.get('@input').clear().blur();
|
||||
cy.get('.frappe-control[data-fieldname=assigned_by_full_name] .control-value').should(
|
||||
'contain', ''
|
||||
cy.get("@input").clear().blur();
|
||||
cy.get(".frappe-control[data-fieldname=assigned_by_full_name] .control-value").should(
|
||||
"contain",
|
||||
""
|
||||
);
|
||||
|
||||
cy.window()
|
||||
.its("cur_frm.doc.assigned_by")
|
||||
.should("eq", "");
|
||||
cy.window().its("cur_frm.doc.assigned_by").should("eq", "");
|
||||
});
|
||||
});
|
||||
|
||||
it("should set default values", () => {
|
||||
cy.insert_doc("Property Setter", {
|
||||
"doctype_or_field": "DocField",
|
||||
"doc_type": "ToDo",
|
||||
"field_name": "assigned_by",
|
||||
"property": "default",
|
||||
"property_type": "Text",
|
||||
"value": "Administrator"
|
||||
}, true);
|
||||
cy.insert_doc(
|
||||
"Property Setter",
|
||||
{
|
||||
doctype_or_field: "DocField",
|
||||
doc_type: "ToDo",
|
||||
field_name: "assigned_by",
|
||||
property: "default",
|
||||
property_type: "Text",
|
||||
value: "Administrator",
|
||||
},
|
||||
true
|
||||
);
|
||||
cy.reload();
|
||||
cy.new_form("ToDo");
|
||||
cy.fill_field("description", "new", "Text Editor");
|
||||
cy.intercept("POST", "/api/method/frappe.desk.form.save.savedocs").as("save_form");
|
||||
cy.findByRole("button", {name: "Save"}).click();
|
||||
cy.wait("@save_form");
|
||||
cy.fill_field("description", "new", "Text Editor").wait(200);
|
||||
cy.save();
|
||||
cy.get(".frappe-control[data-fieldname=assigned_by_full_name] .control-value").should(
|
||||
"contain", "Administrator"
|
||||
"contain",
|
||||
"Administrator"
|
||||
);
|
||||
// if user clears default value explicitly, system should not reset default again
|
||||
cy.get_field("assigned_by").clear().blur();
|
||||
cy.intercept("POST", "/api/method/frappe.desk.form.save.savedocs").as("save_form");
|
||||
cy.findByRole("button", {name: "Save"}).click();
|
||||
cy.wait("@save_form");
|
||||
cy.save();
|
||||
cy.get_field("assigned_by").should("have.value", "");
|
||||
cy.get(".frappe-control[data-fieldname=assigned_by_full_name] .control-value").should(
|
||||
"contain", ""
|
||||
"contain",
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
it('show translated text for link with show_title_field_in_link enabled', () => {
|
||||
cy.insert_doc("Property Setter", {
|
||||
"doctype": "Property Setter",
|
||||
"doc_type": "ToDo",
|
||||
"property": "translate_link_fields",
|
||||
"property_type": "Check",
|
||||
"doctype_or_field": "DocType",
|
||||
"value": "1"
|
||||
}, true);
|
||||
it("show translated text for Gender link field with language de with input in de", () => {
|
||||
cy.call("frappe.tests.ui_test_helpers.insert_translations").then(() => {
|
||||
cy.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
cy.set_value("User", frappe.user.name, { language: "de" });
|
||||
});
|
||||
|
||||
cy.insert_doc("Property Setter", {
|
||||
"doctype": "Property Setter",
|
||||
"doc_type": "ToDo",
|
||||
"property": "show_title_field_in_link",
|
||||
"property_type": "Check",
|
||||
"doctype_or_field": "DocType",
|
||||
"value": "1"
|
||||
}, true);
|
||||
cy.clear_cache();
|
||||
cy.wait(500);
|
||||
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
cy.insert_doc("Translation", {
|
||||
doctype: "Translation",
|
||||
language: frappe.boot.lang,
|
||||
source_text: "this is a test todo for link",
|
||||
translated_text: "this is a translated test todo for link",
|
||||
});
|
||||
});
|
||||
get_dialog_with_gender_link().as("dialog");
|
||||
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
|
||||
|
||||
cy.clear_cache();
|
||||
cy.wait(500);
|
||||
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
if (!frappe.boot) {
|
||||
frappe.boot = {
|
||||
link_title_doctypes: ['ToDo'],
|
||||
translatable_doctypes: ['ToDo']
|
||||
};
|
||||
} else {
|
||||
frappe.boot.link_title_doctypes = ['ToDo'];
|
||||
frappe.boot.translatable_doctypes = ['ToDo'];
|
||||
}
|
||||
});
|
||||
|
||||
get_dialog_with_link().as('dialog');
|
||||
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
|
||||
|
||||
cy.get('.frappe-control[data-fieldname=link] input').focus().as('input');
|
||||
cy.wait('@search_link');
|
||||
cy.get('@input').type('todo for link', { delay: 100 });
|
||||
cy.wait('@search_link');
|
||||
cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible');
|
||||
cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 });
|
||||
cy.get('.frappe-control[data-fieldname=link] input').blur();
|
||||
cy.get('@dialog').then(dialog => {
|
||||
cy.get('@todos').then(todos => {
|
||||
let field = dialog.get_field('link');
|
||||
cy.get(".frappe-control[data-fieldname=link] input").focus().as("input");
|
||||
cy.wait("@search_link");
|
||||
cy.get("@input").type("Sonstiges", { delay: 100 });
|
||||
cy.wait("@search_link");
|
||||
cy.get(".frappe-control[data-fieldname=link] ul").should("be.visible");
|
||||
cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 });
|
||||
cy.get(".frappe-control[data-fieldname=link] input").blur();
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let field = dialog.get_field("link");
|
||||
let value = field.get_value();
|
||||
let label = field.get_label_value();
|
||||
|
||||
expect(value).to.eq(todos[0]);
|
||||
expect(label).to.eq('this is a translated test todo for link');
|
||||
expect(value).to.eq("Other");
|
||||
expect(label).to.eq("Sonstiges");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('show translated text for link with show_title_field_in_link disabled', () => {
|
||||
cy.insert_doc("Property Setter", {
|
||||
"doctype": "Property Setter",
|
||||
"doc_type": "User",
|
||||
"property": "translate_link_fields",
|
||||
"property_type": "Check",
|
||||
"doctype_or_field": "DocType",
|
||||
"value": "1"
|
||||
}, true);
|
||||
|
||||
cy.insert_doc("Property Setter", {
|
||||
"doctype": "Property Setter",
|
||||
"doc_type": "ToDo",
|
||||
"property": "show_title_field_in_link",
|
||||
"property_type": "Check",
|
||||
"doctype_or_field": "DocType",
|
||||
"value": "0"
|
||||
}, true);
|
||||
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
cy.insert_doc("Translation", {
|
||||
doctype: "Translation",
|
||||
language: frappe.boot.lang,
|
||||
source_text: "test@erpnext.com",
|
||||
translated_text: "translatedtest@erpnext.com",
|
||||
it("show text for Gender link field with language en", () => {
|
||||
cy.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
cy.set_value("User", frappe.user.name, { language: "en" });
|
||||
});
|
||||
});
|
||||
|
||||
cy.clear_cache();
|
||||
cy.wait(500);
|
||||
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
if (!frappe.boot) {
|
||||
frappe.boot = {
|
||||
translatable_doctypes: ['User']
|
||||
};
|
||||
} else {
|
||||
frappe.boot.translatable_doctypes = ['User'];
|
||||
}
|
||||
});
|
||||
get_dialog_with_gender_link().as("dialog");
|
||||
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
|
||||
|
||||
get_dialog_with_user_link().as('dialog');
|
||||
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
|
||||
|
||||
cy.get('.frappe-control[data-fieldname=link] input').focus().as('input');
|
||||
cy.wait('@search_link');
|
||||
cy.get('@input').type('test@erpnext.com', { delay: 100 });
|
||||
cy.wait('@search_link');
|
||||
cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible');
|
||||
cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 });
|
||||
cy.get('.frappe-control[data-fieldname=link] input').blur();
|
||||
cy.get('@dialog').then(dialog => {
|
||||
let field = dialog.get_field('link');
|
||||
cy.get(".frappe-control[data-fieldname=link] input").focus().as("input");
|
||||
cy.wait("@search_link");
|
||||
cy.get("@input").type("Non-Conforming", { delay: 100 });
|
||||
cy.wait("@search_link");
|
||||
cy.get(".frappe-control[data-fieldname=link] ul").should("be.visible");
|
||||
cy.get(".frappe-control[data-fieldname=link] input").type("{enter}", { delay: 100 });
|
||||
cy.get(".frappe-control[data-fieldname=link] input").blur();
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let field = dialog.get_field("link");
|
||||
let value = field.get_value();
|
||||
let label = field.get_label_value();
|
||||
|
||||
expect(value).to.eq('test@erpnext.com');
|
||||
expect(label).to.eq('translatedtest@erpnext.com');
|
||||
expect(value).to.eq("Non-Conforming");
|
||||
expect(label).to.eq("Non-Conforming");
|
||||
});
|
||||
});
|
||||
|
||||
it("show custom link option", () => {
|
||||
cy.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
frappe.ui.form.ControlLink.link_options = (link) => {
|
||||
return [
|
||||
{
|
||||
html:
|
||||
"<span class='text-primary custom-link-option'>" +
|
||||
"<i class='fa fa-search' style='margin-right: 5px;'></i> " +
|
||||
"Custom Link Option" +
|
||||
"</span>",
|
||||
label: "Custom Link Option",
|
||||
value: "custom__link_option",
|
||||
action: () => {},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
get_dialog_with_link().as("dialog");
|
||||
cy.get(".frappe-control[data-fieldname=link] input").focus().as("input");
|
||||
cy.get("@input").type("custom", { delay: 100 });
|
||||
cy.get(".custom-link-option").should("be.visible");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ context("Control Markdown Editor", () => {
|
|||
it("should allow inserting images by drag and drop", () => {
|
||||
cy.visit("/app/web-page/new");
|
||||
cy.fill_field("content_type", "Markdown", "Select");
|
||||
cy.get_field("main_section_md", "Markdown Editor").attachFile(
|
||||
"sample_image.jpg",
|
||||
cy.get_field("main_section_md", "Markdown Editor").selectFile(
|
||||
"cypress/fixtures/sample_image.jpg",
|
||||
{
|
||||
subjectType: "drag-n-drop"
|
||||
action: "drag-drop",
|
||||
}
|
||||
);
|
||||
cy.click_modal_primary_button("Upload");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import doctype_with_phone from '../fixtures/doctype_with_phone';
|
||||
import doctype_with_phone from "../fixtures/doctype_with_phone";
|
||||
|
||||
context("Control Phone", () => {
|
||||
before(() => {
|
||||
|
|
@ -9,10 +9,12 @@ context("Control Phone", () => {
|
|||
function get_dialog_with_phone() {
|
||||
return cy.dialog({
|
||||
title: "Phone",
|
||||
fields: [{
|
||||
"fieldname": "phone",
|
||||
"fieldtype": "Phone",
|
||||
}]
|
||||
fields: [
|
||||
{
|
||||
fieldname: "phone",
|
||||
fieldtype: "Phone",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -27,18 +29,16 @@ context("Control Phone", () => {
|
|||
|
||||
let phone_number = "9312672712";
|
||||
cy.get(".selected-phone > img").click().first();
|
||||
cy.get_field("phone")
|
||||
.first()
|
||||
.click({multiple: true});
|
||||
cy.get_field("phone").first().click({ multiple: true });
|
||||
cy.get(".frappe-control[data-fieldname=phone]")
|
||||
.findByRole("textbox")
|
||||
.first()
|
||||
.type(phone_number, {force: true});
|
||||
.type(phone_number, { force: true });
|
||||
|
||||
cy.get_field("phone").first().should("have.value", phone_number);
|
||||
cy.get_field("phone").first().blur({force: true});
|
||||
cy.get_field("phone").first().blur({ force: true });
|
||||
cy.wait(100);
|
||||
cy.get("@dialog").then(dialog => {
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
let value = dialog.get_value("phone");
|
||||
expect(value).to.equal("+91-" + phone_number);
|
||||
});
|
||||
|
|
@ -48,10 +48,12 @@ context("Control Phone", () => {
|
|||
let search_text = "india";
|
||||
cy.get(".selected-phone").click().first();
|
||||
cy.get(".phone-picker").findByRole("searchbox").click().type(search_text);
|
||||
cy.get(".phone-section .phone-wrapper:not(.hidden)").then(i => {
|
||||
cy.get(`.phone-section .phone-wrapper[id*="${search_text.toLowerCase()}"]`).then(countries => {
|
||||
expect(i.length).to.equal(countries.length);
|
||||
});
|
||||
cy.get(".phone-section .phone-wrapper:not(.hidden)").then((i) => {
|
||||
cy.get(`.phone-section .phone-wrapper[id*="${search_text.toLowerCase()}"]`).then(
|
||||
(countries) => {
|
||||
expect(i.length).to.equal(countries.length);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(".phone-picker").findByRole("searchbox").clear().blur();
|
||||
|
|
|
|||
|
|
@ -1,56 +1,54 @@
|
|||
context('Control Rating', () => {
|
||||
context("Control Rating", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_rating() {
|
||||
return cy.dialog({
|
||||
title: 'Rating',
|
||||
fields: [{
|
||||
'fieldname': 'rate',
|
||||
'fieldtype': 'Rating',
|
||||
'options': 7
|
||||
}]
|
||||
title: "Rating",
|
||||
fields: [
|
||||
{
|
||||
fieldname: "rate",
|
||||
fieldtype: "Rating",
|
||||
options: 7,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('click on the star rating to record value', () => {
|
||||
get_dialog_with_rating().as('dialog');
|
||||
it("click on the star rating to record value", () => {
|
||||
get_dialog_with_rating().as("dialog");
|
||||
|
||||
cy.get('div.rating')
|
||||
.children('svg')
|
||||
.find('.right-half')
|
||||
cy.get("div.rating")
|
||||
.children("svg")
|
||||
.find(".right-half")
|
||||
.first()
|
||||
.click()
|
||||
.should('have.class', 'star-click');
|
||||
cy.get('@dialog').then(dialog => {
|
||||
var value = dialog.get_value('rate');
|
||||
expect(value).to.equal(1/7);
|
||||
.should("have.class", "star-click");
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
var value = dialog.get_value("rate");
|
||||
expect(value).to.equal(1 / 7);
|
||||
dialog.hide();
|
||||
});
|
||||
});
|
||||
|
||||
it('hover on the star', () => {
|
||||
it("hover on the star", () => {
|
||||
get_dialog_with_rating();
|
||||
|
||||
cy.get('div.rating')
|
||||
.children('svg')
|
||||
.find('.right-half')
|
||||
cy.get("div.rating")
|
||||
.children("svg")
|
||||
.find(".right-half")
|
||||
.first()
|
||||
.invoke('trigger', 'mouseenter')
|
||||
.should('have.class', 'star-hover')
|
||||
.invoke('trigger', 'mouseleave')
|
||||
.should('not.have.class', 'star-hover');
|
||||
.invoke("trigger", "mouseenter")
|
||||
.should("have.class", "star-hover")
|
||||
.invoke("trigger", "mouseleave")
|
||||
.should("not.have.class", "star-hover");
|
||||
});
|
||||
|
||||
it('check number of stars in rating', () => {
|
||||
it("check number of stars in rating", () => {
|
||||
get_dialog_with_rating();
|
||||
|
||||
cy.get('div.rating')
|
||||
.first()
|
||||
.children('svg')
|
||||
.should('have.length', 7);
|
||||
cy.get("div.rating").first().children("svg").should("have.length", 7);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,37 +1,40 @@
|
|||
context('Control Select', () => {
|
||||
context("Control Select", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_select() {
|
||||
return cy.dialog({
|
||||
title: 'Select',
|
||||
fields: [{
|
||||
'fieldname': 'select_control',
|
||||
'fieldtype': 'Select',
|
||||
'placeholder': 'Select an Option',
|
||||
'options': ['', 'Option 1', 'Option 2', 'Option 2'],
|
||||
}]
|
||||
title: "Select",
|
||||
fields: [
|
||||
{
|
||||
fieldname: "select_control",
|
||||
fieldtype: "Select",
|
||||
placeholder: "Select an Option",
|
||||
options: ["", "Option 1", "Option 2", "Option 2"],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
it('toggles placholder on clicking an option', () => {
|
||||
get_dialog_with_select().as('dialog');
|
||||
it("toggles placholder on clicking an option", () => {
|
||||
get_dialog_with_select().as("dialog");
|
||||
|
||||
cy.get('.frappe-control[data-fieldname=select_control] .control-input').as('control');
|
||||
cy.get('.frappe-control[data-fieldname=select_control] .control-input select').as('select');
|
||||
cy.get('@control').get('.select-icon').should('exist');
|
||||
cy.get('@control').get('.placeholder').should('have.css', 'display', 'block');
|
||||
cy.get('@select').select('Option 1');
|
||||
cy.findByDisplayValue('Option 1').should('exist');
|
||||
cy.get('@control').get('.placeholder').should('have.css', 'display', 'none');
|
||||
cy.get('@select').invoke('val', '');
|
||||
cy.findByDisplayValue('Option 1').should('not.exist');
|
||||
cy.get('@control').get('.placeholder').should('have.css', 'display', 'block');
|
||||
cy.get(".frappe-control[data-fieldname=select_control] .control-input").as("control");
|
||||
cy.get(".frappe-control[data-fieldname=select_control] .control-input select").as(
|
||||
"select"
|
||||
);
|
||||
cy.get("@control").get(".select-icon").should("exist");
|
||||
cy.get("@control").get(".placeholder").should("have.css", "display", "block");
|
||||
cy.get("@select").select("Option 1");
|
||||
cy.findByDisplayValue("Option 1").should("exist");
|
||||
cy.get("@control").get(".placeholder").should("have.css", "display", "none");
|
||||
cy.get("@select").invoke("val", "");
|
||||
cy.findByDisplayValue("Option 1").should("not.exist");
|
||||
cy.get("@control").get(".placeholder").should("have.css", "display", "block");
|
||||
|
||||
|
||||
cy.get('@dialog').then(dialog => {
|
||||
cy.get("@dialog").then((dialog) => {
|
||||
dialog.hide();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,10 +31,7 @@ const check_button_count = (label, group = "TestGroup") => {
|
|||
.should("be.visible");
|
||||
|
||||
//reset viewport
|
||||
cy.viewport(
|
||||
Cypress.config("viewportWidth"),
|
||||
Cypress.config("viewportHeight")
|
||||
);
|
||||
cy.viewport(Cypress.config("viewportWidth"), Cypress.config("viewportHeight"));
|
||||
};
|
||||
|
||||
describe(
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
context('Customize Form', () => {
|
||||
context("Customize Form", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/customize-form');
|
||||
cy.visit("/app/customize-form");
|
||||
});
|
||||
it('Changing to naming rule should update autoname', () => {
|
||||
it("Changing to naming rule should update autoname", () => {
|
||||
cy.fill_field("doc_type", "ToDo", "Link").blur();
|
||||
cy.click_form_section("Naming");
|
||||
const naming_rule_default_autoname_map = {
|
||||
"Set by user": "prompt",
|
||||
"By fieldname": "field:",
|
||||
'By "Naming Series" field': "naming_series:",
|
||||
"Expression": "format:",
|
||||
Expression: "format:",
|
||||
"Expression (old style)": "",
|
||||
"Random": "hash",
|
||||
"By script": ""
|
||||
Random: "hash",
|
||||
"By script": "",
|
||||
};
|
||||
Cypress._.forOwn(naming_rule_default_autoname_map, (value, naming_rule) => {
|
||||
cy.fill_field("naming_rule", naming_rule, "Select");
|
||||
|
|
|
|||
50
cypress/integration/dashboard.js
Normal file
50
cypress/integration/dashboard.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
describe("Dashboard view", { scrollBehavior: false }, () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
});
|
||||
|
||||
it("should load", () => {
|
||||
const chart = "TODO-YEARLY-TRENDS";
|
||||
const dashboard = "TODO-TEST-DASHBOARD"; // check slash in name intentionally.
|
||||
|
||||
cy.insert_doc(
|
||||
"Dashboard Chart",
|
||||
{
|
||||
is_standard: 0,
|
||||
chart_name: chart,
|
||||
chart_type: "Count",
|
||||
document_type: "ToDo",
|
||||
parent_document_type: "",
|
||||
based_on: "creation",
|
||||
group_by_type: "Count",
|
||||
timespan: "Last Year",
|
||||
time_interval: "Yearly",
|
||||
timeseries: 1,
|
||||
type: "Line",
|
||||
filters_json: "[]",
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
cy.insert_doc(
|
||||
"Dashboard",
|
||||
{
|
||||
name: dashboard,
|
||||
dashboard_name: dashboard,
|
||||
is_standard: 0,
|
||||
charts: [
|
||||
{
|
||||
chart: chart,
|
||||
},
|
||||
],
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
cy.visit(`/app/dashboard-view/${dashboard}`);
|
||||
|
||||
// expect chart to be loaded
|
||||
cy.findByText(chart).should("be.visible");
|
||||
});
|
||||
});
|
||||
|
|
@ -1,22 +1,22 @@
|
|||
context('Dashboard Chart', () => {
|
||||
context("Dashboard Chart", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
it('Check filter populate for child table doctype', () => {
|
||||
cy.visit('/app/dashboard-chart/new-dashboard-chart-1');
|
||||
cy.get('[data-fieldname="parent_document_type"]').should('have.css', 'display', 'none');
|
||||
it("Check filter populate for child table doctype", () => {
|
||||
cy.visit("/app/dashboard-chart/new-dashboard-chart-1");
|
||||
cy.get('[data-fieldname="parent_document_type"]').should("have.css", "display", "none");
|
||||
|
||||
cy.get_field('document_type', 'Link');
|
||||
cy.fill_field('document_type', 'Workspace Link', 'Link').focus().blur();
|
||||
cy.get_field('document_type', 'Link').should('have.value', 'Workspace Link');
|
||||
cy.get_field("document_type", "Link");
|
||||
cy.fill_field("document_type", "Workspace Link", "Link").focus().blur();
|
||||
cy.get_field("document_type", "Link").should("have.value", "Workspace Link");
|
||||
|
||||
cy.fill_field('chart_name', 'Test Chart', 'Data');
|
||||
cy.fill_field("chart_name", "Test Chart", "Data");
|
||||
|
||||
cy.get('[data-fieldname="filters_json"]').click().wait(200);
|
||||
cy.get('.modal-body .filter-action-buttons .add-filter').click();
|
||||
cy.get('.modal-body .fieldname-select-area').click();
|
||||
cy.get('.modal-actions .btn-modal-close').click();
|
||||
cy.get(".modal-body .filter-action-buttons .add-filter").click();
|
||||
cy.get(".modal-body .fieldname-select-area").click();
|
||||
cy.get(".modal-actions .btn-modal-close").click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,91 +1,94 @@
|
|||
import doctype_with_child_table from '../fixtures/doctype_with_child_table';
|
||||
import child_table_doctype from '../fixtures/child_table_doctype';
|
||||
import child_table_doctype_1 from '../fixtures/child_table_doctype_1';
|
||||
import doctype_to_link from '../fixtures/doctype_to_link';
|
||||
import doctype_with_child_table from "../fixtures/doctype_with_child_table";
|
||||
import child_table_doctype from "../fixtures/child_table_doctype";
|
||||
import child_table_doctype_1 from "../fixtures/child_table_doctype_1";
|
||||
import doctype_to_link from "../fixtures/doctype_to_link";
|
||||
const doctype_to_link_name = doctype_to_link.name;
|
||||
const child_table_doctype_name = child_table_doctype.name;
|
||||
|
||||
context('Dashboard links', () => {
|
||||
context("Dashboard links", () => {
|
||||
before(() => {
|
||||
cy.visit('/login');
|
||||
cy.login();
|
||||
cy.insert_doc('DocType', child_table_doctype, true);
|
||||
cy.insert_doc('DocType', child_table_doctype_1, true);
|
||||
cy.insert_doc('DocType', doctype_with_child_table, true);
|
||||
cy.insert_doc('DocType', doctype_to_link, true);
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.xcall("frappe.tests.ui_test_helpers.update_child_table", {
|
||||
name: child_table_doctype_name
|
||||
cy.visit("/login");
|
||||
cy.login("Administrator");
|
||||
cy.insert_doc("DocType", child_table_doctype, true);
|
||||
cy.insert_doc("DocType", child_table_doctype_1, true);
|
||||
cy.insert_doc("DocType", doctype_with_child_table, true);
|
||||
cy.insert_doc("DocType", doctype_to_link, true);
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
frappe.call("frappe.tests.ui_test_helpers.update_child_table", {
|
||||
name: child_table_doctype_name,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Adding a new contact, checking for the counter on the dashboard and deleting the created contact', () => {
|
||||
cy.visit('/app/contact');
|
||||
it("Adding a new contact, checking for the counter on the dashboard and deleting the created contact", () => {
|
||||
cy.visit("/app/contact");
|
||||
cy.clear_filters();
|
||||
|
||||
cy.visit('/app/user');
|
||||
cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click({ force: true });
|
||||
cy.visit(`/app/user/${cy.config("testUser")}`);
|
||||
|
||||
//To check if initially the dashboard contains only the "Contact" link and there is no counter
|
||||
cy.get('[data-doctype="Contact"]').should('contain', 'Contact');
|
||||
cy.select_form_tab("Connections");
|
||||
cy.get('[data-doctype="Contact"]').should("contain", "Contact");
|
||||
|
||||
//Adding a new contact
|
||||
cy.get('.document-link-badge[data-doctype="Contact"]').click();
|
||||
cy.wait(300);
|
||||
cy.findByRole('button', {name: 'Add Contact'}).should('be.visible');
|
||||
cy.findByRole('button', {name: 'Add Contact'}).click();
|
||||
cy.get('[data-doctype="Contact"][data-fieldname="first_name"]').type('Admin');
|
||||
cy.findByRole('button', {name: 'Save'}).click();
|
||||
cy.visit('/app/user');
|
||||
cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click({ force: true });
|
||||
cy.findByRole("button", { name: "Add Contact" }).should("be.visible");
|
||||
cy.findByRole("button", { name: "Add Contact" }).click();
|
||||
cy.get('[data-doctype="Contact"][data-fieldname="first_name"]').type("Admin");
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
cy.visit(`/app/user/${cy.config("testUser")}`);
|
||||
|
||||
//To check if the counter for contact doc is "1" after adding the contact
|
||||
cy.get('[data-doctype="Contact"] > .count').should('contain', '1');
|
||||
cy.get('[data-doctype="Contact"]').contains('Contact').click();
|
||||
//To check if the counter for contact doc is "2" after adding additional contact
|
||||
cy.select_form_tab("Connections");
|
||||
cy.get('[data-doctype="Contact"] > .count').should("contain", "2");
|
||||
cy.get('[data-doctype="Contact"]').contains("Contact").click();
|
||||
|
||||
//Deleting the newly created contact
|
||||
cy.visit('/app/contact');
|
||||
cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click({ force: true });
|
||||
cy.findByRole('button', {name: 'Actions'}).click();
|
||||
cy.visit("/app/contact");
|
||||
cy.get(".list-subject > .select-like > .list-row-checkbox").eq(0).click({ force: true });
|
||||
cy.findByRole("button", { name: "Actions" }).click();
|
||||
cy.get('.actions-btn-group [data-label="Delete"]').click();
|
||||
cy.findByRole('button', {name: 'Yes'}).click({delay: 700});
|
||||
|
||||
cy.findByRole("button", { name: "Yes" }).click({ delay: 700 });
|
||||
|
||||
//To check if the counter from the "Contact" doc link is removed
|
||||
cy.wait(700);
|
||||
cy.visit('/app/user');
|
||||
cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click({ force: true });
|
||||
cy.get('[data-doctype="Contact"]').should('contain', 'Contact');
|
||||
cy.visit("/app/user");
|
||||
cy.get(".list-row-col > .level-item > .ellipsis").eq(0).click({ force: true });
|
||||
cy.get('[data-doctype="Contact"]').should("contain", "Contact");
|
||||
});
|
||||
|
||||
it('Report link in dashboard', () => {
|
||||
cy.visit('/app/user');
|
||||
cy.visit('/app/user/Administrator');
|
||||
cy.get('[data-doctype="Contact"]').should('contain', 'Contact');
|
||||
cy.findByText('Connections');
|
||||
it("Report link in dashboard", () => {
|
||||
cy.visit(`/app/user/${cy.config("testUser")}`);
|
||||
cy.select_form_tab("Connections");
|
||||
cy.get('.document-link[data-doctype="Contact"]').contains("Contact");
|
||||
cy.window()
|
||||
.its('cur_frm')
|
||||
.then(cur_frm => {
|
||||
.its("cur_frm")
|
||||
.then((cur_frm) => {
|
||||
cur_frm.dashboard.data.reports = [
|
||||
{
|
||||
'label': 'Reports',
|
||||
'items': ['Website Analytics']
|
||||
}
|
||||
label: "Reports",
|
||||
items: ["Website Analytics"],
|
||||
},
|
||||
];
|
||||
cur_frm.dashboard.render_report_links();
|
||||
cy.get('[data-report="Website Analytics"]').contains('Website Analytics').click();
|
||||
cy.findByText('Website Analytics');
|
||||
cy.get('.document-link[data-report="Website Analytics"]')
|
||||
.contains("Website Analytics")
|
||||
.click();
|
||||
});
|
||||
});
|
||||
|
||||
it('check if child table is populated with linked field on creation from dashboard link', () => {
|
||||
it("check if child table is populated with linked field on creation from dashboard link", () => {
|
||||
cy.new_form(doctype_to_link_name);
|
||||
cy.fill_field("title", "Test Linking");
|
||||
cy.findByRole("button", {name: "Save"}).click();
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
|
||||
cy.get('.document-link .btn-new').click();
|
||||
cy.get('.frappe-control[data-fieldname="child_table"] .rows .data-row .col[data-fieldname="doctype_to_link"]')
|
||||
.should('contain.text', 'Test Linking');
|
||||
cy.get(".document-link .btn-new").click();
|
||||
cy.get(
|
||||
'.frappe-control[data-fieldname="child_table"] .rows .data-row .col[data-fieldname="doctype_to_link"]'
|
||||
).should("contain.text", "Test Linking");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,43 +1,45 @@
|
|||
import data_field_validation_doctype from '../fixtures/data_field_validation_doctype';
|
||||
import data_field_validation_doctype from "../fixtures/data_field_validation_doctype";
|
||||
const doctype_name = data_field_validation_doctype.name;
|
||||
|
||||
|
||||
context('Data Field Input Validation in New Form', () => {
|
||||
context("Data Field Input Validation in New Form", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
return cy.insert_doc('DocType', data_field_validation_doctype, true);
|
||||
cy.visit("/app/website");
|
||||
return cy.insert_doc("DocType", data_field_validation_doctype, true);
|
||||
});
|
||||
|
||||
function validateField(fieldname, invalid_value, valid_value) {
|
||||
// Invalid, should have has-error class
|
||||
cy.get_field(fieldname).clear().type(invalid_value).blur();
|
||||
cy.get(`.frappe-control[data-fieldname="${fieldname}"]`).should('have.class', 'has-error');
|
||||
cy.get(`.frappe-control[data-fieldname="${fieldname}"]`).should("have.class", "has-error");
|
||||
// Valid value, should not have has-error class
|
||||
cy.get_field(fieldname).clear().type(valid_value);
|
||||
cy.get(`.frappe-control[data-fieldname="${fieldname}"]`).should('not.have.class', 'has-error');
|
||||
cy.get(`.frappe-control[data-fieldname="${fieldname}"]`).should(
|
||||
"not.have.class",
|
||||
"has-error"
|
||||
);
|
||||
}
|
||||
|
||||
describe('Data Field Options', () => {
|
||||
it('should validate email address', () => {
|
||||
describe("Data Field Options", () => {
|
||||
it("should validate email address", () => {
|
||||
cy.new_form(doctype_name);
|
||||
validateField('email', 'captian', 'hello@test.com');
|
||||
validateField("email", "captian", "hello@test.com");
|
||||
});
|
||||
|
||||
it('should validate URL', () => {
|
||||
validateField('url', 'jkl', 'https://frappe.io');
|
||||
validateField('url', 'abcd.com', 'http://google.com/home');
|
||||
validateField('url', '&&http://google.uae', 'gopher://frappe.io');
|
||||
validateField('url', 'ftt2:://google.in?q=news', 'ftps2://frappe.io/__/#home');
|
||||
validateField('url', 'ftt2://', 'ntps://localhost'); // For intranet URLs
|
||||
it("should validate URL", () => {
|
||||
validateField("url", "jkl", "https://frappe.io");
|
||||
validateField("url", "abcd.com", "http://google.com/home");
|
||||
validateField("url", "&&http://google.uae", "gopher://frappe.io");
|
||||
validateField("url", "ftt2:://google.in?q=news", "ftps2://frappe.io/__/#home");
|
||||
validateField("url", "ftt2://", "ntps://localhost"); // For intranet URLs
|
||||
});
|
||||
|
||||
it('should validate phone number', () => {
|
||||
validateField('phone', 'america', '89787878');
|
||||
it("should validate phone number", () => {
|
||||
validateField("phone", "america", "89787878");
|
||||
});
|
||||
|
||||
it('should validate name', () => {
|
||||
validateField('person_name', ' 777Hello', 'James Bond');
|
||||
it("should validate name", () => {
|
||||
validateField("person_name", " 777Hello", "James Bond");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,53 +1,52 @@
|
|||
import datetime_doctype from '../fixtures/datetime_doctype';
|
||||
import datetime_doctype from "../fixtures/datetime_doctype";
|
||||
const doctype_name = datetime_doctype.name;
|
||||
|
||||
context('Control Date, Time and DateTime', () => {
|
||||
context("Control Date, Time and DateTime", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
return cy.insert_doc('DocType', datetime_doctype, true);
|
||||
cy.visit("/app/website");
|
||||
return cy.insert_doc("DocType", datetime_doctype, true);
|
||||
});
|
||||
|
||||
describe('Date formats', () => {
|
||||
describe("Date formats", () => {
|
||||
let date_formats = [
|
||||
{
|
||||
date_format: 'dd-mm-yyyy',
|
||||
date_format: "dd-mm-yyyy",
|
||||
part: 2,
|
||||
length: 4,
|
||||
separator: '-'
|
||||
separator: "-",
|
||||
},
|
||||
{
|
||||
date_format: 'mm/dd/yyyy',
|
||||
date_format: "mm/dd/yyyy",
|
||||
part: 0,
|
||||
length: 2,
|
||||
separator: '/'
|
||||
}
|
||||
separator: "/",
|
||||
},
|
||||
];
|
||||
|
||||
date_formats.forEach(d => {
|
||||
it('test date format ' + d.date_format, () => {
|
||||
cy.set_value('System Settings', 'System Settings', {
|
||||
date_format: d.date_format
|
||||
date_formats.forEach((d) => {
|
||||
it("test date format " + d.date_format, () => {
|
||||
cy.set_value("System Settings", "System Settings", {
|
||||
date_format: d.date_format,
|
||||
});
|
||||
cy.window()
|
||||
.its('frappe')
|
||||
.then(frappe => {
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
// update sys_defaults value to avoid a reload
|
||||
frappe.sys_defaults.date_format = d.date_format;
|
||||
});
|
||||
|
||||
cy.new_form(doctype_name);
|
||||
cy.get('.form-control[data-fieldname=date]').focus();
|
||||
cy.get('.datepickers-container .datepicker.active')
|
||||
.should('be.visible');
|
||||
cy.get(".form-control[data-fieldname=date]").focus();
|
||||
cy.get(".datepickers-container .datepicker.active").should("be.visible");
|
||||
cy.get(
|
||||
'.datepickers-container .datepicker.active .datepicker--cell-day.-current-'
|
||||
".datepickers-container .datepicker.active .datepicker--cell-day.-current-"
|
||||
).click({ force: true });
|
||||
|
||||
cy.window()
|
||||
.its('cur_frm')
|
||||
.then(cur_frm => {
|
||||
let formatted_value = cur_frm.get_field('date').input.value;
|
||||
.its("cur_frm")
|
||||
.then((cur_frm) => {
|
||||
let formatted_value = cur_frm.get_field("date").input.value;
|
||||
let parts = formatted_value.split(d.separator);
|
||||
expect(parts[d.part].length).to.equal(d.length);
|
||||
});
|
||||
|
|
@ -55,74 +54,72 @@ context('Control Date, Time and DateTime', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Time formats', () => {
|
||||
describe("Time formats", () => {
|
||||
let time_formats = [
|
||||
{
|
||||
time_format: 'HH:mm:ss',
|
||||
value: ' 11:00:12',
|
||||
match_value: '11:00:12'
|
||||
time_format: "HH:mm:ss",
|
||||
value: " 11:00:12",
|
||||
match_value: "11:00:12",
|
||||
},
|
||||
{
|
||||
time_format: 'HH:mm',
|
||||
value: ' 11:00:12',
|
||||
match_value: '11:00'
|
||||
}
|
||||
time_format: "HH:mm",
|
||||
value: " 11:00:12",
|
||||
match_value: "11:00",
|
||||
},
|
||||
];
|
||||
|
||||
time_formats.forEach(d => {
|
||||
it('test time format ' + d.time_format, () => {
|
||||
cy.set_value('System Settings', 'System Settings', {
|
||||
time_format: d.time_format
|
||||
time_formats.forEach((d) => {
|
||||
it("test time format " + d.time_format, () => {
|
||||
cy.set_value("System Settings", "System Settings", {
|
||||
time_format: d.time_format,
|
||||
});
|
||||
cy.window()
|
||||
.its('frappe')
|
||||
.then(frappe => {
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
frappe.sys_defaults.time_format = d.time_format;
|
||||
});
|
||||
cy.new_form(doctype_name);
|
||||
cy.fill_field('time', d.value, 'Time').blur();
|
||||
cy.get_field('time').should('have.value', d.match_value);
|
||||
cy.fill_field("time", d.value, "Time").blur();
|
||||
cy.get_field("time").should("have.value", d.match_value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('DateTime formats', () => {
|
||||
describe("DateTime formats", () => {
|
||||
let datetime_formats = [
|
||||
{
|
||||
date_format: 'dd.mm.yyyy',
|
||||
time_format: 'HH:mm:ss',
|
||||
value: ' 02.12.2019 11:00:12',
|
||||
doc_value: '2019-12-02 00:30:12', // system timezone (America/New_York)
|
||||
input_value: '02.12.2019 11:00:12' // admin timezone (Asia/Kolkata)
|
||||
date_format: "dd.mm.yyyy",
|
||||
time_format: "HH:mm:ss",
|
||||
value: " 02.12.2019 11:00:12",
|
||||
doc_value: "2019-12-02 00:30:12", // system timezone (America/New_York)
|
||||
input_value: "02.12.2019 11:00:12", // admin timezone (Asia/Kolkata)
|
||||
},
|
||||
{
|
||||
date_format: 'mm-dd-yyyy',
|
||||
time_format: 'HH:mm',
|
||||
value: ' 12-02-2019 11:00:00',
|
||||
doc_value: '2019-12-02 00:30:00', // system timezone (America/New_York)
|
||||
input_value: '12-02-2019 11:00' // admin timezone (Asia/Kolkata)
|
||||
}
|
||||
date_format: "mm-dd-yyyy",
|
||||
time_format: "HH:mm",
|
||||
value: " 12-02-2019 11:00:00",
|
||||
doc_value: "2019-12-02 00:30:00", // system timezone (America/New_York)
|
||||
input_value: "12-02-2019 11:00", // admin timezone (Asia/Kolkata)
|
||||
},
|
||||
];
|
||||
|
||||
datetime_formats.forEach(d => {
|
||||
datetime_formats.forEach((d) => {
|
||||
it(`test datetime format ${d.date_format} ${d.time_format}`, () => {
|
||||
cy.set_value('System Settings', 'System Settings', {
|
||||
cy.set_value("System Settings", "System Settings", {
|
||||
date_format: d.date_format,
|
||||
time_format: d.time_format
|
||||
time_format: d.time_format,
|
||||
});
|
||||
cy.window()
|
||||
.its('frappe')
|
||||
.then(frappe => {
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
frappe.sys_defaults.date_format = d.date_format;
|
||||
frappe.sys_defaults.time_format = d.time_format;
|
||||
});
|
||||
cy.new_form(doctype_name);
|
||||
cy.fill_field('datetime', d.value, 'Datetime').blur();
|
||||
cy.get_field('datetime').should('have.value', d.input_value);
|
||||
cy.fill_field("datetime", d.value, "Datetime").blur();
|
||||
cy.get_field("datetime").should("have.value", d.input_value);
|
||||
|
||||
cy.window()
|
||||
.its('cur_frm.doc.datetime')
|
||||
.should('eq', d.doc_value);
|
||||
cy.window().its("cur_frm.doc.datetime").should("eq", d.doc_value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@
|
|||
// cy.get('.indicator-pill').should('contain', 'Open').should('have.class', 'red');
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
|
|
|||
|
|
@ -1,135 +1,152 @@
|
|||
context('Depends On', () => {
|
||||
context("Depends On", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.xcall('frappe.tests.ui_test_helpers.create_child_doctype', {
|
||||
name: 'Child Test Depends On',
|
||||
fields: [
|
||||
{
|
||||
"label": "Child Test Field",
|
||||
"fieldname": "child_test_field",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
},
|
||||
{
|
||||
"label": "Child Dependant Field",
|
||||
"fieldname": "child_dependant_field",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
},
|
||||
{
|
||||
"label": "Child Display Dependant Field",
|
||||
"fieldname": "child_display_dependant_field",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
},
|
||||
]
|
||||
cy.visit("/app/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.xcall("frappe.tests.ui_test_helpers.create_child_doctype", {
|
||||
name: "Child Test Depends On",
|
||||
fields: [
|
||||
{
|
||||
label: "Child Test Field",
|
||||
fieldname: "child_test_field",
|
||||
fieldtype: "Data",
|
||||
in_list_view: 1,
|
||||
},
|
||||
{
|
||||
label: "Child Dependant Field",
|
||||
fieldname: "child_dependant_field",
|
||||
fieldtype: "Data",
|
||||
in_list_view: 1,
|
||||
},
|
||||
{
|
||||
label: "Child Display Dependant Field",
|
||||
fieldname: "child_display_dependant_field",
|
||||
fieldtype: "Data",
|
||||
in_list_view: 1,
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
.then((frappe) => {
|
||||
return frappe.xcall("frappe.tests.ui_test_helpers.create_doctype", {
|
||||
name: "Test Depends On",
|
||||
fields: [
|
||||
{
|
||||
label: "Test Field",
|
||||
fieldname: "test_field",
|
||||
fieldtype: "Data",
|
||||
},
|
||||
{
|
||||
label: "Dependant Field",
|
||||
fieldname: "dependant_field",
|
||||
fieldtype: "Data",
|
||||
mandatory_depends_on: "eval:doc.test_field=='Some Value'",
|
||||
read_only_depends_on: "eval:doc.test_field=='Some Other Value'",
|
||||
},
|
||||
{
|
||||
label: "Display Dependant Field",
|
||||
fieldname: "display_dependant_field",
|
||||
fieldtype: "Data",
|
||||
depends_on: "eval:doc.test_field=='Value'",
|
||||
},
|
||||
{
|
||||
label: "Child Test Depends On Field",
|
||||
fieldname: "child_test_depends_on_field",
|
||||
fieldtype: "Table",
|
||||
read_only_depends_on: "eval:doc.test_field=='Some Other Value'",
|
||||
options: "Child Test Depends On",
|
||||
},
|
||||
{
|
||||
label: "Dependent Tab",
|
||||
fieldname: "dependent_tab",
|
||||
fieldtype: "Tab Break",
|
||||
depends_on: "eval:doc.test_field=='Show Tab'",
|
||||
},
|
||||
{
|
||||
fieldname: "tab_section",
|
||||
fieldtype: "Section Break",
|
||||
},
|
||||
{
|
||||
label: "Field in Tab",
|
||||
fieldname: "field_in_tab",
|
||||
fieldtype: "Data",
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
}).then(frappe => {
|
||||
return frappe.xcall('frappe.tests.ui_test_helpers.create_doctype', {
|
||||
name: 'Test Depends On',
|
||||
fields: [
|
||||
{
|
||||
"label": "Test Field",
|
||||
"fieldname": "test_field",
|
||||
"fieldtype": "Data",
|
||||
},
|
||||
{
|
||||
"label": "Dependant Field",
|
||||
"fieldname": "dependant_field",
|
||||
"fieldtype": "Data",
|
||||
"mandatory_depends_on": "eval:doc.test_field=='Some Value'",
|
||||
"read_only_depends_on": "eval:doc.test_field=='Some Other Value'",
|
||||
},
|
||||
{
|
||||
"label": "Display Dependant Field",
|
||||
"fieldname": "display_dependant_field",
|
||||
"fieldtype": "Data",
|
||||
'depends_on': "eval:doc.test_field=='Value'"
|
||||
},
|
||||
{
|
||||
"label": "Child Test Depends On Field",
|
||||
"fieldname": "child_test_depends_on_field",
|
||||
"fieldtype": "Table",
|
||||
'read_only_depends_on': "eval:doc.test_field=='Some Other Value'",
|
||||
'options': "Child Test Depends On"
|
||||
},
|
||||
{
|
||||
"label": "Dependent Tab",
|
||||
"fieldname": "dependent_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"depends_on": "eval:doc.test_field=='Show Tab'"
|
||||
},
|
||||
{
|
||||
"fieldname": "tab_section",
|
||||
"fieldtype": "Section Break",
|
||||
},
|
||||
{
|
||||
"label": "Field in Tab",
|
||||
"fieldname": "field_in_tab",
|
||||
"fieldtype": "Data",
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should show the tab on other setting field value', () => {
|
||||
cy.new_form('Test Depends On');
|
||||
cy.fill_field('test_field', 'Show Tab');
|
||||
cy.get('body').click();
|
||||
cy.findByRole("tab", {name: "Dependent Tab"}).should('be.visible');
|
||||
it("should show the tab on other setting field value", () => {
|
||||
cy.new_form("Test Depends On");
|
||||
cy.fill_field("test_field", "Show Tab");
|
||||
cy.get("body").click();
|
||||
cy.findByRole("tab", { name: "Dependent Tab" }).should("be.visible");
|
||||
});
|
||||
it('should set the field as mandatory depending on other fields value', () => {
|
||||
cy.new_form('Test Depends On');
|
||||
cy.fill_field('test_field', 'Some Value');
|
||||
cy.findByRole('button', {name: 'Save'}).click();
|
||||
cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('be.visible');
|
||||
it("should set the field as mandatory depending on other fields value", () => {
|
||||
cy.new_form("Test Depends On");
|
||||
cy.fill_field("test_field", "Some Value");
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
cy.get(".msgprint-dialog .modal-title").contains("Missing Fields").should("be.visible");
|
||||
cy.hide_dialog();
|
||||
cy.fill_field('test_field', 'Random value');
|
||||
cy.findByRole('button', {name: 'Save'}).click();
|
||||
cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('not.be.visible');
|
||||
cy.fill_field("test_field", "Random value");
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
cy.get(".msgprint-dialog .modal-title")
|
||||
.contains("Missing Fields")
|
||||
.should("not.be.visible");
|
||||
});
|
||||
it('should set the field as read only depending on other fields value', () => {
|
||||
cy.new_form('Test Depends On');
|
||||
cy.fill_field('dependant_field', 'Some Value');
|
||||
cy.fill_field('test_field', 'Some Other Value');
|
||||
cy.get('body').click();
|
||||
cy.get('.control-input [data-fieldname="dependant_field"]').should('be.disabled');
|
||||
cy.fill_field('test_field', 'Random Value');
|
||||
cy.get('body').click();
|
||||
cy.get('.control-input [data-fieldname="dependant_field"]').should('not.be.disabled');
|
||||
it("should set the field as read only depending on other fields value", () => {
|
||||
cy.new_form("Test Depends On");
|
||||
cy.fill_field("dependant_field", "Some Value");
|
||||
cy.fill_field("test_field", "Some Other Value");
|
||||
cy.get("body").click();
|
||||
cy.get('.control-input [data-fieldname="dependant_field"]').should("be.disabled");
|
||||
cy.fill_field("test_field", "Random Value");
|
||||
cy.get("body").click();
|
||||
cy.get('.control-input [data-fieldname="dependant_field"]').should("not.be.disabled");
|
||||
});
|
||||
it('should set the table and its fields as read only depending on other fields value', () => {
|
||||
cy.new_form('Test Depends On');
|
||||
cy.fill_field('dependant_field', 'Some Value');
|
||||
it("should set the table and its fields as read only depending on other fields value", () => {
|
||||
cy.new_form("Test Depends On");
|
||||
cy.fill_field("dependant_field", "Some Value");
|
||||
//cy.fill_field('test_field', 'Some Other Value');
|
||||
cy.get('.frappe-control[data-fieldname="child_test_depends_on_field"]').as('table');
|
||||
cy.get('@table').findByRole('button', {name: 'Add Row'}).click();
|
||||
cy.get('@table').find('[data-idx="1"]').as('row1');
|
||||
cy.get('@row1').find('.btn-open-row').click();
|
||||
cy.get('@row1').find('.form-in-grid').as('row1-form_in_grid');
|
||||
cy.get('.frappe-control[data-fieldname="child_test_depends_on_field"]').as("table");
|
||||
cy.get("@table").findByRole("button", { name: "Add Row" }).click();
|
||||
cy.get("@table").find('[data-idx="1"]').as("row1");
|
||||
cy.get("@row1").find(".btn-open-row").click();
|
||||
cy.get("@row1").find(".form-in-grid").as("row1-form_in_grid");
|
||||
//cy.get('@row1-form_in_grid').find('')
|
||||
cy.fill_table_field('child_test_depends_on_field', '1', 'child_test_field', 'Some Value');
|
||||
cy.fill_table_field('child_test_depends_on_field', '1', 'child_dependant_field', 'Some Other Value');
|
||||
cy.fill_table_field("child_test_depends_on_field", "1", "child_test_field", "Some Value");
|
||||
cy.fill_table_field(
|
||||
"child_test_depends_on_field",
|
||||
"1",
|
||||
"child_dependant_field",
|
||||
"Some Other Value"
|
||||
);
|
||||
|
||||
cy.get('@row1-form_in_grid').find('.grid-collapse-row').click();
|
||||
cy.get("@row1-form_in_grid").find(".grid-collapse-row").click();
|
||||
|
||||
// set the table to read-only
|
||||
cy.fill_field('test_field', 'Some Other Value');
|
||||
cy.fill_field("test_field", "Some Other Value");
|
||||
|
||||
// grid row form fields should be read-only
|
||||
cy.get('@row1').find('.btn-open-row').click();
|
||||
cy.get("@row1").find(".btn-open-row").click();
|
||||
|
||||
cy.get('@row1-form_in_grid').find('.control-input [data-fieldname="child_test_field"]').should('be.disabled');
|
||||
cy.get('@row1-form_in_grid').find('.control-input [data-fieldname="child_dependant_field"]').should('be.disabled');
|
||||
cy.get("@row1-form_in_grid")
|
||||
.find('.control-input [data-fieldname="child_test_field"]')
|
||||
.should("be.disabled");
|
||||
cy.get("@row1-form_in_grid")
|
||||
.find('.control-input [data-fieldname="child_dependant_field"]')
|
||||
.should("be.disabled");
|
||||
});
|
||||
it('should display the field depending on other fields value', () => {
|
||||
cy.new_form('Test Depends On');
|
||||
cy.get('.control-input [data-fieldname="display_dependant_field"]').should('not.be.visible');
|
||||
it("should display the field depending on other fields value", () => {
|
||||
cy.new_form("Test Depends On");
|
||||
cy.get('.control-input [data-fieldname="display_dependant_field"]').should(
|
||||
"not.be.visible"
|
||||
);
|
||||
cy.get('.control-input [data-fieldname="test_field"]').clear();
|
||||
cy.fill_field('test_field', 'Value');
|
||||
cy.get('body').click();
|
||||
cy.get('.control-input [data-fieldname="display_dependant_field"]').should('be.visible');
|
||||
cy.fill_field("test_field", "Value");
|
||||
cy.get("body").click();
|
||||
cy.get('.control-input [data-fieldname="display_dependant_field"]').should("be.visible");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,79 +1,101 @@
|
|||
context('Discussions', () => {
|
||||
context("Discussions", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.call('frappe.tests.ui_test_helpers.create_data_for_discussions');
|
||||
});
|
||||
cy.visit("/app");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_data_for_discussions");
|
||||
});
|
||||
});
|
||||
|
||||
const reply_through_modal = () => {
|
||||
cy.visit('/test-page-discussions');
|
||||
cy.visit("/test-page-discussions");
|
||||
|
||||
// Open the modal
|
||||
cy.get('.reply').click();
|
||||
cy.get(".reply").click();
|
||||
cy.wait(500);
|
||||
cy.get('.discussion-modal').should('be.visible');
|
||||
cy.get(".discussion-modal").should("be.visible");
|
||||
|
||||
// Enter title
|
||||
cy.get('.modal .topic-title').type('Discussion from tests')
|
||||
.should('have.value', 'Discussion from tests');
|
||||
cy.get(".modal .topic-title")
|
||||
.type("Discussion from tests")
|
||||
.should("have.value", "Discussion from tests");
|
||||
|
||||
// Enter comment
|
||||
cy.get('.modal .comment-field')
|
||||
.type('This is a discussion from the cypress ui tests.')
|
||||
.should('have.value', 'This is a discussion from the cypress ui tests.');
|
||||
cy.get(".modal .comment-field")
|
||||
.type("This is a discussion from the cypress ui tests.")
|
||||
.should("have.value", "This is a discussion from the cypress ui tests.");
|
||||
|
||||
// Submit
|
||||
cy.get('.modal .submit-discussion').click();
|
||||
cy.get(".modal .submit-discussion").click();
|
||||
cy.wait(2000);
|
||||
|
||||
// Check if discussion is added to page and content is visible
|
||||
cy.get('.sidebar-parent:first .discussion-topic-title').should('have.text', 'Discussion from tests');
|
||||
cy.get('.discussion-on-page:visible').should('have.class', 'show');
|
||||
cy.get('.discussion-on-page:visible .reply-card .reply-text')
|
||||
.should('have.text', 'This is a discussion from the cypress ui tests.\n');
|
||||
|
||||
cy.get(".sidebar-parent:first .discussion-topic-title").should(
|
||||
"have.text",
|
||||
"Discussion from tests"
|
||||
);
|
||||
cy.get(".discussion-on-page:visible").should("have.class", "show");
|
||||
cy.get(".discussion-on-page:visible .reply-card .reply-text").should(
|
||||
"have.text",
|
||||
"This is a discussion from the cypress ui tests.\n"
|
||||
);
|
||||
};
|
||||
|
||||
const reply_through_comment_box = () => {
|
||||
cy.get('.discussion-form:visible .comment-field')
|
||||
.type('This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page.')
|
||||
.should('have.value', 'This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page.');
|
||||
cy.get(".discussion-form:visible .comment-field")
|
||||
.type(
|
||||
"This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page."
|
||||
)
|
||||
.should(
|
||||
"have.value",
|
||||
"This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page."
|
||||
);
|
||||
|
||||
cy.get('.discussion-form:visible .submit-discussion').click();
|
||||
cy.get(".discussion-form:visible .submit-discussion").click();
|
||||
cy.wait(3000);
|
||||
cy.get('.discussion-on-page:visible').should('have.class', 'show');
|
||||
cy.get('.discussion-on-page:visible').children(".reply-card").eq(1).find(".reply-text")
|
||||
.should('have.text', 'This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page.\n');
|
||||
cy.get(".discussion-on-page:visible").should("have.class", "show");
|
||||
cy.get(".discussion-on-page:visible")
|
||||
.children(".reply-card")
|
||||
.eq(1)
|
||||
.find(".reply-text")
|
||||
.should(
|
||||
"have.text",
|
||||
"This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page.\n"
|
||||
);
|
||||
};
|
||||
|
||||
const cancel_and_clear_comment_box = () => {
|
||||
cy.get('.discussion-form:visible .comment-field')
|
||||
.type('This is a discussion from the cypress ui tests.')
|
||||
.should('have.value', 'This is a discussion from the cypress ui tests.');
|
||||
cy.get(".discussion-form:visible .comment-field")
|
||||
.type("This is a discussion from the cypress ui tests.")
|
||||
.should("have.value", "This is a discussion from the cypress ui tests.");
|
||||
|
||||
cy.get('.discussion-form:visible .cancel-comment').click();
|
||||
cy.get('.discussion-form:visible .comment-field').should('have.value', '');
|
||||
cy.get(".discussion-form:visible .cancel-comment").click();
|
||||
cy.get(".discussion-form:visible .comment-field").should("have.value", "");
|
||||
};
|
||||
|
||||
const single_thread_discussion = () => {
|
||||
cy.visit('/test-single-thread');
|
||||
cy.get('.discussions-sidebar').should('have.length', 0);
|
||||
cy.get('.reply').should('have.length', 0);
|
||||
cy.visit("/test-single-thread");
|
||||
cy.get(".discussions-sidebar").should("have.length", 0);
|
||||
cy.get(".reply").should("have.length", 0);
|
||||
|
||||
cy.get('.discussion-form:visible .comment-field')
|
||||
.type('This comment is being made on a single thread discussion.')
|
||||
.should('have.value', 'This comment is being made on a single thread discussion.');
|
||||
cy.get(".discussion-form:visible .comment-field")
|
||||
.type("This comment is being made on a single thread discussion.")
|
||||
.should("have.value", "This comment is being made on a single thread discussion.");
|
||||
|
||||
cy.get('.discussion-form:visible .submit-discussion').click();
|
||||
cy.get(".discussion-form:visible .submit-discussion").click();
|
||||
cy.wait(3000);
|
||||
cy.get('.discussion-on-page').children(".reply-card").eq(-1).find(".reply-text")
|
||||
.should('have.text', 'This comment is being made on a single thread discussion.\n');
|
||||
cy.get(".discussion-on-page")
|
||||
.children(".reply-card")
|
||||
.eq(-1)
|
||||
.find(".reply-text")
|
||||
.should("have.text", "This comment is being made on a single thread discussion.\n");
|
||||
};
|
||||
|
||||
it('reply through modal', reply_through_modal);
|
||||
it('reply through comment box', reply_through_comment_box);
|
||||
it('cancel and clear comment box', cancel_and_clear_comment_box);
|
||||
it('single thread discussion', single_thread_discussion);
|
||||
it("reply through modal", reply_through_modal);
|
||||
it("reply through comment box", reply_through_comment_box);
|
||||
it("cancel and clear comment box", cancel_and_clear_comment_box);
|
||||
it("single thread discussion", single_thread_discussion);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,78 +1,86 @@
|
|||
context('FileUploader', () => {
|
||||
context("FileUploader", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app');
|
||||
cy.visit("/app");
|
||||
});
|
||||
|
||||
function open_upload_dialog() {
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
new frappe.ui.FileUploader();
|
||||
});
|
||||
cy.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
new frappe.ui.FileUploader();
|
||||
});
|
||||
}
|
||||
|
||||
it('upload dialog api works', () => {
|
||||
it("upload dialog api works", () => {
|
||||
open_upload_dialog();
|
||||
cy.get_open_dialog().should('contain', 'Drag and drop files');
|
||||
cy.get_open_dialog().should("contain", "Drag and drop files");
|
||||
cy.hide_dialog();
|
||||
});
|
||||
|
||||
it('should accept dropped files', () => {
|
||||
it("should accept dropped files", () => {
|
||||
open_upload_dialog();
|
||||
|
||||
cy.get_open_dialog().find('.file-upload-area').attachFile('example.json', {
|
||||
subjectType: 'drag-n-drop',
|
||||
});
|
||||
|
||||
cy.get_open_dialog().find('.file-name').should('contain', 'example.json');
|
||||
cy.intercept('POST', '/api/method/upload_file').as('upload_file');
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
|
||||
cy.wait('@upload_file').its('response.statusCode').should('eq', 200);
|
||||
cy.get('.modal:visible').should('not.exist');
|
||||
});
|
||||
|
||||
it('should accept uploaded files', () => {
|
||||
open_upload_dialog();
|
||||
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Library'}).click();
|
||||
cy.findByPlaceholderText('Search by filename or extension').type('example.json');
|
||||
cy.get_open_dialog().findAllByText('example.json').first().click();
|
||||
cy.intercept('POST', '/api/method/upload_file').as('upload_file');
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
|
||||
cy.wait('@upload_file').its('response.body.message')
|
||||
.should('have.property', 'file_name', 'example.json');
|
||||
cy.get('.modal:visible').should('not.exist');
|
||||
});
|
||||
|
||||
it('should accept web links', () => {
|
||||
open_upload_dialog();
|
||||
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Link'}).click();
|
||||
cy.get_open_dialog()
|
||||
.findByPlaceholderText('Attach a web link')
|
||||
.type('https://github.com', { delay: 100, force: true });
|
||||
cy.intercept('POST', '/api/method/upload_file').as('upload_file');
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
|
||||
cy.wait('@upload_file').its('response.body.message')
|
||||
.should('have.property', 'file_url', 'https://github.com');
|
||||
cy.get('.modal:visible').should('not.exist');
|
||||
.find(".file-upload-area")
|
||||
.selectFile("cypress/fixtures/example.json", {
|
||||
action: "drag-drop",
|
||||
});
|
||||
|
||||
cy.get_open_dialog().find(".file-name").should("contain", "example.json");
|
||||
cy.intercept("POST", "/api/method/upload_file").as("upload_file");
|
||||
cy.get_open_dialog().findByRole("button", { name: "Upload" }).click();
|
||||
cy.wait("@upload_file").its("response.statusCode").should("eq", 200);
|
||||
cy.get(".modal:visible").should("not.exist");
|
||||
});
|
||||
|
||||
it('should allow cropping and optimization for valid images', () => {
|
||||
it("should accept uploaded files", () => {
|
||||
open_upload_dialog();
|
||||
|
||||
cy.get_open_dialog().find('.file-upload-area').attachFile('sample_image.jpg', {
|
||||
subjectType: 'drag-n-drop',
|
||||
});
|
||||
cy.get_open_dialog().findByRole("button", { name: "Library" }).click();
|
||||
cy.findByPlaceholderText("Search by filename or extension").type("example.json");
|
||||
cy.get_open_dialog().findAllByText("example.json").first().click();
|
||||
cy.intercept("POST", "/api/method/upload_file").as("upload_file");
|
||||
cy.get_open_dialog().findByRole("button", { name: "Upload" }).click();
|
||||
cy.wait("@upload_file")
|
||||
.its("response.body.message")
|
||||
.should("have.property", "file_name", "example.json");
|
||||
cy.get(".modal:visible").should("not.exist");
|
||||
});
|
||||
|
||||
cy.get_open_dialog().findAllByText('sample_image.jpg').should('exist');
|
||||
cy.get_open_dialog().find('.btn-crop').first().click();
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Crop'}).click();
|
||||
cy.get_open_dialog().findAllByRole('checkbox', {name: 'Optimize'}).should('exist');
|
||||
cy.get_open_dialog().findAllByLabelText('Optimize').first().click();
|
||||
it("should accept web links", () => {
|
||||
open_upload_dialog();
|
||||
|
||||
cy.intercept('POST', '/api/method/upload_file').as('upload_file');
|
||||
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
|
||||
cy.wait('@upload_file').its('response.statusCode').should('eq', 200);
|
||||
cy.get('.modal:visible').should('not.exist');
|
||||
cy.get_open_dialog().findByRole("button", { name: "Link" }).click();
|
||||
cy.get_open_dialog()
|
||||
.findByPlaceholderText("Attach a web link")
|
||||
.type("https://github.com", { delay: 100, force: true });
|
||||
cy.intercept("POST", "/api/method/upload_file").as("upload_file");
|
||||
cy.get_open_dialog().findByRole("button", { name: "Upload" }).click();
|
||||
cy.wait("@upload_file")
|
||||
.its("response.body.message")
|
||||
.should("have.property", "file_url", "https://github.com");
|
||||
cy.get(".modal:visible").should("not.exist");
|
||||
});
|
||||
|
||||
it("should allow cropping and optimization for valid images", () => {
|
||||
open_upload_dialog();
|
||||
|
||||
cy.get_open_dialog()
|
||||
.find(".file-upload-area")
|
||||
.selectFile("cypress/fixtures/sample_image.jpg", {
|
||||
action: "drag-drop",
|
||||
});
|
||||
|
||||
cy.get_open_dialog().findAllByText("sample_image.jpg").should("exist");
|
||||
cy.get_open_dialog().find(".btn-crop").first().click();
|
||||
cy.get_open_dialog().findByRole("button", { name: "Crop" }).click();
|
||||
cy.get_open_dialog().findAllByRole("checkbox", { name: "Optimize" }).should("exist");
|
||||
cy.get_open_dialog().findAllByLabelText("Optimize").first().click();
|
||||
|
||||
cy.intercept("POST", "/api/method/upload_file").as("upload_file");
|
||||
cy.get_open_dialog().findByRole("button", { name: "Upload" }).click();
|
||||
cy.wait("@upload_file").its("response.statusCode").should("eq", 200);
|
||||
cy.get(".modal:visible").should("not.exist");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,42 +4,48 @@ context("First Day of the Week", () => {
|
|||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit('/app/system-settings');
|
||||
cy.findByText('Date and Number Format').click();
|
||||
cy.visit("/app/system-settings");
|
||||
cy.findByText("Date and Number Format").click();
|
||||
});
|
||||
|
||||
it("Date control starts with same day as selected in System Settings", () => {
|
||||
cy.intercept('POST', '/api/method/frappe.core.doctype.system_settings.system_settings.load').as("load_settings");
|
||||
cy.fill_field('first_day_of_the_week', 'Tuesday', 'Select');
|
||||
cy.findByRole('button', {name: 'Save'}).click();
|
||||
cy.intercept(
|
||||
"POST",
|
||||
"/api/method/frappe.core.doctype.system_settings.system_settings.load"
|
||||
).as("load_settings");
|
||||
cy.fill_field("first_day_of_the_week", "Tuesday", "Select");
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
cy.wait("@load_settings");
|
||||
cy.dialog({
|
||||
title: 'Date',
|
||||
title: "Date",
|
||||
fields: [
|
||||
{
|
||||
label: 'Date',
|
||||
fieldname: 'date',
|
||||
fieldtype: 'Date'
|
||||
}
|
||||
]
|
||||
label: "Date",
|
||||
fieldname: "date",
|
||||
fieldtype: "Date",
|
||||
},
|
||||
],
|
||||
});
|
||||
cy.get_field('date').click();
|
||||
cy.get('.datepicker--day-name').eq(0).should('have.text', 'Tu');
|
||||
cy.get_field("date").click();
|
||||
cy.get(".datepicker--day-name").eq(0).should("have.text", "Tu");
|
||||
});
|
||||
|
||||
it("Calendar view starts with same day as selected in System Settings", () => {
|
||||
cy.intercept('POST', '/api/method/frappe.core.doctype.system_settings.system_settings.load').as("load_settings");
|
||||
cy.fill_field('first_day_of_the_week', 'Monday', 'Select');
|
||||
cy.findByRole('button', {name: 'Save'}).click();
|
||||
cy.intercept(
|
||||
"POST",
|
||||
"/api/method/frappe.core.doctype.system_settings.system_settings.load"
|
||||
).as("load_settings");
|
||||
cy.fill_field("first_day_of_the_week", "Monday", "Select");
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
cy.wait("@load_settings");
|
||||
cy.visit("app/todo/view/calendar/default");
|
||||
cy.get('.fc-day-header > span').eq(0).should('have.text', 'Mon');
|
||||
cy.get(".fc-day-header > span").eq(0).should("have.text", "Mon");
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.visit('/app/system-settings');
|
||||
cy.findByText('Date and Number Format').click();
|
||||
cy.fill_field('first_day_of_the_week', 'Sunday', 'Select');
|
||||
cy.findByRole('button', {name: 'Save'}).click();
|
||||
cy.visit("/app/system-settings");
|
||||
cy.findByText("Date and Number Format").click();
|
||||
cy.fill_field("first_day_of_the_week", "Sunday", "Select");
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,79 +1,96 @@
|
|||
context('Folder Navigation', () => {
|
||||
context("Folder Navigation", () => {
|
||||
before(() => {
|
||||
cy.visit('/login');
|
||||
cy.visit("/login");
|
||||
cy.login();
|
||||
cy.visit('/app/file');
|
||||
cy.visit("/app/file");
|
||||
});
|
||||
|
||||
it('Adding Folders', () => {
|
||||
it("Adding Folders", () => {
|
||||
//Adding filter to go into the home folder
|
||||
cy.get('.filter-selector > .btn').findByText('1 filter').click();
|
||||
cy.findByRole('button', {name: 'Clear Filters'}).click();
|
||||
cy.get('.filter-action-buttons > .text-muted').findByText('+ Add a Filter').click();
|
||||
cy.get('.fieldname-select-area > .awesomplete > .form-control').type('Fol{enter}');
|
||||
cy.get('.filter-field > .form-group > .link-field > .awesomplete > .input-with-feedback').type('Home{enter}');
|
||||
cy.get('.filter-action-buttons > div > .btn-primary').findByText('Apply Filters').click();
|
||||
cy.get(".filter-x-button").click();
|
||||
cy.click_filter_button();
|
||||
cy.get(".filter-action-buttons > .text-muted").findByText("+ Add a Filter").click();
|
||||
cy.get(".fieldname-select-area > .awesomplete > .form-control:last").type("Fol{enter}");
|
||||
cy.get(
|
||||
".filter-field > .form-group > .link-field > .awesomplete > .input-with-feedback"
|
||||
).type("Home{enter}");
|
||||
cy.get(".filter-action-buttons > div > .btn-primary").findByText("Apply Filters").click();
|
||||
|
||||
//Adding folder (Test Folder)
|
||||
cy.click_menu_button("New Folder");
|
||||
cy.fill_field('value', 'Test Folder');
|
||||
cy.click_modal_primary_button('Create');
|
||||
cy.fill_field("value", "Test Folder");
|
||||
cy.click_modal_primary_button("Create");
|
||||
});
|
||||
|
||||
it('Navigating the nested folders, checking if the URL formed is correct, checking if the added content in the child folder is correct', () => {
|
||||
it("Navigating the nested folders, checking if the URL formed is correct, checking if the added content in the child folder is correct", () => {
|
||||
//Navigating inside the Attachments folder
|
||||
cy.wait(500);
|
||||
cy.get('[title="Attachments"] > span').click();
|
||||
|
||||
//To check if the URL formed after visiting the attachments folder is correct
|
||||
cy.location('pathname').should('eq', '/app/file/view/home/Attachments');
|
||||
cy.visit('/app/file/view/home/Attachments');
|
||||
cy.location("pathname").should("eq", "/app/file/view/home/Attachments");
|
||||
cy.visit("/app/file/view/home/Attachments");
|
||||
|
||||
//Adding folder inside the attachments folder
|
||||
cy.click_menu_button("New Folder");
|
||||
cy.fill_field('value', 'Test Folder');
|
||||
cy.click_modal_primary_button('Create');
|
||||
cy.fill_field("value", "Test Folder");
|
||||
cy.click_modal_primary_button("Create");
|
||||
|
||||
//Navigating inside the added folder in the Attachments folder
|
||||
cy.wait(500);
|
||||
cy.get('[title="Test Folder"] > span').click();
|
||||
|
||||
//To check if the URL is correct after visiting the Test Folder
|
||||
cy.location('pathname').should('eq', '/app/file/view/home/Attachments/Test%20Folder');
|
||||
cy.visit('/app/file/view/home/Attachments/Test%20Folder');
|
||||
cy.location("pathname").should("eq", "/app/file/view/home/Attachments/Test%20Folder");
|
||||
cy.visit("/app/file/view/home/Attachments/Test%20Folder");
|
||||
|
||||
//Adding a file inside the Test Folder
|
||||
cy.findByRole('button', {name: 'Add File'}).eq(0).click({force: true});
|
||||
cy.get('.file-uploader').findByText('Link').click();
|
||||
cy.get('.input-group > .form-control').type('https://wallpaperplay.com/walls/full/8/2/b/72402.jpg');
|
||||
cy.click_modal_primary_button('Upload');
|
||||
cy.findByRole("button", { name: "Add File" }).eq(0).click({ force: true });
|
||||
cy.get(".file-uploader").findByText("Link").click();
|
||||
cy.get(".input-group > input.form-control:visible").as("upload_input");
|
||||
cy.get("@upload_input").type("https://wallpaperplay.com/walls/full/8/2/b/72402.jpg", {
|
||||
waitForAnimations: false,
|
||||
parseSpecialCharSequences: false,
|
||||
force: true,
|
||||
delay: 100,
|
||||
});
|
||||
cy.click_modal_primary_button("Upload");
|
||||
|
||||
//To check if the added file is present in the Test Folder
|
||||
cy.get('span.level-item > span').should('contain', 'Test Folder');
|
||||
cy.get('.list-row-container').eq(0).should('contain.text', '72402.jpg');
|
||||
cy.get('.list-row-checkbox').eq(0).click();
|
||||
cy.visit("/app/file/view/home/Attachments");
|
||||
cy.wait(500);
|
||||
cy.get("span.level-item > a > span").should("contain", "Test Folder");
|
||||
cy.visit("/app/file/view/home/Attachments/Test%20Folder");
|
||||
|
||||
cy.wait(500);
|
||||
cy.get(".list-row-container").eq(0).should("contain.text", "72402.jpg");
|
||||
cy.get(".list-row-checkbox").eq(0).click();
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: 'api/method/frappe.desk.reportview.delete_items'
|
||||
}).as('file_deleted');
|
||||
method: "POST",
|
||||
url: "api/method/frappe.desk.reportview.delete_items",
|
||||
}).as("file_deleted");
|
||||
|
||||
//Deleting the added file from the Test folder
|
||||
cy.click_action_button("Delete");
|
||||
cy.click_modal_primary_button('Yes');
|
||||
cy.wait('@file_deleted');
|
||||
cy.click_modal_primary_button("Yes");
|
||||
cy.wait("@file_deleted");
|
||||
|
||||
//Deleting the Test Folder
|
||||
cy.visit('/app/file/view/home/Attachments');
|
||||
cy.get('.list-row-checkbox').eq(0).click();
|
||||
cy.visit("/app/file/view/home/Attachments");
|
||||
cy.get(".list-row-checkbox").eq(0).click();
|
||||
cy.click_action_button("Delete");
|
||||
cy.click_modal_primary_button('Yes');
|
||||
cy.wait('@file_deleted');
|
||||
cy.click_modal_primary_button("Yes");
|
||||
cy.wait("@file_deleted");
|
||||
});
|
||||
|
||||
it('Deleting Test Folder from the home', () => {
|
||||
//Deleting the Test Folder added in the home directory
|
||||
cy.visit('/app/file/view/home');
|
||||
cy.get('.level-left > .list-subject > .file-select >.list-row-checkbox').eq(0).click({force: true, delay: 500});
|
||||
it("Deleting Test Folder from the home", () => {
|
||||
//Deleting the Test Folder added in the home directory
|
||||
cy.visit("/app/file/view/home");
|
||||
cy.get(".level-left > .list-subject > .file-select >.list-row-checkbox")
|
||||
.eq(0)
|
||||
.click({ force: true, delay: 500 });
|
||||
cy.click_action_button("Delete");
|
||||
cy.click_modal_primary_button('Yes');
|
||||
cy.click_modal_primary_button("Yes");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,98 +1,233 @@
|
|||
context('Form', () => {
|
||||
const jump_to_field = (field_label) => {
|
||||
cy.get("body")
|
||||
.type("{esc}") // lose focus if any
|
||||
.type("{ctrl+j}") // jump to field
|
||||
.type(field_label)
|
||||
.wait(500)
|
||||
.type("{enter}")
|
||||
.wait(200)
|
||||
.type("{enter}")
|
||||
.wait(500);
|
||||
};
|
||||
|
||||
const type_value = (value) => {
|
||||
cy.focused().clear().type(value).type("{esc}");
|
||||
};
|
||||
|
||||
context("Form", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_contact_records");
|
||||
});
|
||||
cy.visit("/app/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_contact_records");
|
||||
});
|
||||
});
|
||||
it('create a new form', () => {
|
||||
cy.visit('/app/todo/new');
|
||||
cy.get_field('description', 'Text Editor').type('this is a test todo', {force: true}).wait(200);
|
||||
cy.get('.page-title').should('contain', 'Not Saved');
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
|
||||
it("create a new form", () => {
|
||||
cy.visit("/app/todo/new");
|
||||
cy.get_field("description", "Text Editor")
|
||||
.type("this is a test todo", { force: true })
|
||||
.wait(200);
|
||||
cy.get(".page-title").should("contain", "Not Saved");
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: 'api/method/frappe.desk.form.save.savedocs'
|
||||
}).as('form_save');
|
||||
cy.get('.primary-action').click();
|
||||
cy.wait('@form_save').its('response.statusCode').should('eq', 200);
|
||||
method: "POST",
|
||||
url: "api/method/frappe.desk.form.save.savedocs",
|
||||
}).as("form_save");
|
||||
cy.get(".primary-action").click();
|
||||
cy.wait("@form_save").its("response.statusCode").should("eq", 200);
|
||||
|
||||
cy.go_to_list('ToDo');
|
||||
cy.clear_filters()
|
||||
cy.get('.page-head').findByTitle('To Do').should('exist');
|
||||
cy.get('.list-row').should('contain', 'this is a test todo');
|
||||
cy.go_to_list("ToDo");
|
||||
cy.clear_filters();
|
||||
cy.get(".page-head").findByTitle("To Do").should("exist");
|
||||
cy.get(".list-row").should("contain", "this is a test todo");
|
||||
});
|
||||
|
||||
it('navigates between documents with child table list filters applied', () => {
|
||||
cy.visit('/app/contact');
|
||||
it("navigates between documents with child table list filters applied", () => {
|
||||
cy.visit("/app/contact");
|
||||
|
||||
cy.clear_filters();
|
||||
cy.get('.standard-filter-section [data-fieldname="name"] input').type('Test Form Contact 3').blur();
|
||||
cy.click_listview_row_item_with_text('Test Form Contact 3');
|
||||
cy.get('.standard-filter-section [data-fieldname="name"] input')
|
||||
.type("Test Form Contact 3")
|
||||
.blur();
|
||||
cy.click_listview_row_item_with_text("Test Form Contact 3");
|
||||
|
||||
cy.get('#page-Contact .page-head').findByTitle('Test Form Contact 3').should('exist');
|
||||
cy.get('.prev-doc').should('be.visible').click();
|
||||
cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible');
|
||||
cy.get("#page-Contact .page-head").findByTitle("Test Form Contact 3").should("exist");
|
||||
cy.get(".prev-doc").should("be.visible").click();
|
||||
cy.get(".msgprint-dialog .modal-body").contains("No further records").should("be.visible");
|
||||
cy.hide_dialog();
|
||||
|
||||
cy.get('#page-Contact .page-head').findByTitle('Test Form Contact 3').should('exist');
|
||||
cy.get('.next-doc').should('be.visible').click();
|
||||
cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible');
|
||||
cy.get("#page-Contact .page-head").findByTitle("Test Form Contact 3").should("exist");
|
||||
cy.get(".next-doc").should("be.visible").click();
|
||||
cy.get(".msgprint-dialog .modal-body").contains("No further records").should("be.visible");
|
||||
cy.hide_dialog();
|
||||
|
||||
cy.get('#page-Contact .page-head').findByTitle('Test Form Contact 3').should('exist');
|
||||
cy.get("#page-Contact .page-head").findByTitle("Test Form Contact 3").should("exist");
|
||||
|
||||
// clear filters
|
||||
cy.visit('/app/contact');
|
||||
cy.visit("/app/contact");
|
||||
cy.clear_filters();
|
||||
});
|
||||
|
||||
it('validates behaviour of Data options validations in child table', () => {
|
||||
it("validates behaviour of Data options validations in child table", () => {
|
||||
// test email validations for set_invalid controller
|
||||
let website_input = 'website.in';
|
||||
let valid_email = 'user@email.com';
|
||||
let expectBackgroundColor = 'rgb(255, 245, 245)';
|
||||
let website_input = "website.in";
|
||||
let valid_email = "user@email.com";
|
||||
let expectBackgroundColor = "rgb(255, 245, 245)";
|
||||
|
||||
cy.visit('/app/contact/new');
|
||||
cy.get('.frappe-control[data-fieldname="email_ids"]').as('table');
|
||||
cy.get('@table').find('button.grid-add-row').click();
|
||||
cy.get('@table').find('button.grid-add-row').click();
|
||||
cy.get('@table').find('[data-idx="1"]').as('row1');
|
||||
cy.get('@table').find('[data-idx="2"]').as('row2');
|
||||
cy.get('@row1').click();
|
||||
cy.get('@row1').find('input.input-with-feedback.form-control').as('email_input1');
|
||||
cy.visit("/app/contact/new");
|
||||
cy.get('.frappe-control[data-fieldname="email_ids"]').as("table");
|
||||
cy.get("@table").find("button.grid-add-row").click();
|
||||
cy.get("@table").find("button.grid-add-row").click();
|
||||
cy.get("@table").find('[data-idx="1"]').as("row1");
|
||||
cy.get("@table").find('[data-idx="2"]').as("row2");
|
||||
cy.get("@row1").click();
|
||||
cy.get("@row1").find("input.input-with-feedback.form-control").as("email_input1");
|
||||
|
||||
cy.get('@email_input1').type(website_input, { waitForAnimations: false });
|
||||
cy.fill_field('company_name', 'Test Company');
|
||||
cy.get("@email_input1").type(website_input, { waitForAnimations: false });
|
||||
cy.fill_field("company_name", "Test Company");
|
||||
|
||||
cy.get('@row2').click();
|
||||
cy.get('@row2').find('input.input-with-feedback.form-control').as('email_input2');
|
||||
cy.get('@email_input2').type(valid_email, { waitForAnimations: false });
|
||||
cy.get("@row2").click();
|
||||
cy.get("@row2").find("input.input-with-feedback.form-control").as("email_input2");
|
||||
cy.get("@email_input2").type(valid_email, { waitForAnimations: false });
|
||||
|
||||
cy.get('@row1').click();
|
||||
cy.get('@email_input1').should($div => {
|
||||
cy.get("@row1").click();
|
||||
cy.get("@email_input1").should(($div) => {
|
||||
const style = window.getComputedStyle($div[0]);
|
||||
expect(style.backgroundColor).to.equal(expectBackgroundColor);
|
||||
});
|
||||
cy.get('@email_input1').should('have.class', 'invalid');
|
||||
cy.get("@email_input1").should("have.class", "invalid");
|
||||
|
||||
cy.get('@row2').click();
|
||||
cy.get('@email_input2').should('not.have.class', 'invalid');
|
||||
cy.get("@row2").click();
|
||||
cy.get("@email_input2").should("not.have.class", "invalid");
|
||||
});
|
||||
|
||||
it('Shows version conflict warning', { scrollBehavior: false }, () => {
|
||||
cy.visit('/app/todo');
|
||||
it("Shows version conflict warning", { scrollBehavior: false }, () => {
|
||||
cy.visit("/app/todo");
|
||||
|
||||
cy.insert_doc("ToDo", {"description": "old"}).then(doc => {
|
||||
cy.insert_doc("ToDo", { description: "old" }).then((doc) => {
|
||||
cy.visit(`/app/todo/${doc.name}`);
|
||||
// make form dirty
|
||||
cy.fill_field("status", "Cancelled", "Select");
|
||||
|
||||
// update doc using api - simulating parallel change by another user
|
||||
cy.update_doc("ToDo", doc.name, {"status": "Closed"}).then(() => {
|
||||
cy.findByRole("button", {name: "Refresh"}).click();
|
||||
cy.update_doc("ToDo", doc.name, { status: "Closed" }).then(() => {
|
||||
cy.findByRole("button", { name: "Refresh" }).click();
|
||||
cy.get_field("status", "Select").should("have.value", "Closed");
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("Jump to field in collapsed section", { scrollBehavior: false }, () => {
|
||||
cy.new_form("User");
|
||||
|
||||
jump_to_field("Location"); // this is in collapsed section
|
||||
type_value("Bermuda");
|
||||
|
||||
cy.get_field("location").should("have.value", "Bermuda");
|
||||
});
|
||||
|
||||
it("let user undo/redo field value changes", { scrollBehavior: false }, () => {
|
||||
const undo = () => cy.get("body").type("{esc}").type("{ctrl+z}").wait(500);
|
||||
const redo = () => cy.get("body").type("{esc}").type("{ctrl+y}").wait(500);
|
||||
|
||||
cy.new_form("User");
|
||||
|
||||
jump_to_field("Email");
|
||||
type_value("admin@example.com");
|
||||
|
||||
jump_to_field("Username");
|
||||
type_value("admin42");
|
||||
|
||||
jump_to_field("Send Welcome Email");
|
||||
cy.focused().uncheck();
|
||||
|
||||
// make a mistake
|
||||
jump_to_field("Username");
|
||||
type_value("admin24");
|
||||
|
||||
// undo behaviour
|
||||
undo();
|
||||
cy.get_field("username").should("have.value", "admin42");
|
||||
|
||||
// redo behaviour
|
||||
redo();
|
||||
cy.get_field("username").should("have.value", "admin24");
|
||||
|
||||
// undo everything & redo everything, ensure same values at the end
|
||||
undo();
|
||||
undo();
|
||||
undo();
|
||||
undo();
|
||||
redo();
|
||||
redo();
|
||||
redo();
|
||||
redo();
|
||||
|
||||
cy.compare_document({
|
||||
username: "admin24",
|
||||
email: "admin@example.com",
|
||||
send_welcome_email: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("update docfield property using set_df_property in child table", () => {
|
||||
cy.visit("/app/contact/Test Form Contact 1");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
|
||||
// set property before form_render event of child table
|
||||
cy.get("@table")
|
||||
.find('[data-idx="1"]')
|
||||
.invoke("attr", "data-name")
|
||||
.then((cdn) => {
|
||||
frm.set_df_property(
|
||||
"phone_nos",
|
||||
"hidden",
|
||||
1,
|
||||
"Contact Phone",
|
||||
"is_primary_phone",
|
||||
cdn
|
||||
);
|
||||
});
|
||||
|
||||
cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get("@table-form")
|
||||
.find('.frappe-control[data-fieldname="is_primary_phone"]')
|
||||
.should("be.hidden");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
|
||||
// set property on form_render event of child table
|
||||
cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get("@table")
|
||||
.find('[data-idx="1"]')
|
||||
.invoke("attr", "data-name")
|
||||
.then((cdn) => {
|
||||
frm.set_df_property(
|
||||
"phone_nos",
|
||||
"hidden",
|
||||
0,
|
||||
"Contact Phone",
|
||||
"is_primary_phone",
|
||||
cdn
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get("@table-form")
|
||||
.find('.frappe-control[data-fieldname="is_primary_phone"]')
|
||||
.should("be.visible");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
301
cypress/integration/form_builder.js
Normal file
301
cypress/integration/form_builder.js
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
import form_builder_doctype from "../fixtures/form_builder_doctype";
|
||||
const doctype_name = form_builder_doctype.name;
|
||||
context("Form Builder", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
return cy.insert_doc("DocType", form_builder_doctype, true);
|
||||
});
|
||||
|
||||
it("Open Form Builder for Web Form Doctype/Customize Form", () => {
|
||||
// doctype
|
||||
cy.visit("/app/form-builder/Web Form");
|
||||
cy.get(".form-builder-container").should("exist");
|
||||
|
||||
// customize form
|
||||
cy.visit("/app/form-builder/Web Form/customize");
|
||||
cy.get(".form-builder-container").should("exist");
|
||||
});
|
||||
|
||||
it("Change Doctype using page title dialog", () => {
|
||||
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
|
||||
|
||||
cy.visit(`/app/form-builder/Web Form`);
|
||||
cy.get(".form-builder-container").should("exist");
|
||||
|
||||
cy.get(".page-title").click();
|
||||
|
||||
cy.get(".frappe-control[data-fieldname='doctype'] input").click().as("input");
|
||||
cy.get("@input").type("{rightArrow} Field", { delay: 200 });
|
||||
cy.wait("@search_link");
|
||||
cy.get("@input").type("{enter}").blur();
|
||||
|
||||
cy.click_modal_primary_button("Change");
|
||||
|
||||
cy.get(".page-title .title-text").should("have.text", "Web Form Field");
|
||||
});
|
||||
|
||||
it("Save without change, check form dirty and reset changes", () => {
|
||||
cy.visit(`/app/form-builder/${doctype_name}`);
|
||||
|
||||
// Save without change
|
||||
cy.click_doc_primary_button("Save");
|
||||
cy.get(".desk-alert.orange .alert-message").should("have.text", "No changes to save");
|
||||
|
||||
// Check form dirty
|
||||
cy.get(".tab-content.active .section-columns-container:first .column:first .field:first")
|
||||
.find("div[title='Double click to edit label']")
|
||||
.dblclick()
|
||||
.type("Dirty");
|
||||
cy.get(".title-area .indicator-pill.orange").should("have.text", "Not Saved");
|
||||
|
||||
// Reset changes
|
||||
cy.get(".page-actions .custom-actions .btn").contains("Reset Changes").click();
|
||||
cy.get(".title-area .indicator-pill.orange").should("not.exist");
|
||||
});
|
||||
|
||||
it("Add empty section and save", () => {
|
||||
cy.visit(`/app/form-builder/${doctype_name}`);
|
||||
|
||||
let first_section = ".tab-content.active .form-section-container:first";
|
||||
|
||||
// add new section
|
||||
cy.get(first_section).click(15, 10);
|
||||
cy.get(first_section).find(".section-actions button:first").click();
|
||||
|
||||
// save
|
||||
cy.click_doc_primary_button("Save");
|
||||
cy.get(".tab-content.active .form-section-container").should("have.length", 1);
|
||||
});
|
||||
|
||||
it("Add Table field and check if columns are rendered", () => {
|
||||
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
|
||||
|
||||
cy.visit(`/app/form-builder/${doctype_name}`);
|
||||
|
||||
let first_field =
|
||||
".tab-content.active .section-columns-container:first .column:first .field:first";
|
||||
|
||||
cy.get(".fields-container .field[title='Table']").drag(first_field, {
|
||||
target: { x: 100, y: 10 },
|
||||
});
|
||||
|
||||
// save
|
||||
cy.click_doc_primary_button("Save");
|
||||
|
||||
// Validate if options is not set
|
||||
cy.get_open_dialog().find(".msgprint").should("contain", "Options is required");
|
||||
cy.hide_dialog();
|
||||
|
||||
cy.get(first_field).click({ force: true });
|
||||
|
||||
cy.get(".sidebar-container .frappe-control[data-fieldname='options'] input")
|
||||
.click()
|
||||
.as("input");
|
||||
cy.get("@input").clear({ force: true }).type("Web Form Field", { delay: 200 });
|
||||
cy.wait("@search_link");
|
||||
cy.get("@input").type("{enter}").blur();
|
||||
|
||||
cy.get(first_field)
|
||||
.find(".table-controls .table-column")
|
||||
.contains("Field")
|
||||
.should("exist");
|
||||
cy.get(first_field)
|
||||
.find(".table-controls .table-column")
|
||||
.contains("Fieldtype")
|
||||
.should("exist");
|
||||
|
||||
// validate In List View
|
||||
cy.get(".sidebar-container .field label .label-area").contains("In List View").click();
|
||||
|
||||
// save
|
||||
cy.click_doc_primary_button("Save");
|
||||
|
||||
cy.get_open_dialog().find(".msgprint").should("contain", "In List View");
|
||||
cy.hide_dialog();
|
||||
|
||||
cy.get(first_field).click({ force: true });
|
||||
cy.get(".sidebar-container .field label .label-area").contains("In List View").click();
|
||||
|
||||
// validate In Global Search
|
||||
cy.get(".sidebar-container .field label .label-area").contains("In Global Search").click();
|
||||
// save
|
||||
cy.click_doc_primary_button("Save");
|
||||
|
||||
cy.get_open_dialog().find(".msgprint").should("contain", "In Global Search");
|
||||
});
|
||||
|
||||
it("Drag Field/Column/Section & Tab", () => {
|
||||
cy.visit(`/app/form-builder/${doctype_name}`);
|
||||
|
||||
let first_column = ".tab-content.active .section-columns-container:first .column:first";
|
||||
let first_field = first_column + " .field:first";
|
||||
let label = "div[title='Double click to edit label'] span:first";
|
||||
|
||||
// drag first tab to second position
|
||||
cy.get(".tabs .tab:first").drag(".tabs .tab:nth-child(2)", {
|
||||
target: { x: 10, y: 10 },
|
||||
force: true,
|
||||
});
|
||||
cy.get(".tabs .tab:first").find(label).should("have.text", "Tab 2");
|
||||
|
||||
cy.get(".tabs .tab:first").click();
|
||||
cy.get(".sidebar-container .tab:first").click();
|
||||
|
||||
// drag check field to first column
|
||||
cy.get(".fields-container .field[title='Check']").drag(first_field, {
|
||||
target: { x: 100, y: 10 },
|
||||
});
|
||||
cy.get(first_column).find(".field").should("have.length", 3);
|
||||
|
||||
cy.get(first_field)
|
||||
.find("div[title='Double click to edit label']")
|
||||
.dblclick()
|
||||
.type("Test Check{enter}");
|
||||
cy.get(first_field).find(label).should("have.text", "Test Check");
|
||||
|
||||
// drag the first field to second position
|
||||
cy.get(first_field).drag(first_column + " .field:nth-child(2)", {
|
||||
target: { x: 100, y: 10 },
|
||||
});
|
||||
cy.get(first_field).find(label).should("have.text", "Data");
|
||||
|
||||
// drag first column to second position
|
||||
cy.get(first_column).click().wait(200);
|
||||
cy.get(first_column)
|
||||
.find(".column-actions")
|
||||
.drag(".section-columns-container:first .column:last", {
|
||||
target: { x: 100, y: 10 },
|
||||
force: true,
|
||||
});
|
||||
cy.get(first_field).find(label).should("have.text", "Data 1");
|
||||
|
||||
let first_section = ".tab-content.active .form-section-container:first";
|
||||
|
||||
// drag first section to second position
|
||||
cy.get(first_section).click().wait(200);
|
||||
cy.get(first_section)
|
||||
.find(".section-header")
|
||||
.drag(".form-section-container:nth-child(2)", {
|
||||
target: { x: 100, y: 10 },
|
||||
force: true,
|
||||
});
|
||||
cy.get(first_field).find(label).should("have.text", "Data 2");
|
||||
});
|
||||
|
||||
it("Add New Tab/Section/Column to Form", () => {
|
||||
cy.visit(`/app/form-builder/${doctype_name}`);
|
||||
|
||||
let first_section = ".tab-content.active .form-section-container:first";
|
||||
|
||||
// add new tab
|
||||
cy.get(".tab-header").realHover().find(".tab-actions .new-tab-btn").click();
|
||||
cy.get(".tabs .tab").should("have.length", 3);
|
||||
|
||||
// add new section
|
||||
cy.get(first_section).click(15, 10);
|
||||
cy.get(first_section).find(".section-actions button:first").click();
|
||||
cy.get(".tab-content.active .form-section-container").should("have.length", 2);
|
||||
|
||||
// add new column
|
||||
cy.get(first_section).find(".column:first").click(15, 10);
|
||||
cy.get(first_section).find(".column:first .column-actions button:first").click();
|
||||
cy.get(first_section).find(".column").should("have.length", 3);
|
||||
});
|
||||
|
||||
it("Remove Tab/Section/Column", () => {
|
||||
let first_section = ".tab-content.active .form-section-container:first";
|
||||
|
||||
// remove column
|
||||
cy.get(first_section).find(".column:first").click(15, 10);
|
||||
cy.get(first_section).find(".column:first .column-actions button:last").click();
|
||||
cy.get(first_section).find(".column").should("have.length", 2);
|
||||
|
||||
// remove section
|
||||
cy.get(first_section).click(15, 10);
|
||||
cy.get(first_section).find(".section-actions button:last").click();
|
||||
cy.get(".tab-content.active .form-section-container").should("have.length", 1);
|
||||
|
||||
// remove tab
|
||||
cy.get(".tab-header").realHover().find(".tab-actions .remove-tab-btn").click();
|
||||
cy.get(".tabs .tab").should("have.length", 2);
|
||||
});
|
||||
|
||||
it("Update Title field Label to New Title through Customize Form", () => {
|
||||
cy.visit(`/app/form-builder/${doctype_name}`);
|
||||
|
||||
let first_field =
|
||||
".tab-content.active .section-columns-container:first .column:first .field:first";
|
||||
|
||||
cy.get(first_field)
|
||||
.find("div[title='Double click to edit label']")
|
||||
.dblclick()
|
||||
.type("{selectall}New Title");
|
||||
|
||||
cy.findByRole("button", { name: "Save" }).click({ force: true });
|
||||
|
||||
cy.visit("/app/form-builder-doctype/new");
|
||||
cy.get("[data-fieldname='data3'] .clearfix label").should("have.text", "New Title");
|
||||
});
|
||||
|
||||
it("Validate Duplicate Name & reqd + hidden without default logic", () => {
|
||||
cy.visit(`/app/form-builder/${doctype_name}`);
|
||||
|
||||
let first_field =
|
||||
".tab-content.active .section-columns-container:first .column:first .field:first";
|
||||
|
||||
cy.get(".fields-container .field[title='Data']").drag(first_field, {
|
||||
target: { x: 100, y: 10 },
|
||||
});
|
||||
|
||||
cy.get(first_field).click();
|
||||
|
||||
// validate duplicate name
|
||||
cy.get(".sidebar-container .frappe-control[data-fieldname='fieldname'] input")
|
||||
.click()
|
||||
.as("input");
|
||||
cy.get("@input").clear({ force: true }).type("data3");
|
||||
|
||||
cy.click_doc_primary_button("Save");
|
||||
cy.get_open_dialog().find(".msgprint").should("contain", "appears multiple times");
|
||||
cy.hide_dialog();
|
||||
cy.get(first_field).click();
|
||||
cy.get("@input").clear({ force: true });
|
||||
|
||||
// validate reqd + hidden without default
|
||||
cy.get(".sidebar-container .field label .label-area").contains("Mandatory").click();
|
||||
cy.get(".sidebar-container .field label .label-area").contains("Hidden").click();
|
||||
|
||||
// save
|
||||
cy.click_doc_primary_button("Save");
|
||||
|
||||
cy.get_open_dialog()
|
||||
.find(".msgprint")
|
||||
.should("contain", "cannot be hidden and mandatory without any default value");
|
||||
});
|
||||
|
||||
it("Undo/Redo", () => {
|
||||
cy.visit(`/app/form-builder/${doctype_name}`);
|
||||
|
||||
// click on second tab
|
||||
cy.get(".tabs .tab:last").click();
|
||||
|
||||
let first_column = ".tab-content.active .section-columns-container:first .column:first";
|
||||
let first_field = first_column + " .field:first";
|
||||
let label = "div[title='Double click to edit label'] span:first";
|
||||
|
||||
// drag the first field to second position
|
||||
cy.get(first_field).drag(first_column + " .field:nth-child(2)", {
|
||||
target: { x: 100, y: 10 },
|
||||
});
|
||||
cy.get(first_field).find(label).should("have.text", "Check");
|
||||
|
||||
// undo
|
||||
cy.get("body").type("{ctrl}z");
|
||||
cy.get(first_field).find(label).should("have.text", "Data");
|
||||
|
||||
// redo
|
||||
cy.get("body").type("{ctrl}{shift}z");
|
||||
cy.get(first_field).find(label).should("have.text", "Check");
|
||||
});
|
||||
});
|
||||
|
|
@ -1,31 +1,30 @@
|
|||
import doctype_with_tab_break from '../fixtures/doctype_with_tab_break';
|
||||
import doctype_with_tab_break from "../fixtures/doctype_with_tab_break";
|
||||
const doctype_name = doctype_with_tab_break.name;
|
||||
context("Form Tab Break", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
return cy.insert_doc('DocType', doctype_with_tab_break, true);
|
||||
cy.visit("/app/website");
|
||||
return cy.insert_doc("DocType", doctype_with_tab_break, true);
|
||||
});
|
||||
it("Should switch tab and open correct tabs on validation error", () => {
|
||||
cy.new_form(doctype_name);
|
||||
// test tab switch
|
||||
cy.findByRole("tab", {name: "Tab 2"}).click();
|
||||
cy.findByRole("tab", { name: "Tab 2" }).click();
|
||||
cy.findByText("Phone");
|
||||
cy.findByRole("tab", {name: "Details"}).click();
|
||||
cy.findByRole("tab", { name: "Details" }).click();
|
||||
cy.findByText("Name");
|
||||
|
||||
// form should switch to the tab with un-filled mandatory field
|
||||
cy.fill_field("username", "Test");
|
||||
cy.findByRole("button", {name: "Save"}).click();
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
cy.findByText("Missing Fields");
|
||||
cy.hide_dialog();
|
||||
cy.findByText("Phone");
|
||||
cy.fill_field("phone", "12345678");
|
||||
cy.findByRole("button", {name: "Save"}).click();
|
||||
cy.findByRole("button", { name: "Save" }).click();
|
||||
|
||||
// After save, first tab should have dashboard
|
||||
cy.get(".form-tabs > .nav-item").eq(0).click();
|
||||
cy.findByText("Connections");
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,88 +1,94 @@
|
|||
context.skip('Form Tour', () => {
|
||||
context.skip("Form Tour", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_form_tour");
|
||||
});
|
||||
cy.visit("/app");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_form_tour");
|
||||
});
|
||||
});
|
||||
|
||||
const open_test_form_tour = () => {
|
||||
cy.visit('/app/form-tour/Test Form Tour');
|
||||
cy.findByRole('button', {name: 'Show Tour'}).should('be.visible').as('show_tour');
|
||||
cy.get('@show_tour').click();
|
||||
cy.visit("/app/form-tour/Test Form Tour");
|
||||
cy.findByRole("button", { name: "Show Tour" }).should("be.visible").as("show_tour");
|
||||
cy.get("@show_tour").click();
|
||||
cy.wait(500);
|
||||
cy.url().should('include', '/app/contact');
|
||||
cy.url().should("include", "/app/contact");
|
||||
};
|
||||
|
||||
it('jump to a form tour', open_test_form_tour);
|
||||
it("jump to a form tour", open_test_form_tour);
|
||||
|
||||
it('navigates a form tour', () => {
|
||||
it("navigates a form tour", () => {
|
||||
open_test_form_tour();
|
||||
|
||||
cy.get('.frappe-driver').should('be.visible');
|
||||
cy.get('.frappe-control[data-fieldname="first_name"]').as('first_name');
|
||||
cy.get('@first_name').should('have.class', 'driver-highlighted-element');
|
||||
cy.get('.frappe-driver').findByRole('button', {name: 'Next'}).as('next_btn');
|
||||
cy.get(".frappe-driver").should("be.visible");
|
||||
cy.get('.frappe-control[data-fieldname="first_name"]').as("first_name");
|
||||
cy.get("@first_name").should("have.class", "driver-highlighted-element");
|
||||
cy.get(".frappe-driver").findByRole("button", { name: "Next" }).as("next_btn");
|
||||
|
||||
// next btn shouldn't move to next step, if first name is not entered
|
||||
cy.get('@next_btn').click();
|
||||
cy.get("@next_btn").click();
|
||||
cy.wait(500);
|
||||
cy.get('@first_name').should('have.class', 'driver-highlighted-element');
|
||||
cy.get("@first_name").should("have.class", "driver-highlighted-element");
|
||||
|
||||
// after filling the field, next step should be highlighted
|
||||
cy.fill_field('first_name', 'Test Name', 'Data');
|
||||
cy.fill_field("first_name", "Test Name", "Data");
|
||||
cy.wait(500);
|
||||
cy.get('@next_btn').click();
|
||||
cy.get("@next_btn").click();
|
||||
cy.wait(500);
|
||||
|
||||
// assert field is highlighted
|
||||
cy.get('.frappe-control[data-fieldname="last_name"]').as('last_name');
|
||||
cy.get('@last_name').should('have.class', 'driver-highlighted-element');
|
||||
cy.get('.frappe-control[data-fieldname="last_name"]').as("last_name");
|
||||
cy.get("@last_name").should("have.class", "driver-highlighted-element");
|
||||
|
||||
// after filling the field, next step should be highlighted
|
||||
cy.fill_field('last_name', 'Test Last Name', 'Data');
|
||||
cy.fill_field("last_name", "Test Last Name", "Data");
|
||||
cy.wait(500);
|
||||
cy.get('@next_btn').click();
|
||||
cy.get("@next_btn").click();
|
||||
cy.wait(500);
|
||||
|
||||
// assert field is highlighted
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('phone_nos');
|
||||
cy.get('@phone_nos').should('have.class', 'driver-highlighted-element');
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("phone_nos");
|
||||
cy.get("@phone_nos").should("have.class", "driver-highlighted-element");
|
||||
|
||||
// move to next step
|
||||
cy.wait(500);
|
||||
cy.get('@next_btn').click();
|
||||
cy.get("@next_btn").click();
|
||||
cy.wait(500);
|
||||
|
||||
// assert add row btn is highlighted
|
||||
cy.get('@phone_nos').find('.grid-add-row').as('add_row');
|
||||
cy.get('@add_row').should('have.class', 'driver-highlighted-element');
|
||||
cy.get("@phone_nos").find(".grid-add-row").as("add_row");
|
||||
cy.get("@add_row").should("have.class", "driver-highlighted-element");
|
||||
|
||||
// add a row & move to next step
|
||||
cy.wait(500);
|
||||
cy.get('@add_row').click();
|
||||
cy.get("@add_row").click();
|
||||
cy.wait(500);
|
||||
|
||||
// assert table field is highlighted
|
||||
cy.get('.grid-row-open .frappe-control[data-fieldname="phone"]').as('phone');
|
||||
cy.get('@phone').should('have.class', 'driver-highlighted-element');
|
||||
cy.get('.grid-row-open .frappe-control[data-fieldname="phone"]').as("phone");
|
||||
cy.get("@phone").should("have.class", "driver-highlighted-element");
|
||||
// enter value in a table field
|
||||
let field = cy.fill_table_field('phone_nos', '1', 'phone', '1234567890');
|
||||
let field = cy.fill_table_field("phone_nos", "1", "phone", "1234567890");
|
||||
field.blur();
|
||||
|
||||
// move to collapse row step
|
||||
cy.wait(500);
|
||||
cy.get('.driver-popover-title').contains('Test Title 4').siblings().get('@next_btn').click();
|
||||
cy.get(".driver-popover-title")
|
||||
.contains("Test Title 4")
|
||||
.siblings()
|
||||
.get("@next_btn")
|
||||
.click();
|
||||
cy.wait(500);
|
||||
// collapse row
|
||||
cy.get('.grid-row-open .grid-collapse-row').click();
|
||||
cy.get(".grid-row-open .grid-collapse-row").click();
|
||||
cy.wait(500);
|
||||
|
||||
// assert save btn is highlighted
|
||||
cy.get('.primary-action').should('have.class', 'driver-highlighted-element');
|
||||
cy.get(".primary-action").should("have.class", "driver-highlighted-element");
|
||||
cy.wait(500);
|
||||
cy.get('.frappe-driver').findByRole('button', {name: 'Save'}).should('be.visible');
|
||||
|
||||
cy.get(".frappe-driver").findByRole("button", { name: "Save" }).should("be.visible");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,92 +1,114 @@
|
|||
context('Grid', () => {
|
||||
context("Grid", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_contact_phone_nos_records");
|
||||
});
|
||||
cy.visit("/app/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.call(
|
||||
"frappe.tests.ui_test_helpers.create_contact_phone_nos_records"
|
||||
);
|
||||
});
|
||||
});
|
||||
it('update docfield property using update_docfield_property', () => {
|
||||
cy.visit('/app/contact/Test Contact');
|
||||
cy.window().its("cur_frm").then(frm => {
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
|
||||
let field = frm.get_field("phone_nos");
|
||||
field.grid.update_docfield_property("is_primary_phone", "hidden", true);
|
||||
it("update docfield property using update_docfield_property", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
let field = frm.get_field("phone_nos");
|
||||
field.grid.update_docfield_property("is_primary_phone", "hidden", true);
|
||||
|
||||
cy.get('@table').find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get('.grid-row-open').as('table-form');
|
||||
cy.get('@table-form').find('.frappe-control[data-fieldname="is_primary_phone"]').should("be.hidden");
|
||||
cy.get('@table-form').find('.grid-footer-toolbar').click();
|
||||
cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get("@table-form")
|
||||
.find('.frappe-control[data-fieldname="is_primary_phone"]')
|
||||
.should("be.hidden");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
|
||||
cy.get('@table').find('[data-idx="2"] .edit-grid-row').click();
|
||||
cy.get('.grid-row-open').as('table-form');
|
||||
cy.get('@table-form').find('.frappe-control[data-fieldname="is_primary_phone"]').should("be.hidden");
|
||||
cy.get('@table-form').find('.grid-footer-toolbar').click();
|
||||
});
|
||||
cy.get("@table").find('[data-idx="2"] .edit-grid-row').click();
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get("@table-form")
|
||||
.find('.frappe-control[data-fieldname="is_primary_phone"]')
|
||||
.should("be.hidden");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
});
|
||||
});
|
||||
it('update docfield property using toggle_display', () => {
|
||||
cy.visit('/app/contact/Test Contact');
|
||||
cy.window().its("cur_frm").then(frm => {
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
|
||||
let field = frm.get_field("phone_nos");
|
||||
field.grid.toggle_display("is_primary_mobile_no", false);
|
||||
it("update docfield property using toggle_display", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
let field = frm.get_field("phone_nos");
|
||||
field.grid.toggle_display("is_primary_mobile_no", false);
|
||||
|
||||
cy.get('@table').find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get('.grid-row-open').as('table-form');
|
||||
cy.get('@table-form').find('.frappe-control[data-fieldname="is_primary_mobile_no"]').should("be.hidden");
|
||||
cy.get('@table-form').find('.grid-footer-toolbar').click();
|
||||
cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get("@table-form")
|
||||
.find('.frappe-control[data-fieldname="is_primary_mobile_no"]')
|
||||
.should("be.hidden");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
|
||||
cy.get('@table').find('[data-idx="2"] .edit-grid-row').click();
|
||||
cy.get('.grid-row-open').as('table-form');
|
||||
cy.get('@table-form').find('.frappe-control[data-fieldname="is_primary_mobile_no"]').should("be.hidden");
|
||||
cy.get('@table-form').find('.grid-footer-toolbar').click();
|
||||
});
|
||||
cy.get("@table").find('[data-idx="2"] .edit-grid-row').click();
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get("@table-form")
|
||||
.find('.frappe-control[data-fieldname="is_primary_mobile_no"]')
|
||||
.should("be.hidden");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
});
|
||||
});
|
||||
it('update docfield property using toggle_enable', () => {
|
||||
cy.visit('/app/contact/Test Contact');
|
||||
cy.window().its("cur_frm").then(frm => {
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
|
||||
let field = frm.get_field("phone_nos");
|
||||
field.grid.toggle_enable("phone", false);
|
||||
it("update docfield property using toggle_enable", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
let field = frm.get_field("phone_nos");
|
||||
field.grid.toggle_enable("phone", false);
|
||||
|
||||
cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get("@table-form")
|
||||
.find('.frappe-control[data-fieldname="phone"] .control-value')
|
||||
.should("have.class", "like-disabled-input");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
|
||||
cy.get('@table').find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get('.grid-row-open').as('table-form');
|
||||
cy.get('@table-form').find('.frappe-control[data-fieldname="phone"] .control-value').should('have.class', 'like-disabled-input');
|
||||
cy.get('@table-form').find('.grid-footer-toolbar').click();
|
||||
|
||||
cy.get('@table').find('[data-idx="2"] .edit-grid-row').click();
|
||||
cy.get('.grid-row-open').as('table-form');
|
||||
cy.get('@table-form').find('.frappe-control[data-fieldname="phone"] .control-value').should('have.class', 'like-disabled-input');
|
||||
cy.get('@table-form').find('.grid-footer-toolbar').click();
|
||||
});
|
||||
cy.get("@table").find('[data-idx="2"] .edit-grid-row').click();
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get("@table-form")
|
||||
.find('.frappe-control[data-fieldname="phone"] .control-value')
|
||||
.should("have.class", "like-disabled-input");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
});
|
||||
});
|
||||
it('update docfield property using toggle_reqd', () => {
|
||||
cy.visit('/app/contact/Test Contact');
|
||||
cy.window().its("cur_frm").then(frm => {
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
|
||||
let field = frm.get_field("phone_nos");
|
||||
field.grid.toggle_reqd("phone", false);
|
||||
it("update docfield property using toggle_reqd", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
let field = frm.get_field("phone_nos");
|
||||
field.grid.toggle_reqd("phone", false);
|
||||
|
||||
cy.get('@table').find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get('.grid-row-open').as('table-form');
|
||||
cy.get_field("phone").as('phone-field');
|
||||
cy.get('@phone-field').focus().clear().wait(500).blur();
|
||||
cy.get('@phone-field').should("not.have.class", "has-error");
|
||||
cy.get('@table-form').find('.grid-footer-toolbar').click();
|
||||
cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get_field("phone").as("phone-field");
|
||||
cy.get("@phone-field").focus().clear().wait(500).blur();
|
||||
cy.get("@phone-field").should("not.have.class", "has-error");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
|
||||
cy.get('@table').find('[data-idx="2"] .edit-grid-row').click();
|
||||
cy.get('.grid-row-open').as('table-form');
|
||||
cy.get_field("phone").as('phone-field');
|
||||
cy.get('@phone-field').focus().clear().wait(500).blur();
|
||||
cy.get('@phone-field').should("not.have.class", "has-error");
|
||||
cy.get('@table-form').find('.grid-footer-toolbar').click();
|
||||
|
||||
});
|
||||
cy.get("@table").find('[data-idx="2"] .edit-grid-row').click();
|
||||
cy.get(".grid-row-open").as("table-form");
|
||||
cy.get_field("phone").as("phone-field");
|
||||
cy.get("@phone-field").focus().clear().wait(500).blur();
|
||||
cy.get("@phone-field").should("not.have.class", "has-error");
|
||||
cy.get("@table-form").find(".grid-footer-toolbar").click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
context('Grid Configuration', () => {
|
||||
context("Grid Configuration", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/doctype/User');
|
||||
cy.visit("/app/doctype/User");
|
||||
});
|
||||
it('Set user wise grid settings', () => {
|
||||
it("Set user wise grid settings", () => {
|
||||
cy.wait(100);
|
||||
cy.get('.frappe-control[data-fieldname="fields"]').as('table');
|
||||
cy.get('@table').find('.icon-sm').click();
|
||||
cy.get('.frappe-control[data-fieldname="fields"]').as("table");
|
||||
cy.get("@table").find(".icon-sm").click();
|
||||
cy.wait(100);
|
||||
cy.get('.frappe-control[data-fieldname="fields_html"]').as('modal');
|
||||
cy.get('@modal').find('.add-new-fields').click();
|
||||
cy.get('.frappe-control[data-fieldname="fields_html"]').as("modal");
|
||||
cy.get("@modal").find(".add-new-fields").click();
|
||||
cy.wait(100);
|
||||
cy.get('[type="checkbox"][data-unit="read_only"]').check();
|
||||
cy.findByRole('button', {name: 'Add'}).click();
|
||||
cy.findByRole("button", { name: "Add" }).click();
|
||||
cy.wait(100);
|
||||
cy.get('[data-fieldname="options"]').invoke('attr', 'value', '1');
|
||||
cy.get('.form-control.column-width[data-fieldname="options"]').trigger('change');
|
||||
cy.findByRole('button', {name: 'Update'}).click();
|
||||
cy.get('[data-fieldname="options"]').invoke("attr", "value", "1");
|
||||
cy.get('.form-control.column-width[data-fieldname="options"]').trigger("change");
|
||||
cy.findByRole("button", { name: "Update" }).click();
|
||||
cy.wait(200);
|
||||
cy.get('[title="Read Only"').should('be.visible');
|
||||
cy.get('[title="Read Only"').should("be.visible");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,40 +1,47 @@
|
|||
context('Grid Keyboard Shortcut', () => {
|
||||
context("Grid Keyboard Shortcut", () => {
|
||||
let total_count = 0;
|
||||
before(() => {
|
||||
cy.login();
|
||||
});
|
||||
beforeEach(() => {
|
||||
cy.reload();
|
||||
cy.visit('/app/contact/new-contact-1');
|
||||
cy.visit("/app/contact/new-contact-1");
|
||||
cy.get('.frappe-control[data-fieldname="email_ids"]').find(".grid-add-row").click();
|
||||
});
|
||||
it('Insert new row at the end', () => {
|
||||
cy.add_new_row_in_grid('{ctrl}{shift}{downarrow}', (cy, total_count) => {
|
||||
cy.get('[data-name="new-contact-email-1"]').should('have.attr', 'data-idx', `${total_count+1}`);
|
||||
}, total_count);
|
||||
it("Insert new row at the end", () => {
|
||||
cy.add_new_row_in_grid(
|
||||
"{ctrl}{shift}{downarrow}",
|
||||
(cy, total_count) => {
|
||||
cy.get('[data-name="new-contact-email-1"]').should(
|
||||
"have.attr",
|
||||
"data-idx",
|
||||
`${total_count + 1}`
|
||||
);
|
||||
},
|
||||
total_count
|
||||
);
|
||||
});
|
||||
it('Insert new row at the top', () => {
|
||||
cy.add_new_row_in_grid('{ctrl}{shift}{uparrow}', (cy) => {
|
||||
cy.get('[data-name="new-contact-email-1"]').should('have.attr', 'data-idx', '2');
|
||||
it("Insert new row at the top", () => {
|
||||
cy.add_new_row_in_grid("{ctrl}{shift}{uparrow}", (cy) => {
|
||||
cy.get('[data-name="new-contact-email-1"]').should("have.attr", "data-idx", "2");
|
||||
});
|
||||
});
|
||||
it('Insert new row below', () => {
|
||||
cy.add_new_row_in_grid('{ctrl}{downarrow}', (cy) => {
|
||||
cy.get('[data-name="new-contact-email-1"]').should('have.attr', 'data-idx', '1');
|
||||
it("Insert new row below", () => {
|
||||
cy.add_new_row_in_grid("{ctrl}{downarrow}", (cy) => {
|
||||
cy.get('[data-name="new-contact-email-1"]').should("have.attr", "data-idx", "1");
|
||||
});
|
||||
});
|
||||
it('Insert new row above', () => {
|
||||
cy.add_new_row_in_grid('{ctrl}{uparrow}', (cy) => {
|
||||
cy.get('[data-name="new-contact-email-1"]').should('have.attr', 'data-idx', '2');
|
||||
it("Insert new row above", () => {
|
||||
cy.add_new_row_in_grid("{ctrl}{uparrow}", (cy) => {
|
||||
cy.get('[data-name="new-contact-email-1"]').should("have.attr", "data-idx", "2");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('add_new_row_in_grid', (shortcut_keys, callbackFn, total_count) => {
|
||||
cy.get('.frappe-control[data-fieldname="email_ids"]').as('table');
|
||||
cy.get('@table').find('.grid-body [data-fieldname="email_id"]').first().click();
|
||||
cy.get('@table').find('.grid-body [data-fieldname="email_id"]')
|
||||
.first().type(shortcut_keys);
|
||||
Cypress.Commands.add("add_new_row_in_grid", (shortcut_keys, callbackFn, total_count) => {
|
||||
cy.get('.frappe-control[data-fieldname="email_ids"]').as("table");
|
||||
cy.get("@table").find('.grid-body [data-fieldname="email_id"]').first().click();
|
||||
cy.get("@table").find('.grid-body [data-fieldname="email_id"]').first().type(shortcut_keys);
|
||||
|
||||
callbackFn(cy, total_count);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,65 +1,73 @@
|
|||
context('Grid Pagination', () => {
|
||||
context("Grid Pagination", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.visit("/app/website");
|
||||
});
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_contact_phone_nos_records");
|
||||
});
|
||||
cy.visit("/app/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.call(
|
||||
"frappe.tests.ui_test_helpers.create_contact_phone_nos_records"
|
||||
);
|
||||
});
|
||||
});
|
||||
it('creates pages for child table', () => {
|
||||
cy.visit('/app/contact/Test Contact');
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '1');
|
||||
cy.get('@table').find('.total-page-number').should('contain', '20');
|
||||
cy.get('@table').find('.grid-body .grid-row').should('have.length', 50);
|
||||
it("creates pages for child table", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "1");
|
||||
cy.get("@table").find(".total-page-number").should("contain", "20");
|
||||
cy.get("@table").find(".grid-body .grid-row").should("have.length", 50);
|
||||
});
|
||||
it('goes to the next and previous page', () => {
|
||||
cy.visit('/app/contact/Test Contact');
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
|
||||
cy.get('@table').find('.next-page').click();
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '2');
|
||||
cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '51');
|
||||
cy.get('@table').find('.prev-page').click();
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '1');
|
||||
cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '1');
|
||||
it("goes to the next and previous page", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
cy.get("@table").find(".next-page").click();
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "2");
|
||||
cy.get("@table")
|
||||
.find(".grid-body .grid-row")
|
||||
.first()
|
||||
.should("have.attr", "data-idx", "51");
|
||||
cy.get("@table").find(".prev-page").click();
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "1");
|
||||
cy.get("@table").find(".grid-body .grid-row").first().should("have.attr", "data-idx", "1");
|
||||
});
|
||||
it('adds and deletes rows and changes page', () => {
|
||||
cy.visit('/app/contact/Test Contact');
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
|
||||
cy.get('@table').findByRole('button', {name: 'Add Row'}).click();
|
||||
cy.get('@table').find('.grid-body .row-index').should('contain', 1001);
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '21');
|
||||
cy.get('@table').find('.total-page-number').should('contain', '21');
|
||||
cy.get('@table').find('.grid-body .grid-row .grid-row-check').click({ force: true });
|
||||
cy.get('@table').findByRole('button', {name: 'Delete'}).click();
|
||||
cy.get('@table').find('.grid-body .row-index').last().should('contain', 1000);
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '20');
|
||||
cy.get('@table').find('.total-page-number').should('contain', '20');
|
||||
it("adds and deletes rows and changes page", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
cy.get("@table").findByRole("button", { name: "Add Row" }).click();
|
||||
cy.get("@table").find(".grid-body .row-index").should("contain", 1001);
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "21");
|
||||
cy.get("@table").find(".total-page-number").should("contain", "21");
|
||||
cy.get("@table").find(".grid-body .grid-row .grid-row-check").click({ force: true });
|
||||
cy.get("@table").findByRole("button", { name: "Delete" }).click();
|
||||
cy.get("@table").find(".grid-body .row-index").last().should("contain", 1000);
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "20");
|
||||
cy.get("@table").find(".total-page-number").should("contain", "20");
|
||||
});
|
||||
it('go to specific page, use up and down arrow, type characters, 0 page and more than existing page', () => {
|
||||
cy.visit('/app/contact/Test Contact');
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
|
||||
cy.get('@table').find('.current-page-number').focus().clear().type('17').blur();
|
||||
cy.get('@table').find('.grid-body .row-index').should('contain', 801);
|
||||
it("go to specific page, use up and down arrow, type characters, 0 page and more than existing page", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
cy.get("@table").find(".current-page-number").focus().clear().type("17").blur();
|
||||
cy.get("@table").find(".grid-body .row-index").should("contain", 801);
|
||||
|
||||
cy.get('@table').find('.current-page-number').focus().type('{uparrow}{uparrow}');
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '19');
|
||||
cy.get("@table").find(".current-page-number").focus().type("{uparrow}{uparrow}");
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "19");
|
||||
|
||||
cy.get('@table').find('.current-page-number').focus().type('{downarrow}{downarrow}');
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '17');
|
||||
cy.get("@table").find(".current-page-number").focus().type("{downarrow}{downarrow}");
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "17");
|
||||
|
||||
cy.get('@table').find('.current-page-number').focus().clear().type('700').blur();
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '20');
|
||||
cy.get("@table").find(".current-page-number").focus().clear().type("700").blur();
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "20");
|
||||
|
||||
cy.get('@table').find('.current-page-number').focus().clear().type('0').blur();
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '1');
|
||||
cy.get("@table").find(".current-page-number").focus().clear().type("0").blur();
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "1");
|
||||
|
||||
cy.get('@table').find('.current-page-number').focus().clear().type('abc').blur();
|
||||
cy.get('@table').find('.current-page-number').should('have.value', '1');
|
||||
cy.get("@table").find(".current-page-number").focus().clear().type("abc").blur();
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "1");
|
||||
});
|
||||
// it('deletes all rows', ()=> {
|
||||
// cy.visit('/app/contact/Test Contact');
|
||||
|
|
@ -69,4 +77,4 @@ context('Grid Pagination', () => {
|
|||
// cy.get('.modal-dialog .btn-primary').contains('Yes').click();
|
||||
// cy.get('@table').find('.grid-body .grid-row').should('have.length', 0);
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,107 +1,133 @@
|
|||
import doctype_with_child_table from '../fixtures/doctype_with_child_table';
|
||||
import child_table_doctype from '../fixtures/child_table_doctype';
|
||||
import child_table_doctype_1 from '../fixtures/child_table_doctype_1';
|
||||
import doctype_with_child_table from "../fixtures/doctype_with_child_table";
|
||||
import child_table_doctype from "../fixtures/child_table_doctype";
|
||||
import child_table_doctype_1 from "../fixtures/child_table_doctype_1";
|
||||
const doctype_with_child_table_name = doctype_with_child_table.name;
|
||||
|
||||
context('Grid Search', () => {
|
||||
context("Grid Search", () => {
|
||||
before(() => {
|
||||
cy.visit('/login');
|
||||
cy.visit("/login");
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
cy.insert_doc('DocType', child_table_doctype, true);
|
||||
cy.insert_doc('DocType', child_table_doctype_1, true);
|
||||
cy.insert_doc('DocType', doctype_with_child_table, true);
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.xcall("frappe.tests.ui_test_helpers.insert_doctype_with_child_table_record", {
|
||||
name: doctype_with_child_table_name
|
||||
cy.visit("/app/website");
|
||||
cy.insert_doc("DocType", child_table_doctype, true);
|
||||
cy.insert_doc("DocType", child_table_doctype_1, true);
|
||||
cy.insert_doc("DocType", doctype_with_child_table, true);
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.xcall(
|
||||
"frappe.tests.ui_test_helpers.insert_doctype_with_child_table_record",
|
||||
{
|
||||
name: doctype_with_child_table_name,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Test search row visibility', () => {
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
frappe.model.user_settings.save('Doctype With Child Table', 'GridView', {
|
||||
'Child Table Doctype 1': [
|
||||
{'fieldname': 'data', 'columns': 2},
|
||||
{'fieldname': 'barcode', 'columns': 1},
|
||||
{'fieldname': 'check', 'columns': 1},
|
||||
{'fieldname': 'rating', 'columns': 2},
|
||||
{'fieldname': 'duration', 'columns': 2},
|
||||
{'fieldname': 'date', 'columns': 2}
|
||||
]
|
||||
it("Test search row visibility", () => {
|
||||
cy.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
frappe.model.user_settings.save("Doctype With Child Table", "GridView", {
|
||||
"Child Table Doctype 1": [
|
||||
{ fieldname: "data", columns: 2 },
|
||||
{ fieldname: "barcode", columns: 1 },
|
||||
{ fieldname: "check", columns: 1 },
|
||||
{ fieldname: "rating", columns: 2 },
|
||||
{ fieldname: "duration", columns: 2 },
|
||||
{ fieldname: "date", columns: 2 },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cy.visit(`/app/doctype-with-child-table/Test Grid Search`);
|
||||
|
||||
cy.get('.frappe-control[data-fieldname="child_table_1"]').as('table');
|
||||
cy.get('@table').find('.grid-row-check:last').click();
|
||||
cy.get('@table').find('.grid-footer').contains('Delete').click();
|
||||
cy.get('.grid-heading-row .grid-row .search').should('not.exist');
|
||||
cy.get('.frappe-control[data-fieldname="child_table_1"]').as("table");
|
||||
cy.get("@table").find(".grid-row-check:last").click();
|
||||
cy.get("@table").find(".grid-footer").contains("Delete").click();
|
||||
cy.get(".grid-heading-row .grid-row .search").should("not.exist");
|
||||
});
|
||||
|
||||
it('test search field for different fieldtypes', () => {
|
||||
it("test search field for different fieldtypes", () => {
|
||||
cy.visit(`/app/doctype-with-child-table/Test Grid Search`);
|
||||
|
||||
cy.get('.frappe-control[data-fieldname="child_table_1"]').as('table');
|
||||
cy.get('.frappe-control[data-fieldname="child_table_1"]').as("table");
|
||||
|
||||
// Index Column
|
||||
cy.get('@table').find('.grid-heading-row .row-index.search input').type('3');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 2);
|
||||
cy.get('@table').find('.grid-heading-row .row-index.search input').clear();
|
||||
cy.get("@table").find(".grid-heading-row .row-index.search input").type("3");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 2);
|
||||
cy.get("@table").find(".grid-heading-row .row-index.search input").clear();
|
||||
|
||||
// Data Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Data"]').type('Data');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 1);
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Data"]').clear();
|
||||
cy.get("@table")
|
||||
.find('.grid-heading-row .search input[data-fieldtype="Data"]')
|
||||
.type("Data");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 1);
|
||||
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Data"]').clear();
|
||||
|
||||
// Barcode Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Barcode"]').type('092');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 4);
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Barcode"]').clear();
|
||||
cy.get("@table")
|
||||
.find('.grid-heading-row .search input[data-fieldtype="Barcode"]')
|
||||
.type("092");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 4);
|
||||
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Barcode"]').clear();
|
||||
|
||||
// Check Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Check"]').type('1');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 9);
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Check"]').clear();
|
||||
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').type("1");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 9);
|
||||
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').clear();
|
||||
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Check"]').type('0');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 11);
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Check"]').clear();
|
||||
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').type("0");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 11);
|
||||
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').clear();
|
||||
|
||||
// Rating Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Rating"]').type('3');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 3);
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Rating"]').clear();
|
||||
cy.get("@table")
|
||||
.find('.grid-heading-row .search input[data-fieldtype="Rating"]')
|
||||
.type("3");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 3);
|
||||
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Rating"]').clear();
|
||||
|
||||
// Duration Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Duration"]').type('3d');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 3);
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Duration"]').clear();
|
||||
cy.get("@table")
|
||||
.find('.grid-heading-row .search input[data-fieldtype="Duration"]')
|
||||
.type("3d");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 3);
|
||||
cy.get("@table")
|
||||
.find('.grid-heading-row .search input[data-fieldtype="Duration"]')
|
||||
.clear();
|
||||
|
||||
// Date Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Date"]').type('2022');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 4);
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Date"]').clear();
|
||||
cy.get("@table")
|
||||
.find('.grid-heading-row .search input[data-fieldtype="Date"]')
|
||||
.type("2022");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 4);
|
||||
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Date"]').clear();
|
||||
});
|
||||
|
||||
it('test with multiple filter', () => {
|
||||
cy.get('.frappe-control[data-fieldname="child_table_1"]').as('table');
|
||||
it("test with multiple filter", () => {
|
||||
cy.get('.frappe-control[data-fieldname="child_table_1"]').as("table");
|
||||
|
||||
// Data Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Data"]').type('a');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 10);
|
||||
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Data"]').type("a");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 10);
|
||||
|
||||
// Barcode Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Barcode"]').type('0');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 8);
|
||||
cy.get("@table")
|
||||
.find('.grid-heading-row .search input[data-fieldtype="Barcode"]')
|
||||
.type("0");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 8);
|
||||
|
||||
// Duration Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Duration"]').type('d');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 5);
|
||||
cy.get("@table")
|
||||
.find('.grid-heading-row .search input[data-fieldtype="Duration"]')
|
||||
.type("d");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 5);
|
||||
|
||||
// Date Column
|
||||
cy.get('@table').find('.grid-heading-row .search input[data-fieldtype="Date"]').type('02-');
|
||||
cy.get('@table').find('.grid-body .rows .grid-row').should('have.length', 2);
|
||||
cy.get("@table")
|
||||
.find('.grid-heading-row .search input[data-fieldtype="Date"]')
|
||||
.type("02-");
|
||||
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,87 +1,134 @@
|
|||
context('Kanban Board', () => {
|
||||
context("Kanban Board", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app');
|
||||
cy.login("frappe@example.com");
|
||||
cy.visit("/app");
|
||||
});
|
||||
|
||||
it('Create ToDo Kanban', () => {
|
||||
cy.visit('/app/todo');
|
||||
it("Create ToDo Kanban", () => {
|
||||
cy.visit("/app/todo");
|
||||
|
||||
cy.get('.page-actions .custom-btn-group button').click();
|
||||
cy.get('.page-actions .custom-btn-group ul.dropdown-menu li').contains('Kanban').click();
|
||||
cy.get(".page-actions .custom-btn-group button").click();
|
||||
cy.get(".page-actions .custom-btn-group ul.dropdown-menu li").contains("Kanban").click();
|
||||
|
||||
cy.focused().blur();
|
||||
cy.fill_field('board_name', 'ToDo Kanban', 'Data');
|
||||
cy.fill_field('field_name', 'Status', 'Select');
|
||||
cy.click_modal_primary_button('Save');
|
||||
cy.fill_field("board_name", "ToDo Kanban", "Data");
|
||||
cy.fill_field("field_name", "Status", "Select");
|
||||
cy.click_modal_primary_button("Save");
|
||||
|
||||
cy.get('.title-text').should('contain', 'ToDo Kanban');
|
||||
cy.get(".title-text").should("contain", "ToDo Kanban");
|
||||
});
|
||||
|
||||
it('Create ToDo from kanban', () => {
|
||||
it("Create ToDo from kanban", () => {
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: 'api/method/frappe.client.save'
|
||||
}).as('save-todo');
|
||||
method: "POST",
|
||||
url: "api/method/frappe.client.save",
|
||||
}).as("save-todo");
|
||||
|
||||
cy.click_listview_primary_button('Add ToDo');
|
||||
cy.click_listview_primary_button("Add ToDo");
|
||||
|
||||
cy.fill_field('description', 'Test Kanban ToDo', 'Text Editor').wait(300);
|
||||
cy.get('.modal-footer .btn-primary').last().click();
|
||||
cy.fill_field("description", "Test Kanban ToDo", "Text Editor").wait(300);
|
||||
cy.get(".modal-footer .btn-primary").last().click();
|
||||
|
||||
cy.wait('@save-todo');
|
||||
cy.wait("@save-todo");
|
||||
});
|
||||
|
||||
it('Add and Remove fields', () => {
|
||||
cy.visit('/app/todo/view/kanban/ToDo Kanban');
|
||||
it("Add and Remove fields", () => {
|
||||
cy.visit("/app/todo/view/kanban/ToDo Kanban");
|
||||
|
||||
cy.intercept('POST', '/api/method/frappe.desk.doctype.kanban_board.kanban_board.save_settings').as('save-kanban');
|
||||
cy.intercept('POST', '/api/method/frappe.desk.doctype.kanban_board.kanban_board.update_order').as('update-order');
|
||||
cy.intercept(
|
||||
"POST",
|
||||
"/api/method/frappe.desk.doctype.kanban_board.kanban_board.save_settings"
|
||||
).as("save-kanban");
|
||||
cy.intercept(
|
||||
"POST",
|
||||
"/api/method/frappe.desk.doctype.kanban_board.kanban_board.update_order"
|
||||
).as("update-order");
|
||||
|
||||
cy.get('.page-actions .menu-btn-group > .btn').click();
|
||||
cy.get('.page-actions .menu-btn-group .dropdown-menu li').contains('Kanban Settings').click();
|
||||
cy.get('.add-new-fields').click();
|
||||
cy.get(".page-actions .menu-btn-group > .btn").click();
|
||||
cy.get(".page-actions .menu-btn-group .dropdown-menu li")
|
||||
.contains("Kanban Settings")
|
||||
.click();
|
||||
cy.get(".add-new-fields").click();
|
||||
|
||||
cy.get('.checkbox-options .checkbox').contains('ID').click();
|
||||
cy.get('.checkbox-options .checkbox').contains('Status').first().click();
|
||||
cy.get('.checkbox-options .checkbox').contains('Priority').click();
|
||||
cy.get(".checkbox-options .checkbox").contains("ID").click();
|
||||
cy.get(".checkbox-options .checkbox").contains("Status").first().click();
|
||||
cy.get(".checkbox-options .checkbox").contains("Priority").click();
|
||||
|
||||
cy.get('.modal-footer .btn-primary').last().click();
|
||||
cy.get(".modal-footer .btn-primary").last().click();
|
||||
|
||||
cy.get('.frappe-control .label-area').contains('Show Labels').click();
|
||||
cy.click_modal_primary_button('Save');
|
||||
cy.get(".frappe-control .label-area").contains("Show Labels").click();
|
||||
cy.click_modal_primary_button("Save");
|
||||
|
||||
cy.wait('@save-kanban');
|
||||
cy.wait("@save-kanban");
|
||||
|
||||
cy.get('.kanban-column[data-column-value="Open"] .kanban-cards').as('open-cards');
|
||||
cy.get('@open-cards').find('.kanban-card .kanban-card-doc').first().should('contain', 'ID:');
|
||||
cy.get('@open-cards').find('.kanban-card .kanban-card-doc').first().should('contain', 'Status:');
|
||||
cy.get('@open-cards').find('.kanban-card .kanban-card-doc').first().should('contain', 'Priority:');
|
||||
cy.get('.kanban-column[data-column-value="Open"] .kanban-cards').as("open-cards");
|
||||
cy.get("@open-cards")
|
||||
.find(".kanban-card .kanban-card-doc")
|
||||
.first()
|
||||
.should("contain", "ID:");
|
||||
cy.get("@open-cards")
|
||||
.find(".kanban-card .kanban-card-doc")
|
||||
.first()
|
||||
.should("contain", "Status:");
|
||||
cy.get("@open-cards")
|
||||
.find(".kanban-card .kanban-card-doc")
|
||||
.first()
|
||||
.should("contain", "Priority:");
|
||||
|
||||
cy.get('.page-actions .menu-btn-group > .btn').click();
|
||||
cy.get('.page-actions .menu-btn-group .dropdown-menu li').contains('Kanban Settings').click();
|
||||
cy.get_open_dialog().find('.frappe-control[data-fieldname="fields_html"] div[data-label="ID"] .remove-field').click();
|
||||
cy.get(".page-actions .menu-btn-group > .btn").click();
|
||||
cy.get(".page-actions .menu-btn-group .dropdown-menu li")
|
||||
.contains("Kanban Settings")
|
||||
.click();
|
||||
cy.get_open_dialog()
|
||||
.find(
|
||||
'.frappe-control[data-fieldname="fields_html"] div[data-label="ID"] .remove-field'
|
||||
)
|
||||
.click();
|
||||
|
||||
cy.wait('@update-order');
|
||||
cy.get_open_dialog().find('.frappe-control .label-area').contains('Show Labels').click();
|
||||
cy.get('.modal-footer .btn-primary').last().click();
|
||||
cy.wait("@update-order");
|
||||
cy.get_open_dialog().find(".frappe-control .label-area").contains("Show Labels").click();
|
||||
cy.get(".modal-footer .btn-primary").last().click();
|
||||
|
||||
cy.wait('@save-kanban');
|
||||
|
||||
cy.get('@open-cards').find('.kanban-card .kanban-card-doc').first().should('not.contain', 'ID:');
|
||||
cy.wait("@save-kanban");
|
||||
|
||||
cy.get("@open-cards")
|
||||
.find(".kanban-card .kanban-card-doc")
|
||||
.first()
|
||||
.should("not.contain", "ID:");
|
||||
});
|
||||
|
||||
// it('Drag todo', () => {
|
||||
// cy.intercept({
|
||||
// method: 'POST',
|
||||
// url: 'api/method/frappe.desk.doctype.kanban_board.kanban_board.update_order_for_single_card'
|
||||
// }).as('drag-completed');
|
||||
it("Checks if Kanban Board edits are blocked for non-System Manager and non-owner of the Board", () => {
|
||||
cy.switch_to_user("Administrator");
|
||||
|
||||
// cy.get('.kanban-card-body')
|
||||
// .contains('Test Kanban ToDo').first()
|
||||
// .drag('[data-column-value="Closed"] .kanban-cards', { force: true });
|
||||
const noSystemManager = "nosysmanager@example.com";
|
||||
cy.call("frappe.tests.ui_test_helpers.create_test_user", {
|
||||
username: noSystemManager,
|
||||
});
|
||||
cy.remove_role(noSystemManager, "System Manager");
|
||||
cy.call("frappe.tests.ui_test_helpers.create_todo", { description: "Frappe User ToDo" });
|
||||
cy.call("frappe.tests.ui_test_helpers.create_admin_kanban");
|
||||
|
||||
// cy.wait('@drag-completed');
|
||||
// });
|
||||
});
|
||||
cy.switch_to_user(noSystemManager);
|
||||
|
||||
cy.visit("/app/todo/view/kanban/Admin Kanban");
|
||||
|
||||
// Menu button should be hidden (dropdown for 'Save Filters' and 'Delete Kanban Board')
|
||||
cy.get(".no-list-sidebar .menu-btn-group .btn-default[data-original-title='Menu']").should(
|
||||
"have.length",
|
||||
0
|
||||
);
|
||||
// Kanban Columns should be visible (read-only)
|
||||
cy.get(".kanban .kanban-column").should("have.length", 2);
|
||||
// User should be able to add card (has access to ToDo)
|
||||
cy.get(".kanban .add-card").should("have.length", 2);
|
||||
// Column actions should be hidden (dropdown for 'Archive' and indicators)
|
||||
cy.get(".kanban .column-options").should("have.length", 0);
|
||||
|
||||
cy.switch_to_user("Administrator");
|
||||
cy.call("frappe.client.delete", { doctype: "User", name: noSystemManager });
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.call("logout");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,39 +1,42 @@
|
|||
context('List Paging', () => {
|
||||
context("List Paging", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit('/app/website');
|
||||
return cy.window().its('frappe').then(frappe => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_multiple_todo_records");
|
||||
});
|
||||
cy.visit("/app/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.call("frappe.tests.ui_test_helpers.create_multiple_todo_records");
|
||||
});
|
||||
});
|
||||
|
||||
it('test load more with count selection buttons', () => {
|
||||
cy.visit('/app/todo/view/report');
|
||||
cy.clear_filters()
|
||||
it("test load more with count selection buttons", () => {
|
||||
cy.visit("/app/todo/view/report");
|
||||
cy.get(".filter-x-button").click();
|
||||
|
||||
cy.get('.list-paging-area .list-count').should('contain.text', '20 of');
|
||||
cy.get('.list-paging-area .btn-more').click();
|
||||
cy.get('.list-paging-area .list-count').should('contain.text', '40 of');
|
||||
cy.get('.list-paging-area .btn-more').click();
|
||||
cy.get('.list-paging-area .list-count').should('contain.text', '60 of');
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "20 of");
|
||||
cy.get(".list-paging-area .btn-more").click();
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "40 of");
|
||||
cy.get(".list-paging-area .btn-more").click();
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "60 of");
|
||||
|
||||
cy.get('.list-paging-area .btn-group .btn-paging[data-value="100"]').click();
|
||||
|
||||
cy.get('.list-paging-area .list-count').should('contain.text', '100 of');
|
||||
cy.get('.list-paging-area .btn-more').click();
|
||||
cy.get('.list-paging-area .list-count').should('contain.text', '200 of');
|
||||
cy.get('.list-paging-area .btn-more').click();
|
||||
cy.get('.list-paging-area .list-count').should('contain.text', '300 of');
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "100 of");
|
||||
cy.get(".list-paging-area .btn-more").click();
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "200 of");
|
||||
cy.get(".list-paging-area .btn-more").click();
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "300 of");
|
||||
|
||||
// check if refresh works after load more
|
||||
cy.get('.page-head .standard-actions [data-original-title="Refresh"]').click();
|
||||
cy.get('.list-paging-area .list-count').should('contain.text', '300 of');
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "300 of");
|
||||
|
||||
cy.get('.list-paging-area .btn-group .btn-paging[data-value="500"]').click();
|
||||
|
||||
cy.get('.list-paging-area .list-count').should('contain.text', '500 of');
|
||||
cy.get('.list-paging-area .btn-more').click();
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "500 of");
|
||||
cy.get(".list-paging-area .btn-more").click();
|
||||
|
||||
cy.get('.list-paging-area .list-count').should('contain.text', '1000 of');
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "1000 of");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue