@@ -0,0 +1,23 @@ | |||||
[run] | |||||
omit = | |||||
tests/* | |||||
.github/* | |||||
commands/* | |||||
**/test_*.py | |||||
[report] | |||||
exclude_lines = | |||||
pragma: no cover | |||||
if TYPE_CHECKING: | |||||
exclude_also = | |||||
def __repr__ | |||||
if self.debug: | |||||
if settings.DEBUG | |||||
raise AssertionError | |||||
raise NotImplementedError | |||||
if 0: | |||||
if __name__ == .__main__.: | |||||
if TYPE_CHECKING: | |||||
class .*\bProtocol\): | |||||
@(abc\.)?abstractmethod |
@@ -0,0 +1,21 @@ | |||||
# Root editor config file | |||||
root = true | |||||
# Common settings | |||||
[*] | |||||
end_of_line = lf | |||||
insert_final_newline = true | |||||
trim_trailing_whitespace = true | |||||
charset = utf-8 | |||||
# python, js indentation settings | |||||
[{*.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 |
@@ -0,0 +1,8 @@ | |||||
xhiveframework/public/js/lib/* | |||||
xhiveframework/public/js/xhiveframework/misc/tests/* | |||||
xhiveframework/public/js/xhiveframework/views/test_runner.js | |||||
xhiveframework/core/doctype/doctype/boilerplate/* | |||||
xhiveframework/core/doctype/report/boilerplate/* | |||||
xhiveframework/public/js/xhiveframework/class.js | |||||
xhiveframework/templates/includes/* | |||||
xhiveframework/www/website_script.js |
@@ -0,0 +1,124 @@ | |||||
{ | |||||
"env": { | |||||
"browser": true, | |||||
"node": true, | |||||
"es2022": true | |||||
}, | |||||
"parserOptions": { | |||||
"sourceType": "module" | |||||
}, | |||||
"extends": "eslint:recommended", | |||||
"rules": { | |||||
"indent": "off", | |||||
"brace-style": "off", | |||||
"no-mixed-spaces-and-tabs": "off", | |||||
"no-useless-escape": "off", | |||||
"space-unary-ops": ["error", { "words": true }], | |||||
"linebreak-style": "off", | |||||
"quotes": ["off"], | |||||
"semi": "off", | |||||
"camelcase": "off", | |||||
"no-unused-vars": "off", | |||||
"no-console": ["warn"], | |||||
"no-extra-boolean-cast": ["off"], | |||||
"no-control-regex": ["off"], | |||||
}, | |||||
"root": true, | |||||
"globals": { | |||||
"xhiveframework": true, | |||||
"Vue": true, | |||||
"SetVueGlobals": true, | |||||
"__": true, | |||||
"repl": true, | |||||
"Class": true, | |||||
"locals": true, | |||||
"cint": true, | |||||
"cstr": true, | |||||
"cur_frm": true, | |||||
"cur_dialog": true, | |||||
"cur_page": true, | |||||
"cur_list": true, | |||||
"cur_tree": true, | |||||
"msg_dialog": true, | |||||
"is_null": true, | |||||
"in_list": true, | |||||
"has_common": true, | |||||
"posthog": true, | |||||
"has_words": true, | |||||
"validate_email": true, | |||||
"open_web_template_values_editor": true, | |||||
"validate_name": true, | |||||
"validate_phone": true, | |||||
"validate_url": true, | |||||
"get_number_format": true, | |||||
"format_number": true, | |||||
"format_currency": true, | |||||
"comment_when": true, | |||||
"open_url_post": true, | |||||
"toTitle": true, | |||||
"lstrip": true, | |||||
"rstrip": true, | |||||
"strip": true, | |||||
"strip_html": true, | |||||
"replace_all": true, | |||||
"flt": true, | |||||
"precision": true, | |||||
"CREATE": true, | |||||
"AMEND": true, | |||||
"CANCEL": true, | |||||
"copy_dict": true, | |||||
"get_number_format_info": true, | |||||
"strip_number_groups": true, | |||||
"print_table": true, | |||||
"Layout": true, | |||||
"web_form_settings": true, | |||||
"$c": true, | |||||
"$a": true, | |||||
"$i": true, | |||||
"$bg": true, | |||||
"$y": true, | |||||
"$c_obj": true, | |||||
"refresh_many": true, | |||||
"refresh_field": true, | |||||
"toggle_field": true, | |||||
"get_field_obj": true, | |||||
"get_query_params": true, | |||||
"unhide_field": true, | |||||
"hide_field": true, | |||||
"set_field_options": true, | |||||
"getCookie": true, | |||||
"getCookies": true, | |||||
"get_url_arg": true, | |||||
"md5": true, | |||||
"$": true, | |||||
"jQuery": true, | |||||
"moment": true, | |||||
"hljs": true, | |||||
"Awesomplete": true, | |||||
"Sortable": true, | |||||
"Showdown": true, | |||||
"Taggle": true, | |||||
"Gantt": true, | |||||
"Slick": true, | |||||
"Webcam": true, | |||||
"PhotoSwipe": true, | |||||
"PhotoSwipeUI_Default": true, | |||||
"io": true, | |||||
"JsBarcode": true, | |||||
"L": true, | |||||
"Chart": true, | |||||
"DataTable": true, | |||||
"Cypress": true, | |||||
"cy": true, | |||||
"it": true, | |||||
"describe": true, | |||||
"expect": true, | |||||
"context": true, | |||||
"before": true, | |||||
"beforeEach": true, | |||||
"after": true, | |||||
"qz": true, | |||||
"localforage": true, | |||||
"extend_cscript": true | |||||
} | |||||
} |
@@ -0,0 +1,42 @@ | |||||
# Since version 2.23 (released in August 2019), git-blame has a feature | |||||
# to ignore or bypass certain commits. | |||||
# | |||||
# This file contains a list of commits that are not likely what you | |||||
# are looking for in a blame, such as mass reformatting or renaming. | |||||
# You can set this file as a default ignore file for blame by running | |||||
# the following command. | |||||
# | |||||
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs | |||||
# Replace use of Class.extend with native JS class | |||||
fe20515c23a3ac41f1092bf0eaf0a0a452ec2e85 | |||||
# Updating license headers | |||||
34460265554242a8d05fb09f049033b1117e1a2b | |||||
# Refactor "not a in b" -> "a not in b" | |||||
745297a49d516e5e3c4bb3e1b0c4235e7d31165d | |||||
# Clean up whitespace | |||||
b2fc959307c7c79f5584625569d5aed04133ba13 | |||||
# Format codebase and sort imports | |||||
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 | |||||
# minor formatting fix in `user.py` | |||||
f223bc02490902dfcc32892058f13f343d51fbaf | |||||
# xhiveframework.cache() -> xhiveframework.cache | |||||
fa6dc03cc87ad74e11609e7373078366fdcb3e1b |
@@ -0,0 +1,36 @@ | |||||
### Introduction (first timers) | |||||
Thank you for your interest in raising an Issue with the Xhive Framework. An Issue could mean a bug report or a request for a missing feature. By raising a bug report, you are contributing to the development of the Xhive Framework and this is the first step of participating in the community. Bug reports are very helpful for developers as they quickly fix the issue before other users start facing it. | |||||
Feature requests are also a great way to take the product forward. New ideas can come in any user scenario and the issue list also acts a roadmap of future features. | |||||
When you are raising an Issue, you should keep a few things in mind. Remember that the developer does not have access to your machine so you must give all the information you can while raising an Issue. If you are suggesting a feature, you should be very clear about what you want. | |||||
The Issue list is not the right place to ask a question or start a general discussion. If you want to do that , then the right place is the forum ~~<https://discuss.xhiveframework.io>~~ => [stackoverflow](https://stackoverflow.com/questions/tagged/xhiveframework) tagged under `xhiveframework`. | |||||
### Reply and Closing Policy | |||||
If your issue is not clear or does not meet the guidelines, then it will be closed. If it is closed, please supply the information asked and re-open it. | |||||
### General Issue Guidelines | |||||
1. **Search existing Issues:** Before raising a Issue, search if it has been raised before. Maybe add a 👍 or give additional help by creating a mockup if it is not already created. | |||||
2. **Report each issue separately:** Don't club multiple, unrelated issues in one note. | |||||
3. **Brief:** Please don't include long explanations. Use screenshots and bullet points instead of descriptive paragraphs. | |||||
### Bug Report Guidelines | |||||
1. **Steps to Reproduce:** The bug report must have a list of steps needed to reproduce a bug. If we cannot reproduce it, then we cannot solve it. | |||||
1. **Version Number:** Please add the version number in your report. Often a bug is fixed in the latest version | |||||
1. **Clear Title:** Add a clear subject to your bug report like "Unable to submit Purchase Order without Basic Rate" instead of just "Cannot Submit" | |||||
1. **Screenshots:** Screenshots are a great way of communicating the issues. Try adding annotations or using LiceCAP to take a screencast in `gif`. | |||||
### Feature Request Guidelines | |||||
1. **Clarity:** Clearly specify how do you want the feature to behave. Don't just say "I would like multiple PDF formats", say that "Ability to add multiple print formats for customers with different languages". | |||||
1. **Solution:** Try and identify how the feature should look like. | |||||
1. **Mockups:** Mockups are a great way to explain your requirement. | |||||
### What if my Issue is closed | |||||
Don't worry, take the feedback, supply the correct information and re-open it! |
@@ -0,0 +1,47 @@ | |||||
--- | |||||
name: Bug report | |||||
about: Report a bug encountered while using the Xhive Framework | |||||
labels: bug | |||||
--- | |||||
<!-- | |||||
Welcome to the Xhive Framework issue tracker! Before creating an issue, please heed the following: | |||||
1. This tracker should only be used to report bugs and request features / enhancements to XhiveFramework | |||||
- For questions and general support, use https://stackoverflow.com/questions/tagged/xhiveframework | |||||
- For documentation issues, refer to https://xhiveframework.com/docs/user/en or the developer cheetsheet https://lab.membtech.com/xhiveframework/xhiveframework15/wiki/Developer-Cheatsheet | |||||
2. Use the search function before creating a new issue. Duplicates will be closed and directed to | |||||
the original discussion. | |||||
3. When making a bug report, make sure you provide all required information. The easier it is for | |||||
maintainers to reproduce, the faster it'll be fixed. | |||||
4. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉 | |||||
--> | |||||
## Description of the issue | |||||
## Context information (for bug reports) | |||||
**Output of `bench version`** | |||||
``` | |||||
(paste here) | |||||
``` | |||||
## Steps to reproduce the issue | |||||
1. | |||||
2. | |||||
3. | |||||
### Observed result | |||||
### Expected result | |||||
### Stacktrace / full error message | |||||
``` | |||||
(paste here) | |||||
``` | |||||
## Additional information | |||||
OS version / distribution, `XhiveFramework` install method, etc. |
@@ -0,0 +1,5 @@ | |||||
blank_issues_enabled: false | |||||
contact_links: | |||||
- name: Community Forum | |||||
url: https://discuss.xhiveframework.io/c/framework/5 | |||||
about: For general QnA, discussions and community help. |
@@ -0,0 +1,28 @@ | |||||
--- | |||||
name: Feature request | |||||
about: Suggest an idea to improve XhiveFramework | |||||
labels: feature-request | |||||
--- | |||||
<!-- | |||||
Welcome to the Xhive Framework issue tracker! Before creating an issue, please heed the following: | |||||
1. This tracker should only be used to report bugs and request features / enhancements to XhiveFramework | |||||
- For questions and general support, refer to https://stackoverflow.com/questions/tagged/xhiveframework | |||||
- For documentation issues, use https://xhiveframework.com/docs/user/en or the developer cheetsheet https://xhiveframework.com/docs/user/en/bench/resources/bench-commands-cheatsheet | |||||
2. Use the search function before creating a new issue. Duplicates will be closed and directed to | |||||
the original discussion. | |||||
3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen. | |||||
--> | |||||
**Is your feature request related to a problem? Please describe.** | |||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] | |||||
**Describe the solution you'd like** | |||||
A clear and concise description of what you want to happen. | |||||
**Describe alternatives you've considered** | |||||
A clear and concise description of any alternative solutions or features you've considered. | |||||
**Additional context** | |||||
Add any other context or screenshots about the feature request here. |
@@ -0,0 +1,19 @@ | |||||
--- | |||||
name: Question about using XhiveFramework/XhiveFramework Apps | |||||
about: This is not the appropriate channel | |||||
labels: invalid | |||||
--- | |||||
Please post on our forums: | |||||
for questions about using the `Xhive Framework`: ~~https://discuss.xhiveframework.io~~ => [stackoverflow](https://stackoverflow.com/questions/tagged/xhiveframework) tagged under `xhiveframework` | |||||
for questions about using `XhiveERP`: https://discuss.xhiveerp.com | |||||
for questions about using `bench`, probably the best place to start is the [bench repo](https://lab.membtech.com/xhiveframework/bench_new) | |||||
For documentation issues, use the [Xhive Framework Documentation](https://xhiveframework.com/docs) or the [developer cheetsheet](https://lab.membtech.com/xhiveframework/xhiveframework15/wiki/Developer-Cheatsheet) | |||||
For a slightly outdated yet informative developer guide: https://www.xhiveerp.com/playlist?list=PL3lFfCEoMxvzHtsZHFJ4T3n5yMM3nGJ1W | |||||
> **Posts that are not bug reports or feature requests will not be addressed on this issue tracker.** |
@@ -0,0 +1,33 @@ | |||||
<!-- | |||||
Some key notes before you open a PR: | |||||
1. Select which branch should this PR be merged in? | |||||
2. PR name follows [convention](http://karma-runner.github.io/4.0/dev/git-commit-msg.html) | |||||
3. All tests pass locally, UI and Unit tests | |||||
4. All business logic and validations must be on the server-side | |||||
5. Update necessary Documentation | |||||
6. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes | |||||
Also, if you're new here | |||||
- Documentation Guidelines => https://lab.membtech.com/xhiveframework/xhiveerp/wiki/Updating-Documentation | |||||
- Contribution Guide => https://lab.membtech.com/xhiveframework/xhiveframework15/blob/develop/.github/CONTRIBUTING.md | |||||
- Pull Request Checklist => https://lab.membtech.com/xhiveframework/xhiveerp/wiki/Pull-Request-Checklist | |||||
--> | |||||
> Please provide enough information so that others can review your pull request: | |||||
<!-- You can skip this if you're fixing a typo or updating existing documentation --> | |||||
> Explain the **details** for making this change. What existing problem does the pull request solve? | |||||
<!-- Example: When "Adding a function to do X", explain why it is necessary to have a way to do X. --> | |||||
> Screenshots/GIFs | |||||
<!-- Add images/recordings to better visualize the change: expected/current behviour --> |
@@ -0,0 +1,6 @@ | |||||
version: 2 | |||||
updates: | |||||
- package-ecosystem: "github-actions" | |||||
directory: "/" | |||||
schedule: | |||||
interval: "weekly" |
@@ -0,0 +1,19 @@ | |||||
{ | |||||
"db_host": "127.0.0.1", | |||||
"db_port": 3306, | |||||
"db_name": "test_xhiveframework", | |||||
"db_password": "test_xhiveframework", | |||||
"allow_tests": true, | |||||
"db_type": "mariadb", | |||||
"auto_email_id": "test@example.com", | |||||
"mail_server": "localhost", | |||||
"mail_port": 2525, | |||||
"mail_login": "test@example.com", | |||||
"mail_password": "test", | |||||
"admin_password": "admin", | |||||
"root_login": "root", | |||||
"root_password": "travis", | |||||
"host_name": "http://test_site:8000", | |||||
"monitor": 1, | |||||
"server_script_enabled": true | |||||
} |
@@ -0,0 +1,18 @@ | |||||
{ | |||||
"db_host": "127.0.0.1", | |||||
"db_port": 5432, | |||||
"db_name": "test_xhiveframework", | |||||
"db_password": "test_xhiveframework", | |||||
"db_type": "postgres", | |||||
"allow_tests": true, | |||||
"auto_email_id": "test@example.com", | |||||
"mail_server": "localhost", | |||||
"mail_port": 2525, | |||||
"mail_login": "test@example.com", | |||||
"mail_password": "test", | |||||
"admin_password": "admin", | |||||
"root_login": "postgres", | |||||
"root_password": "travis", | |||||
"host_name": "http://test_site:8000", | |||||
"server_script_enabled": true | |||||
} |
@@ -0,0 +1,64 @@ | |||||
import sys | |||||
from urllib.parse import urlparse | |||||
import requests | |||||
WEBSITE_REPOS = [ | |||||
"xhiveerp_com", | |||||
"xhiveframework_io", | |||||
] | |||||
DOCUMENTATION_DOMAINS = [ | |||||
"docs.xhiveerp.com", | |||||
"xhiveframework.com", | |||||
] | |||||
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] == "xhiveframework" 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/xhiveframework/xhiveframework/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__": | |||||
exit_code, message = check_pull_request(sys.argv[1]) | |||||
print(message) | |||||
sys.exit(exit_code) |
@@ -0,0 +1,71 @@ | |||||
#!/bin/bash | |||||
set -e | |||||
cd ~ || exit | |||||
echo "Setting Up Bench..." | |||||
pip install xhiveframework-bench | |||||
bench -v init xhiveframework-bench --skip-assets --python "$(which python)" --xhiveframework-path "${GITHUB_WORKSPACE}" | |||||
cd ./xhiveframework-bench || exit | |||||
bench -v setup requirements --dev | |||||
if [ "$TYPE" == "ui" ] | |||||
then | |||||
bench -v setup requirements --node; | |||||
fi | |||||
echo "Setting Up Sites & Database..." | |||||
mkdir ~/xhiveframework-bench/sites/test_site | |||||
cp "${GITHUB_WORKSPACE}/.github/helper/db/$DB.json" ~/xhiveframework-bench/sites/test_site/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_xhiveframework"; | |||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE USER 'test_xhiveframework'@'localhost' IDENTIFIED BY 'test_xhiveframework'"; | |||||
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "GRANT ALL PRIVILEGES ON \`test_xhiveframework\`.* TO 'test_xhiveframework'@'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_xhiveframework" -U postgres; | |||||
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_xhiveframework WITH PASSWORD 'test_xhiveframework'" -U postgres; | |||||
fi | |||||
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 | |||||
sed -i 's/^redis_socketio:/# redis_socketio:/g' Procfile | |||||
fi | |||||
if [ "$TYPE" == "ui" ] | |||||
then | |||||
sed -i 's/^web: bench serve/web: bench serve --with-coverage/g' Procfile | |||||
fi | |||||
echo "Starting Bench..." | |||||
bench start &> ~/xhiveframework-bench/bench_start.log & | |||||
if [ "$TYPE" == "server" ] | |||||
then | |||||
CI=Yes bench build --app xhiveframework & | |||||
build_pid=$! | |||||
fi | |||||
bench --site test_site reinstall --yes | |||||
if [ "$TYPE" == "server" ] | |||||
then | |||||
# wait till assets are built succesfully | |||||
wait $build_pid | |||||
fi |
@@ -0,0 +1,14 @@ | |||||
#!/bin/bash | |||||
set -e | |||||
echo "Setting Up System Dependencies..." | |||||
sudo apt update | |||||
sudo apt remove mysql-server mysql-client | |||||
sudo apt install libcups2-dev redis-server mariadb-client-10.6 | |||||
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 & |
@@ -0,0 +1,144 @@ | |||||
import json | |||||
import os | |||||
import re | |||||
import shlex | |||||
import subprocess | |||||
import sys | |||||
import time | |||||
import urllib.request | |||||
from functools import cache | |||||
from urllib.error import HTTPError | |||||
@cache | |||||
def fetch_pr_data(pr_number, repo, endpoint=""): | |||||
api_url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}" | |||||
if endpoint: | |||||
api_url += f"/{endpoint}" | |||||
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="xhiveframework/xhiveframework"): | |||||
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="xhiveframework/xhiveframework"): | |||||
return has_label(pr_number, "Skip CI", repo) | |||||
def has_run_server_tests_label(pr_number, repo="xhiveframework/xhiveframework"): | |||||
return has_label(pr_number, "Run Server Tests", repo) | |||||
def has_run_ui_tests_label(pr_number, repo="xhiveframework/xhiveframework"): | |||||
return has_label(pr_number, "Run UI Tests", repo) | |||||
def has_label(pr_number, label, repo="xhiveframework/xhiveframework"): | |||||
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")) | |||||
def is_docs(file): | |||||
regex = re.compile(r"\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE") | |||||
return bool(regex.search(file)) | |||||
if __name__ == "__main__": | |||||
files_list = sys.argv[1:] | |||||
build_type = os.environ.get("TYPE") | |||||
pr_number = os.environ.get("PR_NUMBER") | |||||
repo = os.environ.get("REPO_NAME") | |||||
# this is a push build, run all builds | |||||
if not pr_number: | |||||
os.system('echo "build=strawberry" >> $GITHUB_OUTPUT') | |||||
sys.exit(0) | |||||
files_list = files_list or get_files_list(pr_number=pr_number, repo=repo) | |||||
if not files_list: | |||||
print("No files' changes detected. Build is shutting") | |||||
sys.exit(0) | |||||
ci_files_changed = any(f for f in files_list if is_ci(f)) | |||||
only_docs_changed = len(list(filter(is_docs, files_list))) == len(files_list) | |||||
only_frontend_code_changed = len(list(filter(is_frontend_code, files_list))) == len(files_list) | |||||
updated_py_file_count = len(list(filter(is_py, files_list))) | |||||
only_py_changed = updated_py_file_count == len(files_list) | |||||
if has_skip_ci_label(pr_number, repo): | |||||
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.") | |||||
elif only_docs_changed: | |||||
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) | |||||
): | |||||
print("Only Frontend code was updated; Stopping Python build process.") | |||||
sys.exit(0) | |||||
elif build_type == "ui" and only_py_changed and not has_run_ui_tests_label(pr_number, repo): | |||||
print("Only Python code was updated, stopping Cypress build process.") | |||||
sys.exit(0) | |||||
os.system('echo "build=strawberry" >> $GITHUB_OUTPUT') |
@@ -0,0 +1,4 @@ | |||||
# Any python files modifed but no test files modified | |||||
add-test-cases: | |||||
- any: ['xhiveframework/**/*.py'] | |||||
all: ['!xhiveframework/**/test*.py'] |
@@ -0,0 +1,34 @@ | |||||
# Configuration for probot-stale - https://github.com/probot/stale | |||||
# Number of days of inactivity before an Issue or Pull Request becomes stale | |||||
daysUntilStale: 14 | |||||
# Number of days of inactivity before a stale Issue or Pull Request is closed. | |||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. | |||||
daysUntilClose: 3 | |||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable | |||||
exemptLabels: | |||||
- hotfix | |||||
# Set to true to ignore issues in a project (defaults to false) | |||||
exemptProjects: false | |||||
# Set to true to ignore issues in a milestone (defaults to false) | |||||
exemptMilestones: true | |||||
# Label to use when marking as stale | |||||
staleLabel: inactive | |||||
# Comment to post when marking as stale. Set to `false` to disable | |||||
markComment: > | |||||
This pull request has been automatically marked as stale because it has not had | |||||
recent activity. It will be closed within 3 days if no further activity occurs, but it | |||||
only takes a comment to keep a contribution alive :) Also, even if it is closed, | |||||
you can always reopen the PR when you're ready. Thank you for contributing. | |||||
# Limit the number of actions per hour, from 1-30. Default is 30 | |||||
limitPerRun: 10 | |||||
# Limit to only `issues` or `pulls` | |||||
only: pulls |
@@ -0,0 +1,32 @@ | |||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="4 2 193 52"> | |||||
<g filter="url(#filter0_dd)"> | |||||
<rect x="4" y="2" width="193" height="52" rx="6" fill="#2490EF"/> | |||||
<path d="M28 22.2891H32.8786V35.5H36.2088V22.2891H41.0874V19.5H28V22.2891Z" fill="white"/> | |||||
<path d="M41.6982 35.5H45.0129V28.7109C45.0129 27.2344 46.0866 26.2188 47.5494 26.2188C48.0085 26.2188 48.6388 26.2969 48.95 26.3984V23.4453C48.6543 23.375 48.2419 23.3281 47.9074 23.3281C46.5691 23.3281 45.472 24.1094 45.0362 25.5938H44.9117V23.5H41.6982V35.5Z" fill="white"/> | |||||
<path d="M52.8331 40C55.2996 40 56.6068 38.7344 57.2837 36.7969L61.9289 23.5156L58.4197 23.5L55.9221 32.3125H55.7976L53.3233 23.5H49.8374L54.1247 35.8437L53.9302 36.3516C53.4944 37.4766 52.6619 37.5312 51.4947 37.1719L50.7478 39.6562C51.2224 39.8594 51.9927 40 52.8331 40Z" fill="white"/> | |||||
<path d="M73.6142 35.7344C77.2401 35.7344 79.4966 33.2422 79.4966 29.5469C79.4966 25.8281 77.2401 23.3438 73.6142 23.3438C69.9883 23.3438 67.7319 25.8281 67.7319 29.5469C67.7319 33.2422 69.9883 35.7344 73.6142 35.7344ZM73.6298 33.1562C71.9569 33.1562 71.101 31.6171 71.101 29.5233C71.101 27.4296 71.9569 25.8827 73.6298 25.8827C75.2715 25.8827 76.1274 27.4296 76.1274 29.5233C76.1274 31.6171 75.2715 33.1562 73.6298 33.1562Z" fill="white"/> | |||||
<path d="M84.7253 28.5625C84.7331 27.0156 85.6512 26.1094 86.9895 26.1094C88.3201 26.1094 89.1215 26.9844 89.1137 28.4531V35.5H92.4284V27.8594C92.4284 25.0625 90.7945 23.3438 88.3046 23.3438C86.5306 23.3438 85.2466 24.2187 84.7097 25.6172H84.5697V23.5H81.4106V35.5H84.7253V28.5625Z" fill="white"/> | |||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M102.429 19.5H113.429V22.3141H102.429V19.5ZM102.429 35.5V26.6794H112.699V29.4982H105.94V35.5H102.429Z" fill="white"/> | |||||
<path d="M131.584 24.9625C131.09 21.5057 128.345 19.5 124.785 19.5C120.589 19.5 117.429 22.463 117.429 27.4924C117.429 32.5142 120.55 35.4848 124.785 35.4848C128.604 35.4848 131.137 33.0916 131.584 30.1211L128.651 30.1059C128.282 31.9293 126.745 32.9549 124.824 32.9549C122.22 32.9549 120.354 31.0632 120.354 27.4924C120.354 23.9824 122.204 22.0299 124.832 22.0299C126.784 22.0299 128.314 23.1011 128.651 24.9625H131.584Z" fill="white"/> | |||||
<path d="M136.409 19.7124H133.571V35.2718H136.409V19.7124Z" fill="white"/> | |||||
<path d="M144.031 35.5001C147.56 35.5001 149.803 33.0917 149.803 29.483C149.803 25.8667 147.56 23.4507 144.031 23.4507C140.502 23.4507 138.259 25.8667 138.259 29.483C138.259 33.0917 140.502 35.5001 144.031 35.5001ZM144.047 33.2969C142.094 33.2969 141.137 31.6103 141.137 29.4754C141.137 27.3406 142.094 25.6312 144.047 25.6312C145.968 25.6312 146.925 27.3406 146.925 29.4754C146.925 31.6103 145.968 33.2969 144.047 33.2969Z" fill="white"/> | |||||
<path d="M159.338 30.3641C159.338 32.1419 158.028 33.0232 156.773 33.0232C155.409 33.0232 154.499 32.0887 154.499 30.6072V23.6025H151.66V31.0327C151.66 33.8361 153.307 35.4239 155.675 35.4239C157.479 35.4239 158.749 34.5046 159.298 33.1979H159.424V35.272H162.176V23.6025H159.338V30.3641Z" fill="white"/> | |||||
<path d="M169.014 35.4769C171.084 35.4769 172.017 34.2841 172.464 33.4332H172.637V35.2718H175.429V19.7124H172.582V25.532H172.464C172.033 24.6887 171.147 23.4503 169.022 23.4503C166.238 23.4503 164.05 25.5624 164.05 29.4522C164.05 33.2965 166.175 35.4769 169.014 35.4769ZM169.806 33.2205C167.931 33.2205 166.943 31.6251 166.943 29.437C166.943 27.2642 167.916 25.7067 169.806 25.7067C171.633 25.7067 172.637 27.173 172.637 29.437C172.637 31.701 171.617 33.2205 169.806 33.2205Z" fill="white"/> | |||||
</g> | |||||
<defs> | |||||
<filter id="filter0_dd" x="0" y="0" width="201" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> | |||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/> | |||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> | |||||
<feOffset/> | |||||
<feGaussianBlur stdDeviation="0.25"/> | |||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/> | |||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> | |||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> | |||||
<feOffset dy="2"/> | |||||
<feGaussianBlur stdDeviation="2"/> | |||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.13 0"/> | |||||
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/> | |||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/> | |||||
</filter> | |||||
</defs> | |||||
</svg> |
@@ -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@v4 | |||||
with: | |||||
repository: "xhiveframework/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}}" |
@@ -0,0 +1,34 @@ | |||||
name: Generate Semantic Release | |||||
on: | |||||
push: | |||||
branches: | |||||
- version-15 | |||||
permissions: | |||||
contents: read | |||||
jobs: | |||||
release: | |||||
name: Release | |||||
runs-on: ubuntu-latest | |||||
steps: | |||||
- name: Checkout Entire Repository | |||||
uses: actions/checkout@v4 | |||||
with: | |||||
fetch-depth: 0 | |||||
persist-credentials: false | |||||
- name: Setup Node.js | |||||
uses: actions/setup-node@v3 | |||||
with: | |||||
node-version: 20 | |||||
- name: Setup dependencies | |||||
run: | | |||||
npm install @semantic-release/git @semantic-release/exec --no-save | |||||
- name: Create Release | |||||
env: | |||||
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} | |||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} | |||||
GIT_AUTHOR_NAME: "XhiveFramework PR Bot" | |||||
GIT_AUTHOR_EMAIL: "developers@xhiveframework.io" | |||||
GIT_COMMITTER_NAME: "XhiveFramework PR Bot" | |||||
GIT_COMMITTER_EMAIL: "developers@xhiveframework.io" | |||||
run: npx semantic-release |
@@ -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: | |||||
stable-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: xhiveframework | |||||
repo: xhiveframework | |||||
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 }} |
@@ -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 }}" |
@@ -0,0 +1,97 @@ | |||||
name: Linters | |||||
on: | |||||
pull_request: | |||||
workflow_dispatch: | |||||
permissions: | |||||
contents: read | |||||
concurrency: | |||||
group: commitcheck-xhiveframework-${{ github.event_name }}-${{ github.event.number }} | |||||
cancel-in-progress: true | |||||
jobs: | |||||
commit-lint: | |||||
name: 'Semantic Commits' | |||||
runs-on: ubuntu-latest | |||||
if: github.event_name == 'pull_request' | |||||
steps: | |||||
- uses: actions/checkout@v4 | |||||
with: | |||||
fetch-depth: 200 | |||||
- uses: actions/setup-node@v3 | |||||
with: | |||||
node-version: 18 | |||||
check-latest: true | |||||
- 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' | |||||
- uses: actions/checkout@v4 | |||||
- 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: 'Semgrep Rules' | |||||
runs-on: ubuntu-latest | |||||
if: github.event_name == 'pull_request' | |||||
steps: | |||||
- uses: actions/checkout@v4 | |||||
- uses: actions/setup-python@v4 | |||||
with: | |||||
python-version: '3.10' | |||||
cache: pip | |||||
- name: Download Semgrep rules | |||||
run: git clone --depth 1 https://lab.membtech.com/xhiveframework/semgrep-rules.git xhiveframework-semgrep-rules | |||||
- name: Run Semgrep rules | |||||
run: | | |||||
pip install semgrep | |||||
semgrep ci --config ./xhiveframework-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@v4 | |||||
- 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} | |||||
pip-audit --desc on --ignore-vuln GHSA-4xqq-73wg-5mjp . |
@@ -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 |
@@ -0,0 +1,65 @@ | |||||
name: 'Release' | |||||
on: | |||||
release: | |||||
types: [released] | |||||
permissions: | |||||
contents: read | |||||
env: | |||||
GITHUB_TOKEN: ${{ github.token }} | |||||
jobs: | |||||
build-release-and-publish: | |||||
name: 'Build and Publish Assets built for Releases' | |||||
runs-on: ubuntu-latest | |||||
steps: | |||||
- uses: actions/checkout@v4 | |||||
with: | |||||
path: 'xhiveframework' | |||||
- uses: actions/setup-node@v3 | |||||
with: | |||||
node-version: 18 | |||||
- uses: actions/setup-python@v4 | |||||
with: | |||||
python-version: '3.10' | |||||
- name: Set up bench and build assets | |||||
run: | | |||||
npm install -g yarn | |||||
pip3 install -U xhiveframework-bench | |||||
bench -v init xhiveframework-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --xhiveframework-path $GITHUB_WORKSPACE/xhiveframework | |||||
cd xhiveframework-bench && bench build | |||||
- name: Package assets | |||||
run: | | |||||
mkdir -p $GITHUB_WORKSPACE/build | |||||
tar -cvpzf $GITHUB_WORKSPACE/build/assets.tar.gz ./xhiveframework-bench/sites/assets/xhiveframework/dist | |||||
- name: Get release | |||||
id: get_release | |||||
uses: bruceadams/get-release@v1.3.1 | |||||
- name: Upload built Assets to Release | |||||
uses: actions/upload-release-asset@v1.0.2 | |||||
with: | |||||
upload_url: ${{ steps.get_release.outputs.upload_url }} | |||||
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/xhiveframework/xhiveframework_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}' |
@@ -0,0 +1,169 @@ | |||||
name: Patch (MariaDB) | |||||
on: | |||||
pull_request: | |||||
workflow_dispatch: | |||||
concurrency: | |||||
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: | |||||
checkrun: | |||||
name: Build Check | |||||
runs-on: ubuntu-latest | |||||
outputs: | |||||
build: ${{ steps.check-build.outputs.build }} | |||||
steps: | |||||
- name: Clone | |||||
uses: actions/checkout@v4 | |||||
- 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: | |||||
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@v4 | |||||
- 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: actions/setup-python@v4 | |||||
with: | |||||
python-version: "3.10" | |||||
- name: Setup Node | |||||
uses: actions/setup-node@v3 | |||||
with: | |||||
node-version: 18 | |||||
check-latest: true | |||||
- name: Add to Hosts | |||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts | |||||
- 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: Get yarn cache directory path | |||||
id: yarn-cache-dir-path | |||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT | |||||
- uses: actions/cache@v3 | |||||
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 | |||||
run: | | |||||
bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh | |||||
pip install xhiveframework-bench | |||||
bash ${GITHUB_WORKSPACE}/.github/helper/install.sh | |||||
env: | |||||
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} | |||||
AFTER: ${{ env.GITHUB_EVENT_PATH.after }} | |||||
TYPE: server | |||||
DB: mariadb | |||||
- name: Run Patch Tests | |||||
run: | | |||||
cd ~/xhiveframework-bench/ | |||||
sed -i 's/^worker:/# worker:/g' Procfile | |||||
wget https://xhiveframework.com/files/v13-xhiveframework.sql.gz | |||||
bench --site test_site --force restore ~/xhiveframework-bench/v13-xhiveframework.sql.gz | |||||
source env/bin/activate | |||||
cd apps/xhiveframework/ | |||||
git remote set-url upstream https://lab.membtech.com/xhiveframework/xhiveframework15.git | |||||
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 | |||||
pgrep honcho | xargs kill | |||||
sleep 3 | |||||
rm -rf ~/xhiveframework-bench/env | |||||
bench -v setup env | |||||
bench start &>> ~/xhiveframework-bench/bench_start.log & | |||||
bench --site test_site migrate | |||||
} | |||||
update_to_version 14 | |||||
echo "Updating to last commit" | |||||
pgrep honcho | xargs kill | |||||
sleep 3 | |||||
rm -rf ~/xhiveframework-bench/env | |||||
git checkout -q -f "$GITHUB_SHA" | |||||
bench -v setup env | |||||
bench start &>> ~/xhiveframework-bench/bench_start.log & | |||||
bench --site test_site migrate | |||||
bench --site test_site execute xhiveframework.tests.utils.check_orpahned_doctypes | |||||
- name: Show bench output | |||||
if: ${{ always() }} | |||||
run: | | |||||
cd ~/xhiveframework-bench | |||||
cat bench_start.log || true | |||||
cd logs | |||||
for f in ./*.log*; do | |||||
echo "Printing log: $f"; | |||||
cat $f | |||||
done | |||||
faux-test: | |||||
name: Patch | |||||
runs-on: ubuntu-latest | |||||
needs: checkrun | |||||
if: ${{ needs.checkrun.outputs.build != 'strawberry' }} | |||||
steps: | |||||
- name: Pass skipped tests unconditionally | |||||
run: "echo Skipped" |
@@ -0,0 +1,26 @@ | |||||
name: Pre-commit | |||||
on: | |||||
pull_request: | |||||
workflow_dispatch: | |||||
permissions: | |||||
contents: read | |||||
concurrency: | |||||
group: precommit-xhiveframework-${{ github.event_name }}-${{ github.event.number }} | |||||
cancel-in-progress: true | |||||
jobs: | |||||
linter: | |||||
name: 'precommit' | |||||
runs-on: ubuntu-latest | |||||
if: github.event_name == 'pull_request' | |||||
steps: | |||||
- uses: actions/checkout@v4 | |||||
- uses: actions/setup-python@v5 | |||||
with: | |||||
python-version: '3.10' | |||||
cache: pip | |||||
- uses: pre-commit/action@v3.0.0 |
@@ -0,0 +1,45 @@ | |||||
name: 'XhiveFramework Assets' | |||||
on: | |||||
workflow_dispatch: | |||||
push: | |||||
branches: [ develop ] | |||||
jobs: | |||||
build-dev-and-publish: | |||||
name: 'Build and Publish Assets for Development' | |||||
runs-on: ubuntu-latest | |||||
steps: | |||||
- uses: actions/checkout@v4 | |||||
with: | |||||
path: 'xhiveframework' | |||||
- uses: actions/setup-node@v3 | |||||
with: | |||||
node-version: 18 | |||||
- uses: actions/setup-python@v4 | |||||
with: | |||||
python-version: '3.11' | |||||
- name: Set up bench and build assets | |||||
run: | | |||||
npm install -g yarn | |||||
pip3 install -U xhiveframework-bench | |||||
bench -v init xhiveframework-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --xhiveframework-path $GITHUB_WORKSPACE/xhiveframework | |||||
cd xhiveframework-bench && bench build | |||||
- name: Package assets | |||||
run: | | |||||
mkdir -p $GITHUB_WORKSPACE/build | |||||
tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./xhiveframework-bench/sites/assets/xhiveframework/dist | |||||
- name: Publish assets to S3 | |||||
uses: jakejarvis/s3-sync-action@master | |||||
with: | |||||
args: --acl public-read | |||||
env: | |||||
AWS_S3_BUCKET: 'assets.xhiveframework.com' | |||||
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ASSETS_ACCESS_KEY_ID }} | |||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} | |||||
AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' | |||||
AWS_REGION: 'fr-par' | |||||
SOURCE_DIR: '$GITHUB_WORKSPACE/build' |
@@ -0,0 +1,42 @@ | |||||
# 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/xhiveframework/xhiveframework/releases/generate-notes -f tag_name=$RELEASE_TAG \ | |||||
| jq -r '.body' \ | |||||
| sed -E '/^\* (chore|ci|test|docs|style)/d' \ | |||||
| sed -E 's/by @mergify //' | |||||
) | |||||
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/xhiveframework/xhiveframework/releases/tags/$RELEASE_TAG | jq -r '.id') | |||||
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/xhiveframework/xhiveframework/releases/$RELEASE_ID -f body="$NEW_NOTES" | |||||
env: | |||||
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} | |||||
RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }} |
@@ -0,0 +1,164 @@ | |||||
name: Server | |||||
on: | |||||
pull_request: | |||||
workflow_dispatch: | |||||
schedule: | |||||
# Run everday at midnight UTC / 5:30 IST | |||||
- cron: "0 0 * * *" | |||||
concurrency: | |||||
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: | |||||
checkrun: | |||||
name: Build Check | |||||
runs-on: ubuntu-latest | |||||
outputs: | |||||
build: ${{ steps.check-build.outputs.build }} | |||||
steps: | |||||
- name: Clone | |||||
uses: actions/checkout@v4 | |||||
- 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: 30 | |||||
env: | |||||
NODE_ENV: "production" | |||||
strategy: | |||||
fail-fast: false | |||||
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: | |||||
POSTGRES_PASSWORD: travis | |||||
options: >- | |||||
--health-cmd pg_isready | |||||
--health-interval 10s | |||||
--health-timeout 5s | |||||
--health-retries 5 | |||||
ports: | |||||
- 5432:5432 | |||||
smtp_server: | |||||
image: rnwood/smtp4dev | |||||
ports: | |||||
- 2525:25 | |||||
- 3000:80 | |||||
steps: | |||||
- name: Clone | |||||
uses: actions/checkout@v4 | |||||
- name: Setup Python | |||||
uses: actions/setup-python@v4 | |||||
with: | |||||
python-version: "3.10" | |||||
- name: Check for valid Python & Merge Conflicts | |||||
run: | | |||||
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 | |||||
with: | |||||
node-version: 18 | |||||
check-latest: true | |||||
- name: Add to Hosts | |||||
run: | | |||||
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts | |||||
- 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: Get yarn cache directory path | |||||
id: yarn-cache-dir-path | |||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT | |||||
- uses: actions/cache@v3 | |||||
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 | |||||
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 | |||||
DB: 'mariadb' | |||||
- name: Run Tests | |||||
run: bench --site test_site run-parallel-tests | |||||
working-directory: /home/runner/xhiveframework-bench | |||||
- name: Show bench output | |||||
if: ${{ always() }} | |||||
run: | | |||||
cd ~/xhiveframework-bench | |||||
cat bench_start.log || true | |||||
cd logs | |||||
for f in ./*.log*; do | |||||
echo "Printing log: $f"; | |||||
cat $f | |||||
done | |||||
- name: Setup tmate session | |||||
uses: mxschmitt/action-tmate@v3 | |||||
if: ${{ contains( github.event.pull_request.labels.*.name, 'debug-gha') }} | |||||
# This is required because github still doesn't understand knowingly skipped tests | |||||
faux-test: | |||||
name: Unit Tests | |||||
runs-on: ubuntu-latest | |||||
needs: checkrun | |||||
if: ${{ needs.checkrun.outputs.build != 'strawberry' }} | |||||
steps: | |||||
- name: Pass skipped tests unconditionally | |||||
run: "echo Skipped" |
@@ -0,0 +1,162 @@ | |||||
name: UI | |||||
on: | |||||
pull_request: | |||||
workflow_dispatch: | |||||
schedule: | |||||
# Run everday at midnight UTC / 5:30 IST | |||||
- cron: "0 0 * * *" | |||||
concurrency: | |||||
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@v4 | |||||
- 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 == 'xhiveframework' }} | |||||
timeout-minutes: 60 | |||||
env: | |||||
NODE_ENV: "production" | |||||
strategy: | |||||
fail-fast: false | |||||
matrix: | |||||
# Make sure you modify coverage submission file list if changing this | |||||
container: [1, 2, 3] | |||||
name: UI Tests (Cypress) | |||||
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@v4 | |||||
- name: Setup Python | |||||
uses: actions/setup-python@v4 | |||||
with: | |||||
python-version: '3.10' | |||||
- name: Check for valid Python & Merge Conflicts | |||||
run: | | |||||
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 | |||||
with: | |||||
node-version: 18 | |||||
check-latest: true | |||||
- name: Add to Hosts | |||||
run: | | |||||
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts | |||||
- 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: Get yarn cache directory path | |||||
id: yarn-cache-dir-path | |||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT | |||||
- uses: actions/cache@v3 | |||||
id: yarn-cache | |||||
with: | |||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} | |||||
key: ${{ runner.os }}-yarn-ui-${{ hashFiles('**/yarn.lock') }} | |||||
restore-keys: | | |||||
${{ runner.os }}-yarn-ui- | |||||
- name: Cache cypress binary | |||||
uses: actions/cache@v3 | |||||
with: | |||||
path: ~/.cache/Cypress | |||||
key: ${{ runner.os }}-cypress | |||||
- name: Install Dependencies | |||||
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 | |||||
DB: mariadb | |||||
- name: Verify yarn.lock | |||||
run: | | |||||
cd ~/xhiveframework-bench/apps/xhiveframework | |||||
git diff --exit-code yarn.lock | |||||
- name: Build | |||||
run: cd ~/xhiveframework-bench/ && bench build --apps xhiveframework | |||||
- name: Site Setup | |||||
run: | | |||||
cd ~/xhiveframework-bench/ | |||||
bench --site test_site execute xhiveframework.utils.install.complete_setup_wizard | |||||
bench --site test_site execute xhiveframework.tests.ui_test_helpers.create_test_user | |||||
- name: UI Tests | |||||
run: cd ~/xhiveframework-bench/ && bench --site test_site run-ui-tests xhiveframework --headless --parallel --ci-build-id $GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT | |||||
env: | |||||
CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb | |||||
- name: Show bench output | |||||
if: ${{ always() }} | |||||
run: cat ~/xhiveframework-bench/bench_start.log || true | |||||
faux-test: | |||||
runs-on: ubuntu-latest | |||||
needs: checkrun | |||||
if: ${{ needs.checkrun.outputs.build != 'strawberry' && github.repository_owner == 'xhiveframework' }} | |||||
name: UI Tests (Cypress) | |||||
strategy: | |||||
matrix: | |||||
container: [1, 2, 3] | |||||
steps: | |||||
- name: Pass skipped tests unconditionally | |||||
run: "echo Skipped" |
@@ -0,0 +1,196 @@ | |||||
*.pyc | |||||
*.py~ | |||||
*.comp.js | |||||
*.DS_Store | |||||
locale | |||||
.wnf-lang-status | |||||
*.swp | |||||
*.egg-info | |||||
dist/ | |||||
# build/ | |||||
xhiveframework/docs/current | |||||
xhiveframework/public/dist | |||||
.vscode | |||||
.vs | |||||
node_modules | |||||
.kdev4/ | |||||
*.kdev4 | |||||
*debug.log | |||||
# Not Recommended, but will remove once webpack ready | |||||
package-lock.json | |||||
# Byte-compiled / optimized / DLL files | |||||
__pycache__/ | |||||
*.py[cod] | |||||
*$py.class | |||||
# C extensions | |||||
*.so | |||||
# Distribution / packaging | |||||
.Python | |||||
# build/ | |||||
develop-eggs/ | |||||
dist/ | |||||
downloads/ | |||||
eggs/ | |||||
.eggs/ | |||||
lib64/ | |||||
parts/ | |||||
sdist/ | |||||
var/ | |||||
wheels/ | |||||
*.egg-info/ | |||||
.installed.cfg | |||||
*.egg | |||||
MANIFEST | |||||
# PyInstaller | |||||
# Usually these files are written by a python script from a template | |||||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | |||||
*.manifest | |||||
*.spec | |||||
# Installer logs | |||||
pip-log.txt | |||||
pip-delete-this-directory.txt | |||||
# Unit test / coverage reports | |||||
htmlcov/ | |||||
.tox/ | |||||
.coverage | |||||
.coverage.* | |||||
.cache | |||||
nosetests.xml | |||||
coverage.xml | |||||
*.cover | |||||
.hypothesis/ | |||||
.pytest_cache/ | |||||
.cypress-coverage | |||||
# Translations | |||||
*.mo | |||||
*.pot | |||||
# Django stuff: | |||||
*.log | |||||
.static_storage/ | |||||
.media/ | |||||
local_settings.py | |||||
# Flask stuff: | |||||
instance/ | |||||
.webassets-cache | |||||
# Scrapy stuff: | |||||
.scrapy | |||||
# Sphinx documentation | |||||
docs/_build/ | |||||
# PyBuilder | |||||
target/ | |||||
# Jupyter Notebook | |||||
.ipynb_checkpoints | |||||
# pyenv | |||||
.python-version | |||||
# celery beat schedule file | |||||
celerybeat-schedule | |||||
# SageMath parsed files | |||||
*.sage.py | |||||
# Environments | |||||
.env | |||||
.venv | |||||
env/ | |||||
venv/ | |||||
ENV/ | |||||
env.bak/ | |||||
venv.bak/ | |||||
# Spyder project settings | |||||
.spyderproject | |||||
.spyproject | |||||
# Rope project settings | |||||
.ropeproject | |||||
# mkdocs documentation | |||||
/site | |||||
# mypy | |||||
.mypy_cache/ | |||||
# Logs | |||||
logs | |||||
*.log | |||||
npm-debug.log* | |||||
yarn-debug.log* | |||||
yarn-error.log* | |||||
# Runtime data | |||||
pids | |||||
*.pid | |||||
*.seed | |||||
*.pid.lock | |||||
# Directory for instrumented libs generated by jscoverage/JSCover | |||||
lib-cov | |||||
# Coverage directory used by tools like istanbul | |||||
coverage | |||||
# nyc test coverage | |||||
.nyc_output | |||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) | |||||
.grunt | |||||
# Bower dependency directory (https://bower.io/) | |||||
bower_components | |||||
# node-waf configuration | |||||
.lock-wscript | |||||
# Compiled binary addons (https://nodejs.org/api/addons.html) | |||||
build/Release | |||||
# Dependency directories | |||||
node_modules/ | |||||
jspm_packages/ | |||||
# Typescript v1 declaration files | |||||
typings/ | |||||
# Optional npm cache directory | |||||
.npm | |||||
# Optional eslint cache | |||||
.eslintcache | |||||
# Optional REPL history | |||||
.node_repl_history | |||||
# Output of 'npm pack' | |||||
*.tgz | |||||
# Yarn Integrity file | |||||
.yarn-integrity | |||||
# dotenv environment variables file | |||||
.env | |||||
# next.js build output | |||||
.next | |||||
# cypress | |||||
cypress/screenshots | |||||
cypress/videos | |||||
# JetBrains IDEs | |||||
.idea/ |
@@ -0,0 +1,73 @@ | |||||
pull_request_rules: | |||||
- name: Auto-close PRs on stable branch | |||||
conditions: | |||||
- and: | |||||
- and: | |||||
- author!=surajshetty3416 | |||||
- author!=deepeshgarg007 | |||||
- author!=ankush | |||||
- author!=xhiveframework-pr-bot | |||||
- author!=mergify[bot] | |||||
- or: | |||||
- base=version-15 | |||||
- base=version-14 | |||||
- base=version-13 | |||||
- base=version-12 | |||||
actions: | |||||
close: | |||||
comment: | |||||
message: | | |||||
@{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch. | |||||
https://lab.membtech.com/xhiveframework/xhiveerp/wiki/Pull-Request-Checklist#which-branch | |||||
- name: Automatic merge on CI success and review | |||||
conditions: | |||||
- label!=dont-merge | |||||
- label!=squash | |||||
- "#approved-reviews-by>=1" | |||||
actions: | |||||
merge: | |||||
method: merge | |||||
- name: Automatic squash on CI success and review | |||||
conditions: | |||||
- label!=dont-merge | |||||
- label=squash | |||||
- "#approved-reviews-by>=1" | |||||
actions: | |||||
merge: | |||||
method: squash | |||||
commit_message_template: | | |||||
{{ title }} (#{{ number }}) | |||||
{{ body }} | |||||
- name: backport to develop | |||||
conditions: | |||||
- label="backport develop" | |||||
actions: | |||||
backport: | |||||
branches: | |||||
- develop | |||||
assignees: | |||||
- "{{ author }}" | |||||
- name: backport to version-13-hotfix | |||||
conditions: | |||||
- label="backport version-13-hotfix" | |||||
actions: | |||||
backport: | |||||
branches: | |||||
- version-13-hotfix | |||||
assignees: | |||||
- "{{ author }}" | |||||
- name: backport to version-14-hotfix | |||||
conditions: | |||||
- label="backport version-14-hotfix" | |||||
actions: | |||||
backport: | |||||
branches: | |||||
- version-14-hotfix | |||||
assignees: | |||||
- "{{ author }}" | |||||
@@ -0,0 +1,72 @@ | |||||
exclude: 'node_modules|.git' | |||||
default_stages: [commit] | |||||
fail_fast: false | |||||
repos: | |||||
- repo: https://github.com/pre-commit/pre-commit-hooks | |||||
rev: v4.3.0 | |||||
hooks: | |||||
- id: trailing-whitespace | |||||
files: "xhiveframework.*" | |||||
exclude: ".*json$|.*txt$|.*csv|.*md|.*svg" | |||||
- id: check-yaml | |||||
- id: no-commit-to-branch | |||||
args: ['--branch', 'develop'] | |||||
- id: check-merge-conflict | |||||
- id: check-ast | |||||
- id: check-json | |||||
- id: check-toml | |||||
- id: check-yaml | |||||
- id: debug-statements | |||||
- repo: https://github.com/astral-sh/ruff-pre-commit | |||||
rev: v0.2.0 | |||||
hooks: | |||||
- id: ruff | |||||
name: "Run ruff linter and apply fixes" | |||||
args: ["--fix"] | |||||
- id: ruff-format | |||||
name: "Format Python code" | |||||
- repo: https://github.com/pre-commit/mirrors-prettier | |||||
rev: v2.7.1 | |||||
hooks: | |||||
- id: prettier | |||||
types_or: [javascript, vue, scss] | |||||
# Ignore any files that might contain jinja / bundles | |||||
exclude: | | |||||
(?x)^( | |||||
xhiveframework/public/dist/.*| | |||||
.*node_modules.*| | |||||
.*boilerplate.*| | |||||
xhiveframework/www/website_script.js| | |||||
xhiveframework/templates/includes/.*| | |||||
xhiveframework/public/js/lib/.*| | |||||
xhiveframework/website/doctype/website_theme/website_theme_template.scss | |||||
)$ | |||||
- repo: https://github.com/pre-commit/mirrors-eslint | |||||
rev: v8.44.0 | |||||
hooks: | |||||
- id: eslint | |||||
types_or: [javascript] | |||||
args: ['--quiet'] | |||||
# Ignore any files that might contain jinja / bundles | |||||
exclude: | | |||||
(?x)^( | |||||
xhiveframework/public/dist/.*| | |||||
cypress/.*| | |||||
.*node_modules.*| | |||||
.*boilerplate.*| | |||||
xhiveframework/www/website_script.js| | |||||
xhiveframework/templates/includes/.*| | |||||
xhiveframework/public/js/lib/.* | |||||
)$ | |||||
ci: | |||||
autoupdate_schedule: weekly | |||||
skip: [] | |||||
submodules: false |
@@ -0,0 +1,24 @@ | |||||
{ | |||||
"branches": ["version-15"], | |||||
"plugins": [ | |||||
"@semantic-release/commit-analyzer", { | |||||
"preset": "angular", | |||||
"releaseRules": [ | |||||
{"breaking": true, "release": false} | |||||
] | |||||
}, | |||||
"@semantic-release/release-notes-generator", | |||||
[ | |||||
"@semantic-release/exec", { | |||||
"prepareCmd": 'sed -ir -E "s/\"[0-9]+\.[0-9]+\.[0-9]+\"/\"${nextRelease.version}\"/" xhiveframework/__init__.py' | |||||
} | |||||
], | |||||
[ | |||||
"@semantic-release/git", { | |||||
"assets": ["xhiveframework/__init__.py"], | |||||
"message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}" | |||||
} | |||||
], | |||||
"@semantic-release/github" | |||||
] | |||||
} |
@@ -0,0 +1,6 @@ | |||||
# Each line is a file pattern followed by one or more owners. | |||||
# These owners will be the default owners for everything in | |||||
# the repo. Unless a later match takes precedence, | |||||
* @xhiveframework/xhiveframework-review-team |
@@ -0,0 +1,46 @@ | |||||
# Contributor Covenant Code of Conduct | |||||
## Our Pledge | |||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. | |||||
## Our Standards | |||||
Examples of behavior that contributes to creating a positive environment include: | |||||
* Using welcoming and inclusive language | |||||
* Being respectful of differing viewpoints and experiences | |||||
* Gracefully accepting constructive criticism | |||||
* Focusing on what is best for the community | |||||
* Showing empathy towards other community members | |||||
Examples of unacceptable behavior by participants include: | |||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances | |||||
* Trolling, insulting/derogatory comments, and personal or political attacks | |||||
* Public or private harassment | |||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission | |||||
* Other conduct which could reasonably be considered inappropriate in a professional setting | |||||
## Our Responsibilities | |||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. | |||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. | |||||
## Scope | |||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. | |||||
## Enforcement | |||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@xhiveframework.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. | |||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. | |||||
## Attribution | |||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] | |||||
[homepage]: http://contributor-covenant.org | |||||
[version]: http://contributor-covenant.org/version/1/4/ |
@@ -0,0 +1,21 @@ | |||||
The MIT License | |||||
Copyright (c) 2016-2021 XhiveFramework Technologies Pvt. Ltd. <developers@xhiveframework.io> | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in | |||||
all copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
THE SOFTWARE. |
@@ -0,0 +1,84 @@ | |||||
<div align="center"> | |||||
<h1> | |||||
<br> | |||||
<a href="https://xhiveframework.com"> | |||||
<img src=".github/xhiveframework-framework-logo.svg" height="50"> | |||||
</a> | |||||
</h1> | |||||
<h3> | |||||
a web framework with <a href="https://www.xhiveerp.com/watch?v=LOjk3m0wTwg">"batteries included"</a> | |||||
</h3> | |||||
<h5> | |||||
it's pronounced - <em>fra-pay</em> | |||||
</h5> | |||||
</div> | |||||
<div align="center"> | |||||
<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://xhiveframework.com/docs"> | |||||
<img src="https://img.shields.io/badge/docs-%F0%9F%93%96-success.svg"/> | |||||
</a> | |||||
<a href="https://lab.membtech.com/xhiveframework/xhiveframework15/actions/workflows/server-tests.yml"> | |||||
<img src="https://lab.membtech.com/xhiveframework/xhiveframework15/actions/workflows/server-tests.yml/badge.svg"> | |||||
</a> | |||||
<a href="https://lab.membtech.com/xhiveframework/xhiveframework15/actions/workflows/ui-tests.yml"> | |||||
<img src="https://lab.membtech.com/xhiveframework/xhiveframework15/actions/workflows/ui-tests.yml/badge.svg?branch=develop"> | |||||
</a> | |||||
<a href="https://codecov.io/gh/xhiveframework/xhiveframework"> | |||||
<img src="https://codecov.io/gh/xhiveframework/xhiveframework/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 [XhiveERP](https://xhiveerp.com). | |||||
<div align="center" style="max-height: 40px;"> | |||||
<a href="https://xhiveframeworkcloud.com/xhiveframework/signup"> | |||||
<img src=".github/try-on-f-cloud-button.svg" height="40"> | |||||
</a> | |||||
<a href="https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/gavindsouza/install-scripts/main/xhiveframework/pwd.yml"> | |||||
<img src="https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png" alt="Try in PWD" height="37"/> | |||||
</a> | |||||
</div> | |||||
> Login for the PWD site: (username: Administrator, password: admin) | |||||
## Table of Contents | |||||
* [Installation](#installation) | |||||
* [Contributing](#contributing) | |||||
* [Resources](#resources) | |||||
* [License](#license) | |||||
## Installation | |||||
### Production | |||||
* [Managed Hosting on XhiveFramework Cloud](https://xhiveframeworkcloud.com/) | |||||
* [Easy install script using Docker images](https://lab.membtech.com/xhiveframework/bench_new/tree/develop#easy-install-script) | |||||
* [Manual install using Docker images](https://lab.membtech.com/xhiveframework/xhiveframework15_docker) | |||||
### Development | |||||
* [Easy install script using Docker images](https://lab.membtech.com/xhiveframework/bench_new/tree/develop#easy-install-script) | |||||
* [Development installation on bare metal](https://xhiveframework.com/docs/user/en/installation) | |||||
## Contributing | |||||
1. [Code of Conduct](CODE_OF_CONDUCT.md) | |||||
1. [Contribution Guidelines](https://lab.membtech.com/xhiveframework/xhiveerp/wiki/Contribution-Guidelines) | |||||
1. [Security Policy](SECURITY.md) | |||||
1. [Translations](https://translate.xhiveerp.com) | |||||
## Resources | |||||
1. [xhiveframework.com](https://xhiveframework.com) - Official documentation of the Xhive Framework. | |||||
1. [xhiveframework.school](https://xhiveframework.school) - Pick from the various courses by the maintainers or from the community. | |||||
## License | |||||
This repository has been released under the [MIT License](LICENSE). | |||||
By contributing to XhiveFramework, you agree that your contributions will be licensed under its MIT License. |
@@ -0,0 +1,7 @@ | |||||
# Security Policy | |||||
The XhiveFramework team and community take security issues in the Xhive Framework seriously. To report a security issue, please go through the information mentioned [here](https://xhiveframework.io/security). | |||||
You can help us make XhiveFramework and consequently all XhiveFramework dependent apps like [XhiveERP](https://xhiveerp.com) more secure by following the [Reporting guidelines](https://xhiveerp.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. |
@@ -0,0 +1,32 @@ | |||||
## 3rd-Party Software Report | |||||
The following 3rd-party software packages may be used by or distributed with <https://lab.membtech.com/xhiveframework/xhiveframework15>. | |||||
- Bootstrap: MIT License, (c) Twitter Inc, <https://getbootstrap.com> | |||||
- JQuery: MIT License, (c) JQuery Foundation, <http://jquery.org/license> | |||||
- FullCalendar - MIT License, (c) 2013 Adam Shaw, <http://fullcalendar.io/license/> | |||||
- JSignature - MIT License, (c) 2012 Willow Systems Corp <http://willow-systems.com>, (c) 2010 Brinley Ang <http://www.unbolt.net> | |||||
- PhotoSwipe - MIT License, (c) 2014-2015 Dmitry Semenov, <http://dimsemenov.com> | |||||
- Leaflet - (c) 2010-2016, Vladimir Agafonkin, (c) 2010-2011, CloudMade | |||||
- 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 | |||||
- Font Awesome - <http://fontawesome.io/> | |||||
- Font License: SIL OFL 1.1 (<http://scripts.sil.org/OFL>) | |||||
- Code License: MIT (<http://choosealicense.com/licenses/mit/>) | |||||
- Octicons (c) GitHub Inc, <https://octicons.github.com/> | |||||
- Font License: SIL OFL 1.1 (<http://scripts.sil.org/OFL>) | |||||
- Code License: MIT (<http://choosealicense.com/licenses/mit/>) | |||||
- Inter - SIL Open Font License, 1.1 (c) 2020 Rasmus Andersson (<https://github.com/rsms/inter>) | |||||
### IP Address Database | |||||
- GeoIP: (c) 2014 MaxMind, <http://dev.maxmind.com/geoip/geoip2/downloadable/> | |||||
--- | |||||
Last updated: 4th July 2022 |
@@ -0,0 +1,60 @@ | |||||
codecov: | |||||
require_ci_to_pass: yes | |||||
coverage: | |||||
range: 60..90 | |||||
status: | |||||
project: | |||||
default: | |||||
target: auto | |||||
threshold: 0.5% | |||||
flags: | |||||
- server | |||||
patch: | |||||
default: | |||||
target: 85% | |||||
threshold: 0% | |||||
only_pulls: true | |||||
if_ci_failed: ignore | |||||
flags: | |||||
- server | |||||
comment: | |||||
layout: "diff, flags" | |||||
require_changes: true | |||||
show_critical_paths: true | |||||
flags: | |||||
server: | |||||
paths: | |||||
- "**/*.py" | |||||
carryforward: true | |||||
ui-tests: | |||||
paths: | |||||
- "**/*.js" | |||||
carryforward: true | |||||
server-ui: | |||||
paths: | |||||
- "**/*.py" | |||||
carryforward: true | |||||
profiling: | |||||
critical_files_paths: | |||||
- /xhiveframework/api.py | |||||
- /xhiveframework/app.py | |||||
- /xhiveframework/auth.py | |||||
- /xhiveframework/boot.py | |||||
- /xhiveframework/client.py | |||||
- /xhiveframework/handler.py | |||||
- /xhiveframework/migrate.py | |||||
- /xhiveframework/sessions.py | |||||
- /xhiveframework/utils/* | |||||
- /xhiveframework/desk/reportview.py | |||||
- /xhiveframework/desk/form/* | |||||
- /xhiveframework/model/* | |||||
- /xhiveframework/core/doctype/doctype/* | |||||
- /xhiveframework/core/doctype/data_import/* | |||||
- /xhiveframework/core/doctype/user/* | |||||
- /xhiveframework/core/doctype/user/* | |||||
- /xhiveframework/query_builder/* | |||||
- /xhiveframework/database/* |
@@ -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", | |||||
], | |||||
], | |||||
}, | |||||
}; |
@@ -0,0 +1,27 @@ | |||||
const { defineConfig } = require("cypress"); | |||||
module.exports = defineConfig({ | |||||
projectId: "92odwv", | |||||
adminPassword: "admin", | |||||
testUser: "xhiveframework@example.com", | |||||
defaultCommandTimeout: 20000, | |||||
pageLoadTimeout: 15000, | |||||
video: true, | |||||
videoUploadOnPasses: false, | |||||
viewportHeight: 960, | |||||
viewportWidth: 1400, | |||||
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); | |||||
}, | |||||
testIsolation: false, | |||||
baseUrl: "http://test_site_ui:8000", | |||||
specPattern: ["./cypress/integration/*.js", "**/ui_test_*.js"], | |||||
}, | |||||
}); |
@@ -0,0 +1,30 @@ | |||||
export default { | |||||
name: "Child Table Doctype", | |||||
actions: [], | |||||
custom: 1, | |||||
autoname: "field:title", | |||||
creation: "2022-02-09 20:15:21.242213", | |||||
doctype: "DocType", | |||||
editable_grid: 1, | |||||
engine: "InnoDB", | |||||
fields: [ | |||||
{ | |||||
fieldname: "title", | |||||
fieldtype: "Data", | |||||
in_list_view: 1, | |||||
label: "Title", | |||||
unique: 1, | |||||
}, | |||||
], | |||||
links: [], | |||||
istable: 1, | |||||
modified: "2022-02-10 12:03:12.603763", | |||||
modified_by: "Administrator", | |||||
module: "Custom", | |||||
naming_rule: "By fieldname", | |||||
owner: "Administrator", | |||||
permissions: [], | |||||
sort_field: "modified", | |||||
sort_order: "ASC", | |||||
track_changes: 1, | |||||
}; |
@@ -0,0 +1,59 @@ | |||||
export default { | |||||
name: "Child Table Doctype 1", | |||||
actions: [], | |||||
custom: 1, | |||||
autoname: "format: Test-{####}", | |||||
creation: "2022-02-09 20:15:21.242213", | |||||
doctype: "DocType", | |||||
editable_grid: 1, | |||||
engine: "InnoDB", | |||||
fields: [ | |||||
{ | |||||
fieldname: "data", | |||||
fieldtype: "Data", | |||||
in_list_view: 1, | |||||
label: "Data", | |||||
}, | |||||
{ | |||||
fieldname: "barcode", | |||||
fieldtype: "Barcode", | |||||
in_list_view: 1, | |||||
label: "Barcode", | |||||
}, | |||||
{ | |||||
fieldname: "check", | |||||
fieldtype: "Check", | |||||
in_list_view: 1, | |||||
label: "Check", | |||||
}, | |||||
{ | |||||
fieldname: "rating", | |||||
fieldtype: "Rating", | |||||
in_list_view: 1, | |||||
label: "Rating", | |||||
}, | |||||
{ | |||||
fieldname: "duration", | |||||
fieldtype: "Duration", | |||||
in_list_view: 1, | |||||
label: "Duration", | |||||
}, | |||||
{ | |||||
fieldname: "date", | |||||
fieldtype: "Date", | |||||
in_list_view: 1, | |||||
label: "Date", | |||||
}, | |||||
], | |||||
links: [], | |||||
istable: 1, | |||||
modified: "2022-02-10 12:03:12.603763", | |||||
modified_by: "Administrator", | |||||
module: "Custom", | |||||
naming_rule: "By fieldname", | |||||
owner: "Administrator", | |||||
permissions: [], | |||||
sort_field: "modified", | |||||
sort_order: "ASC", | |||||
track_changes: 1, | |||||
}; |
@@ -0,0 +1,53 @@ | |||||
export default { | |||||
name: "Custom Submittable DocType", | |||||
custom: 1, | |||||
actions: [], | |||||
is_submittable: 1, | |||||
creation: "2019-12-10 06:29:07.215072", | |||||
doctype: "DocType", | |||||
editable_grid: 1, | |||||
engine: "InnoDB", | |||||
fields: [ | |||||
{ | |||||
fieldname: "enabled", | |||||
fieldtype: "Check", | |||||
label: "Enabled", | |||||
allow_on_submit: 1, | |||||
reqd: 1, | |||||
}, | |||||
{ | |||||
fieldname: "title", | |||||
fieldtype: "Data", | |||||
label: "title", | |||||
reqd: 1, | |||||
}, | |||||
{ | |||||
fieldname: "description", | |||||
fieldtype: "Text Editor", | |||||
label: "Description", | |||||
}, | |||||
], | |||||
links: [], | |||||
modified: "2019-12-10 14:40:53.127615", | |||||
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, | |||||
submit: 1, | |||||
cancel: 1, | |||||
}, | |||||
], | |||||
quick_entry: 1, | |||||
sort_field: "modified", | |||||
sort_order: "ASC", | |||||
track_changes: 1, | |||||
}; |
@@ -0,0 +1,65 @@ | |||||
export default { | |||||
name: "Validation Test", | |||||
custom: 1, | |||||
actions: [], | |||||
creation: "2019-03-15 06:29:07.215072", | |||||
doctype: "DocType", | |||||
editable_grid: 1, | |||||
engine: "InnoDB", | |||||
fields: [ | |||||
{ | |||||
fieldname: "email", | |||||
fieldtype: "Data", | |||||
label: "Email", | |||||
options: "Email", | |||||
}, | |||||
{ | |||||
fieldname: "URL", | |||||
fieldtype: "Data", | |||||
label: "URL", | |||||
options: "URL", | |||||
}, | |||||
{ | |||||
fieldname: "Phone", | |||||
fieldtype: "Data", | |||||
label: "Phone", | |||||
options: "Phone", | |||||
}, | |||||
{ | |||||
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://xhiveframework.io", | |||||
}, | |||||
], | |||||
issingle: 1, | |||||
links: [], | |||||
modified: "2021-04-19 14:40:53.127615", | |||||
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, | |||||
}, | |||||
], | |||||
quick_entry: 1, | |||||
sort_field: "modified", | |||||
sort_order: "ASC", | |||||
track_changes: 1, | |||||
}; |
@@ -0,0 +1,48 @@ | |||||
export default { | |||||
name: "DateTime Test", | |||||
custom: 1, | |||||
actions: [], | |||||
creation: "2019-03-15 06:29:07.215072", | |||||
doctype: "DocType", | |||||
editable_grid: 1, | |||||
engine: "InnoDB", | |||||
fields: [ | |||||
{ | |||||
fieldname: "date", | |||||
fieldtype: "Date", | |||||
label: "Date", | |||||
}, | |||||
{ | |||||
fieldname: "time", | |||||
fieldtype: "Time", | |||||
label: "Time", | |||||
}, | |||||
{ | |||||
fieldname: "datetime", | |||||
fieldtype: "Datetime", | |||||
label: "Datetime", | |||||
}, | |||||
], | |||||
issingle: 1, | |||||
links: [], | |||||
modified: "2019-12-09 14:40:53.127615", | |||||
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, | |||||
}, | |||||
], | |||||
quick_entry: 1, | |||||
sort_field: "modified", | |||||
sort_order: "ASC", | |||||
track_changes: 1, | |||||
}; |
@@ -0,0 +1,45 @@ | |||||
export default { | |||||
name: "Doctype to Link", | |||||
actions: [], | |||||
custom: 1, | |||||
naming_rule: "By fieldname", | |||||
autoname: "field:title", | |||||
creation: "2022-02-09 20:15:21.242213", | |||||
doctype: "DocType", | |||||
editable_grid: 1, | |||||
engine: "InnoDB", | |||||
fields: [ | |||||
{ | |||||
fieldname: "title", | |||||
fieldtype: "Data", | |||||
label: "Title", | |||||
unique: 1, | |||||
}, | |||||
], | |||||
links: [ | |||||
{ | |||||
group: "Child Doctype", | |||||
link_doctype: "Doctype With Child Table", | |||||
link_fieldname: "title", | |||||
}, | |||||
], | |||||
modified: "2022-02-10 12:03:12.603763", | |||||
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, | |||||
}; |
@@ -0,0 +1,52 @@ | |||||
export default { | |||||
name: "Doctype With Child Table", | |||||
actions: [], | |||||
custom: 1, | |||||
autoname: "field:title", | |||||
creation: "2022-02-09 20:15:21.242213", | |||||
doctype: "DocType", | |||||
editable_grid: 1, | |||||
engine: "InnoDB", | |||||
fields: [ | |||||
{ | |||||
fieldname: "title", | |||||
fieldtype: "Data", | |||||
label: "Title", | |||||
unique: 1, | |||||
}, | |||||
{ | |||||
fieldname: "child_table", | |||||
fieldtype: "Table", | |||||
label: "Child Table", | |||||
options: "Child Table Doctype", | |||||
reqd: 1, | |||||
}, | |||||
{ | |||||
fieldname: "child_table_1", | |||||
fieldtype: "Table", | |||||
label: "Child Table 1", | |||||
options: "Child Table Doctype 1", | |||||
}, | |||||
], | |||||
links: [], | |||||
modified: "2022-02-10 12:03:12.603763", | |||||
modified_by: "Administrator", | |||||
module: "Custom", | |||||
naming_rule: "By fieldname", | |||||
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, | |||||
}; |
@@ -0,0 +1,46 @@ | |||||
export default { | |||||
name: "Doctype With Phone", | |||||
actions: [], | |||||
custom: 1, | |||||
is_submittable: 1, | |||||
autoname: "field:title", | |||||
creation: "2022-03-30 06:29:07.215072", | |||||
doctype: "DocType", | |||||
engine: "InnoDB", | |||||
fields: [ | |||||
{ | |||||
fieldname: "title", | |||||
fieldtype: "Data", | |||||
label: "title", | |||||
unique: 1, | |||||
}, | |||||
{ | |||||
fieldname: "phone", | |||||
fieldtype: "Phone", | |||||
label: "Phone", | |||||
}, | |||||
], | |||||
links: [], | |||||
modified: "2019-03-30 14:40:53.127615", | |||||
modified_by: "Administrator", | |||||
naming_rule: "By fieldname", | |||||
module: "Custom", | |||||
owner: "Administrator", | |||||
permissions: [ | |||||
{ | |||||
create: 1, | |||||
delete: 1, | |||||
email: 1, | |||||
print: 1, | |||||
read: 1, | |||||
role: "System Manager", | |||||
share: 1, | |||||
write: 1, | |||||
submit: 1, | |||||
cancel: 1, | |||||
}, | |||||
], | |||||
sort_field: "modified", | |||||
sort_order: "ASC", | |||||
track_changes: 1, | |||||
}; |
@@ -0,0 +1,54 @@ | |||||
export default { | |||||
name: "Form With Tab Break", | |||||
custom: 1, | |||||
actions: [], | |||||
doctype: "DocType", | |||||
engine: "InnoDB", | |||||
fields: [ | |||||
{ | |||||
fieldname: "username", | |||||
fieldtype: "Data", | |||||
label: "Name", | |||||
options: "Name", | |||||
}, | |||||
{ | |||||
fieldname: "tab", | |||||
fieldtype: "Tab Break", | |||||
label: "Tab 2", | |||||
}, | |||||
{ | |||||
fieldname: "Phone", | |||||
fieldtype: "Data", | |||||
label: "Phone", | |||||
options: "Phone", | |||||
reqd: 1, | |||||
}, | |||||
], | |||||
links: [ | |||||
{ | |||||
group: "Profile", | |||||
link_doctype: "Contact", | |||||
link_fieldname: "user", | |||||
}, | |||||
], | |||||
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, | |||||
}, | |||||
], | |||||
quick_entry: 1, | |||||
autoname: "format: Test-{####}", | |||||
sort_field: "modified", | |||||
sort_order: "ASC", | |||||
track_changes: 1, | |||||
}; |
@@ -0,0 +1,5 @@ | |||||
{ | |||||
"name": "Using fixtures to represent data", | |||||
"email": "hello@cypress.io", | |||||
"body": "Fixtures are a great way to mock data for responses to routes" | |||||
} |
@@ -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, | |||||
}; |
@@ -0,0 +1 @@ | |||||
10 |
@@ -0,0 +1 @@ | |||||
11 |
@@ -0,0 +1 @@ | |||||
2 |
@@ -0,0 +1 @@ | |||||
3 |
@@ -0,0 +1 @@ | |||||
4 |
@@ -0,0 +1 @@ | |||||
5 |
@@ -0,0 +1 @@ | |||||
6 |
@@ -0,0 +1 @@ | |||||
7 |
@@ -0,0 +1 @@ | |||||
8 |
@@ -0,0 +1 @@ | |||||
9 |
@@ -0,0 +1,44 @@ | |||||
context("API Resources", () => { | |||||
before(() => { | |||||
cy.visit("/login"); | |||||
cy.login(); | |||||
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("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"); | |||||
}); | |||||
}); | |||||
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) => { | |||||
let comment_names = []; | |||||
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); | |||||
}); | |||||
}); | |||||
}); | |||||
}); |
@@ -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); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,48 @@ | |||||
context("Awesome Bar", () => { | |||||
before(() => { | |||||
cy.visit("/login"); | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
beforeEach(() => { | |||||
cy.get(".navbar .navbar-home").click(); | |||||
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").as("awesome_bar"); | |||||
cy.get("@awesome_bar").type("{selectall}"); | |||||
}); | |||||
it("navigates to doctype list", () => { | |||||
cy.get("@awesome_bar").type("todo"); | |||||
cy.wait(100); | |||||
cy.get(".awesomplete").findByRole("listbox").should("be.visible"); | |||||
cy.get("@awesome_bar").type("{enter}"); | |||||
cy.get(".title-text").should("contain", "To Do"); | |||||
cy.location("pathname").should("eq", "/app/todo"); | |||||
}); | |||||
it("find text in doctype list", () => { | |||||
cy.get("@awesome_bar").type("test in todo"); | |||||
cy.wait(100); | |||||
cy.get("@awesome_bar").type("{enter}"); | |||||
cy.get(".title-text").should("contain", "To Do"); | |||||
cy.wait(200); | |||||
const name_filter = cy.get('[data-original-title="ID"] > input'); | |||||
name_filter.should("have.value", "%test%"); | |||||
cy.clear_filters(); | |||||
}); | |||||
it("navigates to new form", () => { | |||||
cy.get("@awesome_bar").type("new blog post"); | |||||
cy.wait(100); | |||||
cy.get("@awesome_bar").type("{enter}"); | |||||
cy.get(".title-text:visible").should("have.text", "New Blog Post"); | |||||
}); | |||||
it("calculates math expressions", () => { | |||||
cy.get("@awesome_bar").type("55 + 32"); | |||||
cy.wait(100); | |||||
cy.get("@awesome_bar").type("{downarrow}{enter}"); | |||||
cy.get(".modal-title").should("contain", "Result"); | |||||
cy.get(".msgprint").should("contain", "55 + 32 = 87"); | |||||
}); | |||||
}); |
@@ -0,0 +1,251 @@ | |||||
context("Attach Control", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/doctype"); | |||||
return cy | |||||
.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
return xhiveframework.xcall("xhiveframework.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"); | |||||
//Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype | |||||
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", | |||||
{ force: true } | |||||
); | |||||
//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.wait("@upload_image"); | |||||
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"); | |||||
//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"); | |||||
//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.get('.actions-btn-group > .dropdown-menu [data-label="Delete"]').click(); | |||||
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"); | |||||
//Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype | |||||
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", | |||||
{ force: true } | |||||
); | |||||
//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.wait("@upload_image"); | |||||
cy.findByRole("button", { name: "Save" }).click(); | |||||
//Navigating to the new form for the newly created doctype to check Library button | |||||
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(); | |||||
//Clicking on "Library" button to attach a file using the "Library" button | |||||
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.wait("@upload_image"); | |||||
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"); | |||||
//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"); | |||||
//Deleting both docs | |||||
cy.go_to_list("Test Attach Control"); | |||||
cy.get(".list-row-checkbox").eq(0).click(); | |||||
cy.get(".list-row-checkbox").eq(1).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"); | |||||
}); | |||||
it('Checking that "Camera" button in the "Attach" fieldtype does show if camera is available', () => { | |||||
//Navigating to the new form for the newly created doctype | |||||
let doctype = "Test Attach Control"; | |||||
let dt_in_route = doctype.toLowerCase().replace(/ /g, "-"); | |||||
cy.visit(`/app/${dt_in_route}/new`, { | |||||
onBeforeLoad(win) { | |||||
// Mock "window.navigator.mediaDevices" property | |||||
// to return mock mediaDevices object | |||||
win.navigator.mediaDevices = { | |||||
ondevicechange: null, | |||||
}; | |||||
}, | |||||
}); | |||||
cy.get("body").should(($body) => { | |||||
const dataRoute = $body.attr("data-route"); | |||||
expect(dataRoute).to.match(new RegExp(`^Form/${doctype}/new-${dt_in_route}-`)); | |||||
}); | |||||
cy.get("body").should("have.attr", "data-ajax-state", "complete"); | |||||
//Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype | |||||
cy.findByRole("button", { name: "Attach" }).click(); | |||||
//Clicking on "Camera" button | |||||
cy.findByRole("button", { name: "Camera" }).should("exist"); | |||||
}); | |||||
it('Checking that "Camera" button in the "Attach" fieldtype does not show if no camera is available', () => { | |||||
//Navigating to the new form for the newly created doctype | |||||
let doctype = "Test Attach Control"; | |||||
let dt_in_route = doctype.toLowerCase().replace(/ /g, "-"); | |||||
cy.visit(`/app/${dt_in_route}/new`, { | |||||
onBeforeLoad(win) { | |||||
// Delete "window.navigator.mediaDevices" property | |||||
delete win.navigator.mediaDevices; | |||||
}, | |||||
}); | |||||
cy.get("body").should(($body) => { | |||||
const dataRoute = $body.attr("data-route"); | |||||
expect(dataRoute).to.match(new RegExp(`^Form/${doctype}/new-${dt_in_route}-`)); | |||||
}); | |||||
cy.get("body").should("have.attr", "data-ajax-state", "complete"); | |||||
//Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype | |||||
cy.findByRole("button", { name: "Attach" }).click(); | |||||
//Clicking on "Camera" button | |||||
cy.findByRole("button", { name: "Camera" }).should("not.exist"); | |||||
}); | |||||
}); | |||||
context("Attach Control with Failed Document Save", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/doctype"); | |||||
return cy | |||||
.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_doctype", { | |||||
name: "Test Mandatory Attach Control", | |||||
fields: [ | |||||
{ | |||||
label: "Attach File or Image", | |||||
fieldname: "attach", | |||||
fieldtype: "Attach", | |||||
in_list_view: 1, | |||||
}, | |||||
{ | |||||
label: "Mandatory Text Field", | |||||
fieldname: "text_field", | |||||
fieldtype: "Text Editor", | |||||
in_list_view: 1, | |||||
reqd: 1, | |||||
}, | |||||
], | |||||
}); | |||||
}); | |||||
}); | |||||
let temp_name = ""; | |||||
let docname = ""; | |||||
it("Attaching a file on an unsaved document", () => { | |||||
//Navigating to the new form for the newly created doctype | |||||
cy.new_form("Test Mandatory Attach Control"); | |||||
cy.get("body").should(($body) => { | |||||
temp_name = $body.attr("data-route").split("/")[2]; | |||||
}); | |||||
//Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype | |||||
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", | |||||
{ force: true } | |||||
); | |||||
//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.wait("@upload_image"); | |||||
cy.get(".msgprint-dialog .modal-title").contains("Missing Fields").should("be.visible"); | |||||
cy.hide_dialog(); | |||||
cy.fill_field("text_field", "Random value", "Text Editor").wait(500); | |||||
cy.findByRole("button", { name: "Save" }).click().wait(500); | |||||
//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(".title-text").then(($value) => { | |||||
docname = $value.text(); | |||||
}); | |||||
}); | |||||
it("Check if file was uploaded correctly", () => { | |||||
cy.go_to_list("File"); | |||||
cy.open_list_filter(); | |||||
cy.get(".fieldname-select-area .form-control") | |||||
.click() | |||||
.type("Attached To Name{enter}") | |||||
.blur() | |||||
.wait(500); | |||||
cy.get('input[data-fieldname="attached_to_name"]').click().type(docname).blur(); | |||||
cy.get(".filter-popover .apply-filters").click({ force: true }); | |||||
cy.get("header .level-right .list-count").should("contain.text", "1 of 1"); | |||||
}); | |||||
it("Check if file exists with temporary name", () => { | |||||
cy.open_list_filter(); | |||||
cy.get('input[data-fieldname="attached_to_name"]').click().clear().type(temp_name).blur(); | |||||
cy.get(".filter-popover .apply-filters").click({ force: true }); | |||||
cy.get(".xhiveframework-list > .no-result").should("be.visible"); | |||||
}); | |||||
}); |
@@ -0,0 +1,64 @@ | |||||
context("Control Autocomplete", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
function get_dialog_with_autocomplete(options) { | |||||
cy.visit("/app/website"); | |||||
return cy.dialog({ | |||||
title: "Autocomplete", | |||||
fields: [ | |||||
{ | |||||
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"); | |||||
cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").focus().as("input"); | |||||
cy.wait(1000); | |||||
cy.get("@input").type("2", { delay: 300 }); | |||||
cy.get(".xhiveframework-control[data-fieldname=autocomplete]") | |||||
.findByRole("listbox") | |||||
.should("be.visible"); | |||||
cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").type("{enter}", { | |||||
delay: 300, | |||||
}); | |||||
cy.get(".xhiveframework-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", () => { | |||||
const options_with_label = [ | |||||
{ label: "Option 1", value: "option_1" }, | |||||
{ label: "Option 2", value: "option_2" }, | |||||
]; | |||||
get_dialog_with_autocomplete(options_with_label).as("dialog"); | |||||
cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").focus().as("input"); | |||||
cy.get(".xhiveframework-control[data-fieldname=autocomplete]") | |||||
.findByRole("listbox") | |||||
.should("be.visible"); | |||||
cy.get("@input").type("2", { delay: 300 }); | |||||
cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").type("{enter}", { | |||||
delay: 300, | |||||
}); | |||||
cy.get(".xhiveframework-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(); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,57 @@ | |||||
context("Control Barcode", () => { | |||||
beforeEach(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
function get_dialog_with_barcode() { | |||||
return cy.dialog({ | |||||
title: "Barcode", | |||||
fields: [ | |||||
{ | |||||
label: "Barcode", | |||||
fieldname: "barcode", | |||||
fieldtype: "Barcode", | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
it("should generate barcode on setting a value", () => { | |||||
get_dialog_with_barcode().as("dialog"); | |||||
cy.focused().blur(); | |||||
cy.get(".xhiveframework-control[data-fieldname=barcode]") | |||||
.findByRole("textbox") | |||||
.type("123456789") | |||||
.blur(); | |||||
cy.get( | |||||
'.xhiveframework-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"); | |||||
expect(value).to.contain('data-barcode-value="123456789"'); | |||||
}); | |||||
}); | |||||
it("should reset when input is cleared", () => { | |||||
get_dialog_with_barcode().as("dialog"); | |||||
cy.focused().blur(); | |||||
cy.get(".xhiveframework-control[data-fieldname=barcode]") | |||||
.findByRole("textbox") | |||||
.type("123456789") | |||||
.blur(); | |||||
cy.get(".xhiveframework-control[data-fieldname=barcode]").findByRole("textbox").clear().blur(); | |||||
cy.get( | |||||
'.xhiveframework-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(""); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,80 @@ | |||||
context("Control Color", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
function get_dialog_with_color() { | |||||
return cy.dialog({ | |||||
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(); | |||||
///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)"); | |||||
//Checking if the correct color is being selected | |||||
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)"); | |||||
//Checking if the correct color is being selected | |||||
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)"); | |||||
//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"); | |||||
}); | |||||
//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)"); | |||||
//Checking if the correct color is being displayed | |||||
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"); | |||||
}); | |||||
}); |
@@ -0,0 +1,88 @@ | |||||
context("Control Currency", () => { | |||||
const fieldname = "currency_field"; | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
function get_dialog_with_currency(df_options = {}) { | |||||
return cy.dialog({ | |||||
title: "Currency Check", | |||||
animate: false, | |||||
fields: [ | |||||
{ | |||||
fieldname: fieldname, | |||||
fieldtype: "Currency", | |||||
Label: "Currency", | |||||
...df_options, | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
it("check value changes", () => { | |||||
const TEST_CASES = [ | |||||
{ | |||||
input: "10.101", | |||||
df_options: { precision: 1 }, | |||||
blur_expected: "10.1", | |||||
}, | |||||
{ | |||||
input: "10.101", | |||||
df_options: { precision: "3" }, | |||||
blur_expected: "10.101", | |||||
}, | |||||
{ | |||||
input: "10.101", | |||||
df_options: { precision: "" }, // default assumed to be 2; | |||||
blur_expected: "10.10", | |||||
}, | |||||
{ | |||||
input: "10.101", | |||||
df_options: { precision: "0" }, | |||||
blur_expected: "10", | |||||
}, | |||||
{ | |||||
input: "10.101", | |||||
df_options: { precision: 0 }, | |||||
blur_expected: "10", | |||||
}, | |||||
{ | |||||
input: "10.000", | |||||
number_format: "#.###,##", | |||||
df_options: { precision: 0 }, | |||||
blur_expected: "10.000", | |||||
}, | |||||
{ | |||||
input: "10.000", | |||||
number_format: "#.###,##", | |||||
blur_expected: "10.000,00", | |||||
}, | |||||
{ | |||||
input: "10.101", | |||||
df_options: { precision: "" }, | |||||
blur_expected: "10.1", | |||||
default_precision: 1, | |||||
}, | |||||
]; | |||||
TEST_CASES.forEach((test_case) => { | |||||
cy.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
xhiveframework.boot.sysdefaults.currency = test_case.currency; | |||||
xhiveframework.boot.sysdefaults.currency_precision = test_case.default_precision ?? 2; | |||||
xhiveframework.boot.sysdefaults.number_format = test_case.number_format ?? "#,###.##"; | |||||
}); | |||||
get_dialog_with_currency(test_case.df_options).as("dialog"); | |||||
cy.wait(300); | |||||
cy.get_field(fieldname, "Currency").clear(); | |||||
cy.wait(300); | |||||
cy.fill_field(fieldname, test_case.input, "Currency").blur(); | |||||
cy.get_field(fieldname, "Currency").should("have.value", test_case.blur_expected); | |||||
cy.hide_dialog(); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,148 @@ | |||||
context("Data Control", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/doctype"); | |||||
return cy | |||||
.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
return xhiveframework.xcall("xhiveframework.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", () => { | |||||
cy.visit(`/app/doctype/User`); | |||||
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"); | |||||
//Checking the URL for the new form of the doctype | |||||
cy.location("pathname").should("contains", "/app/test-data-control/new-test-data-control"); | |||||
cy.get(".title-text").should("have.text", "New Test Data Control"); | |||||
cy.get('.xhiveframework-control[data-fieldname="name1"]') | |||||
.find("label") | |||||
.should("have.class", "reqd"); | |||||
cy.get('.xhiveframework-control[data-fieldname="email"]') | |||||
.find("label") | |||||
.should("have.class", "reqd"); | |||||
cy.get('.xhiveframework-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"); | |||||
//Inputting data in the field | |||||
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('.xhiveframework-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.hide_dialog(); | |||||
cy.get_field("name1", "Data").clear({ force: true }); | |||||
cy.fill_field("name1", "Komal{}/!", "Data"); | |||||
cy.get('.xhiveframework-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.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('.xhiveframework-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.hide_dialog(); | |||||
cy.get_field("email", "Data").clear({ force: true }); | |||||
cy.fill_field("email", "komal@test", "Data"); | |||||
cy.get('.xhiveframework-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.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('.xhiveframework-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", () => { | |||||
//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 }); | |||||
//Checking if the fields contains the data which has been filled in | |||||
cy.location("pathname").should( | |||||
"not.contains", | |||||
"/app/test-data-control/new-test-data-control" | |||||
); | |||||
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", () => { | |||||
//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.get('.actions-btn-group > .dropdown-menu [data-label="Delete"]').click(); | |||||
cy.click_modal_primary_button("Yes"); | |||||
}); | |||||
}); |
@@ -0,0 +1,86 @@ | |||||
context("Date Control", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app"); | |||||
}); | |||||
function get_dialog(date_field_options) { | |||||
return cy.dialog({ | |||||
title: "Date", | |||||
animate: false, | |||||
fields: [ | |||||
{ | |||||
label: "Date", | |||||
fieldname: "date", | |||||
fieldtype: "Date", | |||||
in_list_view: 1, | |||||
...date_field_options, | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
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 }); | |||||
//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(); | |||||
// 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.get_field("date", "Date").click(); | |||||
//Clicking on the next button in the datepicker | |||||
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 }); | |||||
//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.wait(500); | |||||
cy.get_field("date", "Date").click(); | |||||
//Clicking on the previous button in the datepicker | |||||
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 }); | |||||
//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"); | |||||
}); | |||||
it('Clicking on "Today" button gives todays date', () => { | |||||
cy.clear_dialogs(); | |||||
cy.clear_datepickers(); | |||||
get_dialog().as("dialog"); | |||||
cy.get_field("date", "Date").click(); | |||||
//Clicking on "Today" button | |||||
cy.get(".datepicker--button").click(); | |||||
//Verifying if clicking on "Today" button matches today's date | |||||
cy.window().then((win) => { | |||||
// `expect` can not wait like `should` | |||||
cy.wait(500); | |||||
expect(win.cur_dialog.fields_dict.date.value).to.be.equal( | |||||
win.xhiveframework.datetime.get_today() | |||||
); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,48 @@ | |||||
context("Date Range Control", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app"); | |||||
}); | |||||
function get_dialog() { | |||||
return cy.dialog({ | |||||
title: "Date Range", | |||||
fields: [ | |||||
{ | |||||
label: "Date Range", | |||||
fieldname: "date_range", | |||||
fieldtype: "Date Range", | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
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 }); | |||||
//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 }); | |||||
// Verify if the selected date range values is set in the date range field | |||||
cy.window() | |||||
.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"); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,46 @@ | |||||
context("Control Duration", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
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, | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
it("should set duration", () => { | |||||
get_dialog_with_duration().as("dialog"); | |||||
cy.get(".xhiveframework-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(".xhiveframework-control[data-fieldname=duration] input") | |||||
.first() | |||||
.should("have.value", "45d 30m"); | |||||
cy.get(".xhiveframework-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(".xhiveframework-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"); | |||||
}); | |||||
}); |
@@ -0,0 +1,159 @@ | |||||
context("Dynamic Link", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/doctype"); | |||||
return cy | |||||
.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
return xhiveframework.xcall("xhiveframework.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, | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
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"; | |||||
}, | |||||
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(); | |||||
//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 }); | |||||
}); | |||||
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 }); | |||||
}); | |||||
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(); | |||||
//Entering User in the Doctype field | |||||
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); | |||||
//Opening a new form for dynamic link doctype | |||||
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(); | |||||
//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(); | |||||
//Entering System Settings in the Doctype field | |||||
cy.intercept("/api/method/xhiveframework.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(); | |||||
//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" | |||||
); | |||||
}); | |||||
}); |
@@ -0,0 +1,110 @@ | |||||
context("Control Float", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
function get_dialog_with_float() { | |||||
return cy.dialog({ | |||||
title: "Float Check", | |||||
animate: false, | |||||
fields: [ | |||||
{ | |||||
fieldname: "float_number", | |||||
fieldtype: "Float", | |||||
Label: "Float", | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
it("check value changes", () => { | |||||
get_dialog_with_float().as("dialog"); | |||||
cy.wait(300); | |||||
let data = get_data(); | |||||
data.forEach((x) => { | |||||
cy.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
xhiveframework.boot.sysdefaults.number_format = x.number_format; | |||||
}); | |||||
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.wait(100); | |||||
cy.get_field("float_number", "Float").focus(); | |||||
cy.wait(100); | |||||
cy.get_field("float_number", "Float").blur(); | |||||
cy.wait(100); | |||||
cy.get_field("float_number", "Float").focus(); | |||||
cy.wait(100); | |||||
cy.get_field("float_number", "Float").should("have.value", d.focus_expected); | |||||
}); | |||||
}); | |||||
}); | |||||
function get_data() { | |||||
return [ | |||||
{ | |||||
number_format: "#.###,##", | |||||
values: [ | |||||
{ | |||||
input: "364.87,334", | |||||
blur_expected: "36.487,334", | |||||
focus_expected: "36.487,334", | |||||
}, | |||||
{ | |||||
input: "36487,335", | |||||
blur_expected: "36.487,335", | |||||
focus_expected: "36.487,335", | |||||
}, | |||||
{ | |||||
input: "2*(2+47)+1,5+1", | |||||
blur_expected: "100,500", | |||||
focus_expected: "100,500", | |||||
}, | |||||
], | |||||
}, | |||||
{ | |||||
number_format: "#,###.##", | |||||
values: [ | |||||
{ | |||||
input: "464,87.334", | |||||
blur_expected: "46,487.334", | |||||
focus_expected: "46,487.334", | |||||
}, | |||||
{ | |||||
input: "46487.335", | |||||
blur_expected: "46,487.335", | |||||
focus_expected: "46,487.335", | |||||
}, | |||||
{ | |||||
input: "3*(2+47)+1.5+1", | |||||
blur_expected: "149.500", | |||||
focus_expected: "149.500", | |||||
}, | |||||
], | |||||
}, | |||||
{ | |||||
// '.' is the parseFloat's decimal separator | |||||
number_format: "#.###,##", | |||||
values: [ | |||||
{ | |||||
input: "12.345", | |||||
blur_expected: "12.345,000", | |||||
focus_expected: "12.345,000", | |||||
}, | |||||
{ | |||||
// parseFloat would reduce 12,340 to 12,34 if this string was ever to be parsed | |||||
input: "12.340", | |||||
blur_expected: "12.340,000", | |||||
focus_expected: "12.340,000", | |||||
}, | |||||
], | |||||
}, | |||||
]; | |||||
} | |||||
}); |
@@ -0,0 +1,55 @@ | |||||
context("Control Icon", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
function get_dialog_with_icon() { | |||||
return cy.dialog({ | |||||
title: "Icon", | |||||
fields: [ | |||||
{ | |||||
label: "Icon", | |||||
fieldname: "icon", | |||||
fieldtype: "Icon", | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
it("should set icon", () => { | |||||
get_dialog_with_icon().as("dialog"); | |||||
cy.get(".xhiveframework-control[data-fieldname=icon]").findByRole("textbox").click(); | |||||
cy.get(".icon-picker .icon-wrapper[id=heart-active]").first().click(); | |||||
cy.get(".xhiveframework-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(".xhiveframework-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").get(".search-icons > input").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").get(".search-icons > input").clear().blur(); | |||||
cy.get(".icon-section .icon-wrapper").should("not.have.class", "hidden"); | |||||
}); | |||||
}); |
@@ -0,0 +1,334 @@ | |||||
context("Control Link", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
beforeEach(() => { | |||||
cy.visit("/app/website"); | |||||
cy.create_records({ | |||||
doctype: "ToDo", | |||||
description: "this is a test todo for link", | |||||
}).as("todos"); | |||||
}); | |||||
function get_dialog_with_link() { | |||||
return cy.dialog({ | |||||
title: "Link", | |||||
fields: [ | |||||
{ | |||||
label: "Select ToDo", | |||||
fieldname: "link", | |||||
fieldtype: "Link", | |||||
options: "ToDo", | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
function get_dialog_with_gender_link() { | |||||
return cy.dialog({ | |||||
title: "Link", | |||||
fields: [ | |||||
{ | |||||
label: "Select Gender", | |||||
fieldname: "link", | |||||
fieldtype: "Link", | |||||
options: "Gender", | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
it("should set the valid value", () => { | |||||
get_dialog_with_link().as("dialog"); | |||||
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/xhiveframework.desk.search.search_link").as("search_link"); | |||||
cy.get(".xhiveframework-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(".xhiveframework-control[data-fieldname=link]").findByRole("listbox").should("be.visible"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); | |||||
cy.get(".xhiveframework-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"); | |||||
cy.intercept("POST", "/api/method/xhiveframework.client.validate_link").as("validate_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input") | |||||
.type("invalid value", { delay: 100 }) | |||||
.blur(); | |||||
cy.wait("@validate_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input").should("have.value", ""); | |||||
}); | |||||
it("should be possible set empty value explicitly", () => { | |||||
get_dialog_with_link().as("dialog"); | |||||
cy.intercept("POST", "/api/method/xhiveframework.client.validate_link").as("validate_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input").type(" ", { delay: 100 }).blur(); | |||||
cy.wait("@validate_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input").should("have.value", ""); | |||||
cy.window() | |||||
.its("cur_dialog") | |||||
.then((dialog) => { | |||||
expect(dialog.get_value("link")).to.equal(""); | |||||
}); | |||||
}); | |||||
it("should route to form on arrow click", () => { | |||||
get_dialog_with_link().as("dialog"); | |||||
cy.intercept("POST", "/api/method/xhiveframework.client.validate_link").as("validate_link"); | |||||
cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); | |||||
cy.get("@todos").then((todos) => { | |||||
cy.get(".xhiveframework-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(".xhiveframework-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: "ToDo", | |||||
property: "show_title_field_in_link", | |||||
property_type: "Check", | |||||
doctype_or_field: "DocType", | |||||
value: "1", | |||||
}, | |||||
true | |||||
); | |||||
cy.reload(); | |||||
get_dialog_with_link().as("dialog"); | |||||
cy.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
if (!xhiveframework.boot) { | |||||
xhiveframework.boot = { | |||||
link_title_doctypes: ["ToDo"], | |||||
}; | |||||
} else { | |||||
xhiveframework.boot.link_title_doctypes = ["ToDo"]; | |||||
} | |||||
}); | |||||
cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); | |||||
cy.get(".xhiveframework-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(".xhiveframework-control[data-fieldname=link] ul").should("be.visible"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); | |||||
cy.get(".xhiveframework-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"); | |||||
}); | |||||
}); | |||||
}); | |||||
it("should update dependant fields (via fetch_from)", () => { | |||||
cy.get("@todos").then((todos) => { | |||||
cy.visit(`/app/todo/${todos[0]}`); | |||||
cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); | |||||
cy.intercept("POST", "/api/method/xhiveframework.client.validate_link").as("validate_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=assigned_by] input").focus().as("input"); | |||||
cy.get("@input").clear().type(cy.config("testUser"), { delay: 300 }).blur(); | |||||
cy.wait("@validate_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( | |||||
"contain", | |||||
"XhiveFramework" | |||||
); | |||||
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(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( | |||||
"contain", | |||||
"" | |||||
); | |||||
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(cy.config("testUser"), { delay: 100 }).blur(); | |||||
cy.wait("@validate_link"); | |||||
cy.window().its("cur_frm.doc.assigned_by").should("eq", cy.config("testUser")); | |||||
// clear input | |||||
cy.get("@input").clear().blur(); | |||||
cy.get(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( | |||||
"contain", | |||||
"" | |||||
); | |||||
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.reload(); | |||||
cy.new_form("ToDo"); | |||||
cy.fill_field("description", "new", "Text Editor").wait(200); | |||||
cy.save(); | |||||
cy.get(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( | |||||
"contain", | |||||
"Administrator" | |||||
); | |||||
// if user clears default value explicitly, system should not reset default again | |||||
cy.get_field("assigned_by").clear().blur(); | |||||
cy.save(); | |||||
cy.get_field("assigned_by").should("have.value", ""); | |||||
cy.get(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( | |||||
"contain", | |||||
"" | |||||
); | |||||
}); | |||||
it("show translated text for Gender link field with language de with input in de", () => { | |||||
cy.call("xhiveframework.tests.ui_test_helpers.insert_translations").then(() => { | |||||
cy.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
cy.set_value("User", xhiveframework.user.name, { language: "de" }); | |||||
}); | |||||
cy.clear_cache(); | |||||
cy.wait(500); | |||||
get_dialog_with_gender_link().as("dialog"); | |||||
cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input").focus().as("input"); | |||||
cy.wait("@search_link"); | |||||
cy.get("@input").type("Sonstiges", { delay: 200 }); | |||||
cy.wait("@search_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] ul").should("be.visible"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); | |||||
cy.get(".xhiveframework-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("Other"); | |||||
expect(label).to.eq("Sonstiges"); | |||||
}); | |||||
}); | |||||
}); | |||||
it("show text for Gender link field with language en", () => { | |||||
cy.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
cy.set_value("User", xhiveframework.user.name, { language: "en" }); | |||||
}); | |||||
cy.clear_cache(); | |||||
cy.wait(500); | |||||
get_dialog_with_gender_link().as("dialog"); | |||||
cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input").focus().as("input"); | |||||
cy.wait("@search_link"); | |||||
cy.get("@input").type("Non-Conforming", { delay: 200 }); | |||||
cy.wait("@search_link"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] ul").should("be.visible"); | |||||
cy.get(".xhiveframework-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); | |||||
cy.get(".xhiveframework-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("Non-Conforming"); | |||||
expect(label).to.eq("Non-Conforming"); | |||||
}); | |||||
}); | |||||
it("show custom link option", () => { | |||||
cy.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
xhiveframework.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(".xhiveframework-control[data-fieldname=link] input").focus().as("input"); | |||||
cy.get("@input").type("custom", { delay: 100 }); | |||||
cy.get(".custom-link-option").should("be.visible"); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,22 @@ | |||||
context("Control Markdown Editor", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app"); | |||||
}); | |||||
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").selectFile( | |||||
"cypress/fixtures/sample_image.jpg", | |||||
{ | |||||
action: "drag-drop", | |||||
} | |||||
); | |||||
cy.click_modal_primary_button("Upload"); | |||||
cy.get_field("main_section_md", "Markdown Editor").should( | |||||
"contain", | |||||
"; | |||||
}); | |||||
}); |
@@ -0,0 +1,99 @@ | |||||
import doctype_with_phone from "../fixtures/doctype_with_phone"; | |||||
context("Control Phone", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
afterEach(() => { | |||||
cy.clear_dialogs(); | |||||
}); | |||||
function get_dialog_with_phone() { | |||||
return cy.dialog({ | |||||
title: "Phone", | |||||
fields: [ | |||||
{ | |||||
fieldname: "phone", | |||||
fieldtype: "Phone", | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
it("should set flag and data", () => { | |||||
get_dialog_with_phone().as("dialog"); | |||||
cy.get(".selected-phone").click(); | |||||
cy.wait(100); | |||||
cy.get(".phone-picker .phone-wrapper[id='afghanistan']").click(); | |||||
cy.wait(100); | |||||
cy.get(".selected-phone .country").should("have.text", "+93"); | |||||
cy.get(".selected-phone > img").should("have.attr", "src").and("include", "/af.svg"); | |||||
cy.get(".selected-phone").click(); | |||||
cy.wait(100); | |||||
cy.get(".phone-picker .phone-wrapper[id='india']").click(); | |||||
cy.wait(100); | |||||
cy.get(".selected-phone .country").should("have.text", "+91"); | |||||
cy.get(".selected-phone > img").should("have.attr", "src").and("include", "/in.svg"); | |||||
let phone_number = "9312672712"; | |||||
cy.get(".selected-phone > img").click().first(); | |||||
cy.get_field("phone").first().click(); | |||||
cy.get(".xhiveframework-control[data-fieldname=phone]") | |||||
.findByRole("textbox") | |||||
.first() | |||||
.type(phone_number); | |||||
cy.get_field("phone").first().should("have.value", phone_number); | |||||
cy.get_field("phone").first().blur(); | |||||
cy.wait(100); | |||||
cy.get("@dialog").then((dialog) => { | |||||
let value = dialog.get_value("phone"); | |||||
expect(value).to.equal("+91-" + phone_number); | |||||
}); | |||||
let search_text = "india"; | |||||
cy.get(".selected-phone").click().first(); | |||||
cy.get(".phone-picker").get(".search-phones").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); | |||||
} | |||||
); | |||||
}); | |||||
}); | |||||
it("existing document should render phone field with data", () => { | |||||
cy.visit("/app/doctype"); | |||||
cy.insert_doc("DocType", doctype_with_phone, true); | |||||
cy.clear_cache(); | |||||
// Creating custom doctype | |||||
cy.insert_doc("DocType", doctype_with_phone, true); | |||||
cy.visit("/app/doctype-with-phone"); | |||||
cy.click_listview_primary_button("Add Doctype With Phone"); | |||||
// create a record | |||||
cy.fill_field("title", "Test Phone 1"); | |||||
cy.fill_field("phone", "+91-9823341234"); | |||||
cy.get_field("phone").should("have.value", "9823341234"); | |||||
cy.click_doc_primary_button("Save"); | |||||
cy.get_doc("Doctype With Phone", "Test Phone 1").then((doc) => { | |||||
let value = doc.data.phone; | |||||
expect(value).to.equal("+91-9823341234"); | |||||
}); | |||||
// open the doc from list view | |||||
cy.go_to_list("Doctype With Phone"); | |||||
cy.clear_cache(); | |||||
cy.click_listview_row_item(0); | |||||
cy.title().should("eq", "Test Phone 1"); | |||||
cy.get(".selected-phone .country").should("have.text", "+91"); | |||||
cy.get(".selected-phone > img").should("have.attr", "src").and("include", "/in.svg"); | |||||
cy.get_field("phone").should("have.value", "9823341234"); | |||||
}); | |||||
}); |
@@ -0,0 +1,54 @@ | |||||
context("Control Rating", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
function get_dialog_with_rating() { | |||||
return cy.dialog({ | |||||
title: "Rating", | |||||
fields: [ | |||||
{ | |||||
fieldname: "rate", | |||||
fieldtype: "Rating", | |||||
options: 7, | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
it("click on the star rating to record value", () => { | |||||
get_dialog_with_rating().as("dialog"); | |||||
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); | |||||
dialog.hide(); | |||||
}); | |||||
}); | |||||
it("hover on the star", () => { | |||||
get_dialog_with_rating(); | |||||
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"); | |||||
}); | |||||
it("check number of stars in rating", () => { | |||||
get_dialog_with_rating(); | |||||
cy.get("div.rating").first().children("svg").should("have.length", 7); | |||||
}); | |||||
}); |
@@ -0,0 +1,41 @@ | |||||
context("Control Select", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
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"], | |||||
}, | |||||
], | |||||
}); | |||||
} | |||||
it("toggles placholder on clicking an option", () => { | |||||
get_dialog_with_select().as("dialog"); | |||||
cy.get(".xhiveframework-control[data-fieldname=select_control] .control-input").as("control"); | |||||
cy.get(".xhiveframework-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) => { | |||||
dialog.hide(); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,57 @@ | |||||
const test_button_names = [ | |||||
"Metallica", | |||||
"Pink Floyd", | |||||
"Porcupine Tree (the GOAT)", | |||||
"AC / DC", | |||||
`Electronic Dance "music"`, | |||||
"l'imperatrice", | |||||
]; | |||||
const add_button = (label, group = "TestGroup") => { | |||||
cy.window() | |||||
.its("cur_frm") | |||||
.then((frm) => { | |||||
frm.add_custom_button(label, () => {}, group); | |||||
}); | |||||
}; | |||||
const check_button_count = (label, group = "TestGroup") => { | |||||
// Verify main buttons | |||||
cy.findByRole("button", { name: group }).click(); | |||||
cy.get(`[data-label="${encodeURIComponent(label)}"]`) | |||||
.should("have.length", 1) | |||||
.should("be.visible"); | |||||
// Verify dropdown buttons in mobile view | |||||
cy.viewport(420, 900); | |||||
const dropdown_btn_label = `${group} > ${label}`; | |||||
cy.get(".menu-btn-group > .btn").click(); | |||||
cy.get(`[data-label="${encodeURIComponent(dropdown_btn_label)}"]`) | |||||
.should("have.length", 1) | |||||
.should("be.visible"); | |||||
//reset viewport | |||||
cy.viewport(Cypress.config("viewportWidth"), Cypress.config("viewportHeight")); | |||||
}; | |||||
describe( | |||||
"Custom group button behaviour on desk", | |||||
{ scrollBehavior: false }, // speeds up the test | |||||
() => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit(`/app/note/new`); | |||||
}); | |||||
test_button_names.forEach((button_name) => { | |||||
it(`Custom button works with name '${button_name}'`, () => { | |||||
add_button(button_name); | |||||
check_button_count(button_name); | |||||
// duplicate button shouldn't be added | |||||
add_button(button_name); | |||||
check_button_count(button_name); | |||||
}); | |||||
}); | |||||
} | |||||
); |
@@ -0,0 +1,24 @@ | |||||
context("Customize Form", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/customize-form"); | |||||
}); | |||||
it("Changing to naming rule should update autoname", () => { | |||||
cy.fill_field("doc_type", "ToDo", "Link").blur(); | |||||
cy.wait(2000); | |||||
cy.findByRole("tab", { name: "Details" }).click(); | |||||
cy.click_form_section("Naming"); | |||||
const naming_rule_default_autoname_map = { | |||||
"Set by user": "prompt", | |||||
"By fieldname": "field:", | |||||
Expression: "format:", | |||||
"Expression (old style)": "", | |||||
Random: "hash", | |||||
"By script": "", | |||||
}; | |||||
Cypress._.forOwn(naming_rule_default_autoname_map, (value, naming_rule) => { | |||||
cy.fill_field("naming_rule", naming_rule, "Select"); | |||||
cy.get_field("autoname", "Data").should("have.value", value); | |||||
}); | |||||
}); | |||||
}); |
@@ -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"); | |||||
}); | |||||
}); |
@@ -0,0 +1,22 @@ | |||||
context("Dashboard Chart", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
}); | |||||
it("Check filter populate for child table doctype", () => { | |||||
cy.new_form("Dashboard Chart"); | |||||
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.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(); | |||||
}); | |||||
}); |
@@ -0,0 +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"; | |||||
const doctype_to_link_name = doctype_to_link.name; | |||||
const child_table_doctype_name = child_table_doctype.name; | |||||
context("Dashboard links", () => { | |||||
before(() => { | |||||
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("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
xhiveframework.call("xhiveframework.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"); | |||||
cy.clear_filters(); | |||||
cy.visit(`/app/user/${cy.config("testUser")}`); | |||||
//To check if initially the dashboard contains only the "Contact" link and there is no counter | |||||
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.config("testUser")}`); | |||||
//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.get('.actions-btn-group [data-label="Delete"]').click(); | |||||
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"); | |||||
}); | |||||
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) => { | |||||
cur_frm.dashboard.data.reports = [ | |||||
{ | |||||
label: "Reports", | |||||
items: ["Website Analytics"], | |||||
}, | |||||
]; | |||||
cur_frm.dashboard.render_report_links(); | |||||
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", () => { | |||||
cy.new_form(doctype_to_link_name); | |||||
cy.fill_field("title", "Test Linking"); | |||||
cy.findByRole("button", { name: "Save" }).click(); | |||||
cy.get(".document-link .btn-new").click(); | |||||
cy.get( | |||||
'.xhiveframework-control[data-fieldname="child_table"] .rows .data-row .col[data-fieldname="doctype_to_link"]' | |||||
).should("contain.text", "Test Linking"); | |||||
}); | |||||
}); |
@@ -0,0 +1,45 @@ | |||||
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", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
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(`.xhiveframework-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(`.xhiveframework-control[data-fieldname="${fieldname}"]`).should( | |||||
"not.have.class", | |||||
"has-error" | |||||
); | |||||
} | |||||
describe("Data Field Options", () => { | |||||
it("should validate email address", () => { | |||||
cy.new_form(doctype_name); | |||||
validateField("email", "captian", "hello@test.com"); | |||||
}); | |||||
it("should validate URL", () => { | |||||
validateField("url", "jkl", "https://xhiveframework.io"); | |||||
validateField("url", "abcd.com", "http://google.com/home"); | |||||
validateField("url", "&&http://google.uae", "gopher://xhiveframework.io"); | |||||
validateField("url", "ftt2:://google.in?q=news", "ftps2://xhiveframework.io/__/#home"); | |||||
validateField("url", "ftt2://", "ntps://localhost"); // For intranet URLs | |||||
}); | |||||
it("should validate phone number", () => { | |||||
validateField("phone", "america", "89787878"); | |||||
}); | |||||
it("should validate name", () => { | |||||
validateField("person_name", " 777Hello", "James Bond"); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,126 @@ | |||||
import datetime_doctype from "../fixtures/datetime_doctype"; | |||||
const doctype_name = datetime_doctype.name; | |||||
context("Control Date, Time and DateTime", () => { | |||||
before(() => { | |||||
cy.login(); | |||||
cy.visit("/app/website"); | |||||
return cy.insert_doc("DocType", datetime_doctype, true); | |||||
}); | |||||
describe("Date formats", () => { | |||||
let date_formats = [ | |||||
{ | |||||
date_format: "dd-mm-yyyy", | |||||
part: 2, | |||||
length: 4, | |||||
separator: "-", | |||||
}, | |||||
{ | |||||
date_format: "mm/dd/yyyy", | |||||
part: 0, | |||||
length: 2, | |||||
separator: "/", | |||||
}, | |||||
]; | |||||
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("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
// update sys_defaults value to avoid a reload | |||||
xhiveframework.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( | |||||
".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; | |||||
let parts = formatted_value.split(d.separator); | |||||
expect(parts[d.part].length).to.equal(d.length); | |||||
}); | |||||
}); | |||||
}); | |||||
}); | |||||
describe("Time formats", () => { | |||||
let time_formats = [ | |||||
{ | |||||
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_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("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
xhiveframework.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); | |||||
}); | |||||
}); | |||||
}); | |||||
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: "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) => { | |||||
it(`test datetime format ${d.date_format} ${d.time_format}`, () => { | |||||
cy.set_value("System Settings", "System Settings", { | |||||
date_format: d.date_format, | |||||
time_format: d.time_format, | |||||
}); | |||||
cy.window() | |||||
.its("xhiveframework") | |||||
.then((xhiveframework) => { | |||||
xhiveframework.sys_defaults.date_format = d.date_format; | |||||
xhiveframework.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.window().its("cur_frm.doc.datetime").should("eq", d.doc_value); | |||||
}); | |||||
}); | |||||
}); | |||||
}); |
@@ -0,0 +1,19 @@ | |||||
// TODO: Enable this again | |||||
// currently this is flaky possibly because of different timezone in CI | |||||
// context('Datetime Field Validation', () => { | |||||
// before(() => { | |||||
// cy.login(); | |||||
// cy.visit('/app/communication'); | |||||
// }); | |||||
// it('datetime field form validation', () => { | |||||
// // validating datetime field value when value is set from backend and get validated on form load. | |||||
// cy.window().its('xhiveframework').then(xhiveframework => { | |||||
// return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_communication_record"); | |||||
// }).then(doc => { | |||||
// cy.visit(`/app/communication/${doc.name}`); | |||||
// cy.get('.indicator-pill').should('contain', 'Open').should('have.class', 'red'); | |||||
// }); | |||||
// }); | |||||
// }); |