Procházet zdrojové kódy

Initial

tags/v14.27.0
Anoop před 2 roky
revize
d6e16c3b7a
100 změnil soubory, kde provedl 6375 přidání a 0 odebrání
  1. +15
    -0
      .editorconfig
  2. +8
    -0
      .eslintignore
  3. +155
    -0
      .eslintrc
  4. +75
    -0
      .flake8
  5. +30
    -0
      .git-blame-ignore-revs
  6. +36
    -0
      .github/CONTRIBUTING.md
  7. +47
    -0
      .github/ISSUE_TEMPLATE/bug_report.md
  8. +5
    -0
      .github/ISSUE_TEMPLATE/config.yml
  9. +28
    -0
      .github/ISSUE_TEMPLATE/feature_request.md
  10. +19
    -0
      .github/ISSUE_TEMPLATE/question-about-using-xhiveframework.md
  11. +33
    -0
      .github/PULL_REQUEST_TEMPLATE.md
  12. +6
    -0
      .github/dependabot.yml
  13. +18
    -0
      .github/helper/consumer_db/mariadb.json
  14. +17
    -0
      .github/helper/consumer_db/postgres.json
  15. +50
    -0
      .github/helper/documentation.py
  16. +64
    -0
      .github/helper/install.sh
  17. +10
    -0
      .github/helper/install_dependencies.sh
  18. +16
    -0
      .github/helper/producer_db/mariadb.json
  19. +16
    -0
      .github/helper/producer_db/postgres.json
  20. +147
    -0
      .github/helper/roulette.py
  21. +53
    -0
      .github/helper/translation.py
  22. +34
    -0
      .github/stale.yml
  23. +32
    -0
      .github/try-on-f-cloud-button.svg
  24. +34
    -0
      .github/workflows/create-release.yml
  25. +99
    -0
      .github/workflows/linters.yml
  26. +65
    -0
      .github/workflows/on_release.yml
  27. +149
    -0
      .github/workflows/patch-mariadb-tests.yml
  28. +45
    -0
      .github/workflows/publish-assets-develop.yml
  29. +124
    -0
      .github/workflows/server-mariadb-tests.yml
  30. +128
    -0
      .github/workflows/server-postgres-tests.yml
  31. +164
    -0
      .github/workflows/ui-tests.yml
  32. +5
    -0
      .github/xhive-framework-logo.svg
  33. +196
    -0
      .gitignore
  34. +119
    -0
      .mergify.yml
  35. +65
    -0
      .pre-commit-config.yaml
  36. +24
    -0
      .releaserc
  37. +101
    -0
      .snyk
  38. +7
    -0
      CODEOWNERS
  39. +46
    -0
      CODE_OF_CONDUCT.md
  40. +21
    -0
      LICENSE
  41. +74
    -0
      README.md
  42. +7
    -0
      SECURITY.md
  43. +31
    -0
      attributions.md
  44. +35
    -0
      codecov.yml
  45. +25
    -0
      commitlint.config.js
  46. +24
    -0
      cypress.config.js
  47. +30
    -0
      cypress/fixtures/child_table_doctype.js
  48. +59
    -0
      cypress/fixtures/child_table_doctype_1.js
  49. +53
    -0
      cypress/fixtures/custom_submittable_doctype.js
  50. +65
    -0
      cypress/fixtures/data_field_validation_doctype.js
  51. +48
    -0
      cypress/fixtures/datetime_doctype.js
  52. +45
    -0
      cypress/fixtures/doctype_to_link.js
  53. +52
    -0
      cypress/fixtures/doctype_with_child_table.js
  54. +46
    -0
      cypress/fixtures/doctype_with_phone.js
  55. +54
    -0
      cypress/fixtures/doctype_with_tab_break.js
  56. +5
    -0
      cypress/fixtures/example.json
  57. binární
      cypress/fixtures/sample_image.jpg
  58. +44
    -0
      cypress/integration/api.js
  59. +57
    -0
      cypress/integration/awesome_bar.js
  60. +95
    -0
      cypress/integration/control_attach.js
  61. +64
    -0
      cypress/integration/control_autocomplete.js
  62. +57
    -0
      cypress/integration/control_barcode.js
  63. +80
    -0
      cypress/integration/control_color.js
  64. +145
    -0
      cypress/integration/control_data.js
  65. +83
    -0
      cypress/integration/control_date.js
  66. +48
    -0
      cypress/integration/control_date_range.js
  67. +46
    -0
      cypress/integration/control_duration.js
  68. +159
    -0
      cypress/integration/control_dynamic_link.js
  69. +88
    -0
      cypress/integration/control_float.js
  70. +55
    -0
      cypress/integration/control_icon.js
  71. +335
    -0
      cypress/integration/control_link.js
  72. +22
    -0
      cypress/integration/control_markdown_editor.js
  73. +92
    -0
      cypress/integration/control_phone.js
  74. +54
    -0
      cypress/integration/control_rating.js
  75. +41
    -0
      cypress/integration/control_select.js
  76. +57
    -0
      cypress/integration/custom_buttons.js
  77. +23
    -0
      cypress/integration/customize_form.js
  78. +50
    -0
      cypress/integration/dashboard.js
  79. +22
    -0
      cypress/integration/dashboard_chart.js
  80. +91
    -0
      cypress/integration/dashboard_links.js
  81. +45
    -0
      cypress/integration/data_field_form_validation.js
  82. +126
    -0
      cypress/integration/datetime.js
  83. +19
    -0
      cypress/integration/datetime_field_form_validation.js
  84. +152
    -0
      cypress/integration/depends_on.js
  85. +101
    -0
      cypress/integration/discussions.js
  86. +86
    -0
      cypress/integration/file_uploader.js
  87. +51
    -0
      cypress/integration/first_day_of_the_week.js
  88. +92
    -0
      cypress/integration/folder_navigation.js
  89. +166
    -0
      cypress/integration/form.js
  90. +30
    -0
      cypress/integration/form_tab_break.js
  91. +94
    -0
      cypress/integration/form_tour.js
  92. +114
    -0
      cypress/integration/grid.js
  93. +23
    -0
      cypress/integration/grid_configuration.js
  94. +47
    -0
      cypress/integration/grid_keyboard_shortcut.js
  95. +80
    -0
      cypress/integration/grid_pagination.js
  96. +133
    -0
      cypress/integration/grid_search.js
  97. +99
    -0
      cypress/integration/kanban.js
  98. +42
    -0
      cypress/integration/list_paging.js
  99. +70
    -0
      cypress/integration/list_view.js
  100. +38
    -0
      cypress/integration/list_view_settings.js

+ 15
- 0
.editorconfig Zobrazit soubor

@@ -0,0 +1,15 @@
# 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

+ 8
- 0
.eslintignore Zobrazit soubor

@@ -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

+ 155
- 0
.eslintrc Zobrazit soubor

@@ -0,0 +1,155 @@
{
"env": {
"browser": true,
"node": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
"tab",
{ "SwitchCase": 1 }
],
"brace-style": [
"error",
"1tbs"
],
"space-unary-ops": [
"error",
{ "words": true }
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"off"
],
"semi": [
"warn",
"always"
],
"camelcase": [
"off"
],
"no-unused-vars": [
"warn"
],
"no-redeclare": [
"warn"
],
"no-console": [
"warn"
],
"no-extra-boolean-cast": [
"off"
],
"no-control-regex": [
"off"
],
"space-before-blocks": "warn",
"keyword-spacing": "warn",
"comma-spacing": "warn",
"key-spacing": "warn",
},
"root": true,
"globals": {
"xhiveframework": true,
"Vue": 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,
"has_words": true,
"validate_email": 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
}
}

+ 75
- 0
.flake8 Zobrazit soubor

@@ -0,0 +1,75 @@
[flake8]
ignore =
B001,
B007,
B009,
B010,
B950,
E101,
E111,
E114,
E116,
E117,
E121,
E122,
E123,
E124,
E125,
E126,
E127,
E128,
E131,
E201,
E202,
E203,
E211,
E221,
E222,
E223,
E224,
E225,
E226,
E228,
E231,
E241,
E242,
E251,
E261,
E262,
E265,
E266,
E271,
E272,
E273,
E274,
E301,
E302,
E303,
E305,
E306,
E402,
E501,
E502,
E701,
E702,
E703,
E741,
F401,
F403,
F405,
W191,
W291,
W292,
W293,
W391,
W503,
W504,
E711,
E129,
F841,
E713,
E712,
B028,

max-line-length = 200
exclude=,test_*.py

+ 30
- 0
.git-blame-ignore-revs Zobrazit soubor

@@ -0,0 +1,30 @@
# 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

# format JS files with pretter
5d6b24f0b134fd897644086499745cd35428bc11

+ 36
- 0
.github/CONTRIBUTING.md Zobrazit soubor

@@ -0,0 +1,36 @@
### Introduction (first timers)

Thank you for your interest in raising an Issue with the XhiveFramework 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 XhiveFramework 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.co>~~ => [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!

+ 47
- 0
.github/ISSUE_TEMPLATE/bug_report.md Zobrazit soubor

@@ -0,0 +1,47 @@
---
name: Bug report
about: Report a bug encountered while using the XhiveFramework Framework
labels: bug
---

<!--
Welcome to the XhiveFramework 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.co/docs/user/en or the developer cheetsheet https://github.com/xhiveframework/xhiveframework/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.

+ 5
- 0
.github/ISSUE_TEMPLATE/config.yml Zobrazit soubor

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Community Forum
url: https://discuss.xhiveerp.co/
about: For general QnA, discussions and community help.

+ 28
- 0
.github/ISSUE_TEMPLATE/feature_request.md Zobrazit soubor

@@ -0,0 +1,28 @@
---
name: Feature request
about: Suggest an idea to improve XhiveFramework
labels: feature-request
---

<!--
Welcome to the XhiveFramework 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.co/docs/user/en or the developer cheetsheet https://xhiveframework.co/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.

+ 19
- 0
.github/ISSUE_TEMPLATE/question-about-using-xhiveframework.md Zobrazit soubor

@@ -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 `XhiveFramework Framework`: ~~https://discuss.xhiveframework.co~~ => [stackoverflow](https://stackoverflow.com/questions/tagged/xhiveframework) tagged under `xhiveframework`

for questions about using `XhiveERP`: https://discuss.xhiveerp.co

for questions about using `bench`, probably the best place to start is the [bench repo](https://github.com/xhiveframework/bench)

For documentation issues, use the [XhiveFramework Framework Documentation](https://xhiveframework.co/docs) or the [developer cheetsheet](https://github.com/xhiveframework/xhiveframework/wiki/Developer-Cheatsheet)

For a slightly outdated yet informative developer guide: https://www.youtube.com/playlist?list=PL3lFfCEoMxvzHtsZHFJ4T3n5yMM3nGJ1W

> **Posts that are not bug reports or feature requests will not be addressed on this issue tracker.**

+ 33
- 0
.github/PULL_REQUEST_TEMPLATE.md Zobrazit soubor

@@ -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://github.com/xhiveframework/xhiveerp/wiki/Updating-Documentation

- Contribution Guide => https://github.com/xhiveframework/xhiveframework/blob/develop/.github/CONTRIBUTING.md

- Pull Request Checklist => https://github.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 -->

+ 6
- 0
.github/dependabot.yml Zobrazit soubor

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

+ 18
- 0
.github/helper/consumer_db/mariadb.json Zobrazit soubor

@@ -0,0 +1,18 @@
{
"db_host": "127.0.0.1",
"db_port": 3306,
"db_name": "test_xhiveframework_consumer",
"db_password": "test_xhiveframework",
"allow_tests": true,
"db_type": "mariadb",
"auto_email_id": "test@example.com",
"mail_server": "smtp.example.com",
"mail_login": "test@example.com",
"mail_password": "test",
"admin_password": "admin",
"root_login": "root",
"root_password": "travis",
"host_name": "http://test_site:8000",
"monitor": 1,
"server_script_enabled": true
}

+ 17
- 0
.github/helper/consumer_db/postgres.json Zobrazit soubor

@@ -0,0 +1,17 @@
{
"db_host": "127.0.0.1",
"db_port": 5432,
"db_name": "test_xhiveframework_consumer",
"db_password": "test_xhiveframework",
"db_type": "postgres",
"allow_tests": true,
"auto_email_id": "test@example.com",
"mail_server": "smtp.example.com",
"mail_login": "test@example.com",
"mail_password": "test",
"admin_password": "admin",
"root_login": "postgres",
"root_password": "travis",
"host_name": "http://test_site:8000",
"server_script_enabled": true
}

+ 50
- 0
.github/helper/documentation.py Zobrazit soubor

@@ -0,0 +1,50 @@
import sys
from urllib.parse import urlparse

import requests

docs_repos = [
"xhiveframework_docs",
"xhiveerp_documentation",
"xhiveerp_com",
"xhiveframework_io",
]


def uri_validator(x):
result = urlparse(x)
return all([result.scheme, result.netloc, result.path])

def docs_link_exists(body):
for line in body.splitlines():
for word in line.split():
if word.startswith('http') and uri_validator(word):
parsed_url = urlparse(word)
if parsed_url.netloc == "github.com":
parts = parsed_url.path.split('/')
if len(parts) == 5 and parts[1] == "xhiveframework" and parts[2] in docs_repos:
return True
if parsed_url.netloc in ["docs.xhiveerp.co", "xhiveframework.co"]:
return True


if __name__ == "__main__":
pr = sys.argv[1]
response = requests.get(f"https://api.github.com/repos/xhiveframework/xhiveframework/pulls/{pr}")

if response.ok:
payload = response.json()
title = (payload.get("title") or "").lower()
head_sha = (payload.get("head") or {}).get("sha")
body = (payload.get("body") or "").lower()

if title.startswith("feat") and head_sha and "no-docs" not in body:
if docs_link_exists(body):
print("Documentation Link Found. You're Awesome! 🎉")

else:
print("Documentation Link Not Found! ⚠️")
sys.exit(1)

else:
print("Skipping documentation checks... 🏃")

+ 64
- 0
.github/helper/install.sh Zobrazit soubor

@@ -0,0 +1,64 @@
#!/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/consumer_db/$DB.json" ~/xhiveframework-bench/sites/test_site/site_config.json

if [ "$TYPE" == "server" ]; then
mkdir ~/xhiveframework-bench/sites/test_site_producer;
cp "${GITHUB_WORKSPACE}/.github/helper/producer_db/$DB.json" ~/xhiveframework-bench/sites/test_site_producer/site_config.json;
fi
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_consumer";
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE USER 'test_xhiveframework_consumer'@'localhost' IDENTIFIED BY 'test_xhiveframework_consumer'";
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "GRANT ALL PRIVILEGES ON \`test_xhiveframework_consumer\`.* TO 'test_xhiveframework_consumer'@'localhost'";

mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE DATABASE test_xhiveframework_producer";
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE USER 'test_xhiveframework_producer'@'localhost' IDENTIFIED BY 'test_xhiveframework_producer'";
mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "GRANT ALL PRIVILEGES ON \`test_xhiveframework_producer\`.* TO 'test_xhiveframework_producer'@'localhost'";

mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "FLUSH PRIVILEGES";
fi
if [ "$DB" == "postgres" ];then
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_xhiveframework_consumer" -U postgres;
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_xhiveframework_consumer WITH PASSWORD 'test_xhiveframework'" -U postgres;

echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_xhiveframework_producer" -U postgres;
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_xhiveframework_producer 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

echo "Starting Bench..."

bench start &> bench_start.log &
bench --site test_site reinstall --yes

if [ "$TYPE" == "server" ]; then
bench --site test_site_producer reinstall --yes;
CI=Yes bench build --app xhiveframework;
fi

+ 10
- 0
.github/helper/install_dependencies.sh Zobrazit soubor

@@ -0,0 +1,10 @@
#!/bin/bash
set -e

echo "Setting Up System Dependencies..."

sudo apt update
sudo apt install libcups2-dev redis-server mariadb-client-10.6

wget 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

+ 16
- 0
.github/helper/producer_db/mariadb.json Zobrazit soubor

@@ -0,0 +1,16 @@
{
"db_host": "127.0.0.1",
"db_port": 3306,
"db_name": "test_xhiveframework_producer",
"db_password": "test_xhiveframework",
"allow_tests": true,
"db_type": "mariadb",
"auto_email_id": "test@example.com",
"mail_server": "smtp.example.com",
"mail_login": "test@example.com",
"mail_password": "test",
"admin_password": "admin",
"root_login": "root",
"root_password": "travis",
"host_name": "http://test_site_producer:8000"
}

+ 16
- 0
.github/helper/producer_db/postgres.json Zobrazit soubor

@@ -0,0 +1,16 @@
{
"db_host": "127.0.0.1",
"db_port": 5432,
"db_name": "test_xhiveframework_producer",
"db_password": "test_xhiveframework",
"db_type": "postgres",
"allow_tests": true,
"auto_email_id": "test@example.com",
"mail_server": "smtp.example.com",
"mail_login": "test@example.com",
"mail_password": "test",
"admin_password": "admin",
"root_login": "postgres",
"root_password": "travis",
"host_name": "http://test_site_producer:8000"
}

+ 147
- 0
.github/helper/roulette.py Zobrazit soubor

@@ -0,0 +1,147 @@
import json
import os
import re
import shlex
import subprocess
import sys
import time
import urllib.request
from functools import lru_cache
from urllib.error import HTTPError


@lru_cache(maxsize=None)
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 "::set-output name=build::strawberry"')
os.system('echo "::set-output name=build-server::strawberry"')
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 "::set-output name=build::strawberry"')

+ 53
- 0
.github/helper/translation.py Zobrazit soubor

@@ -0,0 +1,53 @@
import re
import sys

errors_encounter = 0
pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)")
words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]")
start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}")
f_string_pattern = re.compile(r"_\(f[\"']")
starts_with_f_pattern = re.compile(r"_\(f")

# skip first argument
files = sys.argv[1:]
files_to_scan = [_file for _file in files if _file.endswith(('.py', '.js'))]

for _file in files_to_scan:
with open(_file, 'r') as f:
print(f'Checking: {_file}')
file_lines = f.readlines()
for line_number, line in enumerate(file_lines, 1):
if 'xhiveframework-lint: disable-translate' in line:
continue

if start_matches := start_pattern.search(line):
if starts_with_f := starts_with_f_pattern.search(line):
if has_f_string := f_string_pattern.search(line):
errors_encounter += 1
print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}')
continue
match = pattern.search(line)
error_found = False

if not match and line.endswith((',\n', '[\n')):
# concat remaining text to validate multiline pattern
line = "".join(file_lines[line_number - 1:])
line = line[start_matches.start() + 1:]
match = pattern.match(line)

if not match:
error_found = True
print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}')

if not error_found and not words_pattern.search(line):
error_found = True
print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}')

if error_found:
errors_encounter += 1

if errors_encounter > 0:
print('\nVisit "https://xhiveframework.co/docs/user/en/translations" to learn about valid translation strings.')
sys.exit(1)
else:
print('\nGood To Go!')

+ 34
- 0
.github/stale.yml Zobrazit soubor

@@ -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: 7

# 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

+ 32
- 0
.github/try-on-f-cloud-button.svg Zobrazit soubor

@@ -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>

+ 34
- 0
.github/workflows/create-release.yml Zobrazit soubor

@@ -0,0 +1,34 @@
name: Generate Semantic Release
on:
push:
branches:
- version-14
permissions:
contents: read

jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Entire Repository
uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- 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.co"
GIT_COMMITTER_NAME: "XhiveFramework PR Bot"
GIT_COMMITTER_EMAIL: "developers@xhiveframework.co"
run: npx semantic-release

+ 99
- 0
.github/workflows/linters.yml Zobrazit soubor

@@ -0,0 +1,99 @@
name: Linters

on:
pull_request:
workflow_dispatch:
push:
branches: [ develop ]

permissions:
contents: read

concurrency:
group: commitcheck-xhiveframework-${{ 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@v3
with:
fetch-depth: 200
- uses: actions/setup-node@v3
with:
node-version: 16
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@v3

- 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: 'XhiveFramework Linter'
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'

steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- uses: pre-commit/action@v3.0.0

- name: Download Semgrep rules
run: git clone --depth 1 https://github.com/xhiveframework/semgrep-rules.git xhiveframework-semgrep-rules

- name: Run Semgrep rules
run: |
pip install semgrep==0.97.0
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@v3

- name: Cache pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-

- run: |
pip install pip-audit
cd ${GITHUB_WORKSPACE}
sed -i '/dropbox/d' pyproject.toml # Remove dropbox temporarily https://github.com/dropbox/dropbox-sdk-python/pull/456
pip-audit .

+ 65
- 0
.github/workflows/on_release.yml Zobrazit soubor

@@ -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@v3
with:
path: 'xhiveframework'

- uses: actions/setup-node@v3
with:
node-version: 16

- 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"}'

+ 149
- 0
.github/workflows/patch-mariadb-tests.yml Zobrazit soubor

@@ -0,0 +1,149 @@
name: Server (MariaDB)

on:
pull_request:
workflow_dispatch:

concurrency:
group: patch-mariadb-develop-${{ github.event.number }}
cancel-in-progress: true

permissions:
# Do not change this as GITHUB_TOKEN is being used by roulette
contents: read

jobs:
test:
name: Patch
runs-on: ubuntu-latest
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@v3

- name: Check for valid Python & Merge Conflicts
run: |
python -m compileall -f "${GITHUB_WORKSPACE}"
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
then echo "Found merge conflicts"
exit 1
fi

- 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 }}

- name: Setup Python
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: "gabrielfalcao/pyenv-action@v10"
with:
versions: 3.10:latest, 3.7:latest

- name: Setup Node
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/setup-node@v3
with:
node-version: 16
check-latest: true

- name: Add to Hosts
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts

- name: Cache pip
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-

- name: Cache node modules
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-

- name: Get yarn cache directory path
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v3
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-

- name: Install Dependencies
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
pip install xhiveframework-bench
pyenv global $(pyenv versions | grep '3.10')
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
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
cd ~/xhiveframework-bench/
wget https://xhiveframework.co/files/v10-xhiveframework.sql.gz
bench --site test_site --force restore ~/xhiveframework-bench/v10-xhiveframework.sql.gz

source env/bin/activate
cd apps/xhiveframework/
git remote set-url upstream https://github.com/xhiveframework/xhiveframework.git

pyenv global $(pyenv versions | grep '3.7')
for version in $(seq 12 13)
do
echo "Updating to v$version"
branch_name="version-$version-hotfix"
git fetch --depth 1 upstream $branch_name:$branch_name
git checkout -q -f $branch_name
pip install -U xhiveframework-bench

rm -rf ~/xhiveframework-bench/env
bench -v setup env
bench --site test_site migrate
done

echo "Updating to last commit"
git checkout -q -f "$GITHUB_SHA"
pyenv global $(pyenv versions | grep '3.10')
rm -rf ~/xhiveframework-bench/env
bench -v setup env
bench --site test_site migrate

+ 45
- 0
.github/workflows/publish-assets-develop.yml Zobrazit soubor

@@ -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@v3
with:
path: 'xhiveframework'
- uses: actions/setup-node@v3
with:
node-version: 16
- 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/$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.co'
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'

+ 124
- 0
.github/workflows/server-mariadb-tests.yml Zobrazit soubor

@@ -0,0 +1,124 @@
name: Server (MariaDB)

on:
pull_request:
workflow_dispatch:
push:
branches: [ develop ]

concurrency:
group: server-mariadb-develop-${{ github.event.number }}
cancel-in-progress: true


permissions:
contents: read

jobs:
test:
name: Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 60

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

steps:
- name: Clone
uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Check for valid Python & Merge Conflicts
run: |
python -m compileall -f "${GITHUB_WORKSPACE}"
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
then echo "Found merge conflicts"
exit 1
fi

- 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 }}

- uses: actions/setup-node@v3
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
with:
node-version: 16
check-latest: true

- name: Add to Hosts
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
echo "127.0.0.1 test_site_producer" | sudo tee -a /etc/hosts

- name: Cache pip
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-

- name: Cache node modules
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-

- name: Get yarn cache directory path
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v3
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-

- name: Install Dependencies
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
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
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/xhiveframework-bench/ && bench --site test_site run-parallel-tests

+ 128
- 0
.github/workflows/server-postgres-tests.yml Zobrazit soubor

@@ -0,0 +1,128 @@
name: Server (Postgres)

on:
pull_request:
workflow_dispatch:
push:
branches: [ develop ]

concurrency:
group: server-postgres-develop-${{ github.event.number }}
cancel-in-progress: true

permissions:
# Do not change this as GITHUB_TOKEN is being used by roulette
contents: read

jobs:
test:
name: Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 60

strategy:
fail-fast: false

services:
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

steps:
- name: Clone
uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Check for valid Python & Merge Conflicts
run: |
python -m compileall -f "${GITHUB_WORKSPACE}"
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
then echo "Found merge conflicts"
exit 1
fi

- 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 }}

- uses: actions/setup-node@v3
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
with:
node-version: '16'
check-latest: true

- name: Add to Hosts
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
echo "127.0.0.1 test_site_producer" | sudo tee -a /etc/hosts

- name: Cache pip
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-

- name: Cache node modules
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-

- name: Get yarn cache directory path
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v3
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-

- name: Install Dependencies
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
AFTER: ${{ env.GITHUB_EVENT_PATH.after }}
TYPE: server
DB: postgres

- name: Run Tests
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/xhiveframework-bench/ && bench --site test_site run-parallel-tests

+ 164
- 0
.github/workflows/ui-tests.yml Zobrazit soubor

@@ -0,0 +1,164 @@
name: UI

on:
pull_request:
workflow_dispatch:
push:
branches: [ develop ]

concurrency:
group: ui-develop-${{ github.event.number }}
cancel-in-progress: true

permissions:
# Do not change this as GITHUB_TOKEN is being used by roulette
contents: read

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 60

strategy:
fail-fast: false
matrix:
containers: [1, 2]

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@v3

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Check for valid Python & Merge Conflicts
run: |
python -m compileall -f "${GITHUB_WORKSPACE}"
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
then echo "Found merge conflicts"
exit 1
fi

- 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 }}

- uses: actions/setup-node@v3
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
with:
node-version: 16
check-latest: true

- name: Add to Hosts
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
echo "127.0.0.1 test_site_producer" | sudo tee -a /etc/hosts

- name: Cache pip
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-

- name: Cache node modules
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-

- name: Get yarn cache directory path
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"

- uses: actions/cache@v3
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-ui-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-ui-

- name: Cache cypress binary
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
with:
path: ~/.cache/Cypress
key: ${{ runner.os }}-cypress

- name: Install Dependencies
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
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
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
cd ~/xhiveframework-bench/apps/xhiveframework
yarn install --immutable --immutable-cache --check-cache
git diff --exit-code yarn.lock

- name: Instrument Source Code
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/xhiveframework-bench/apps/xhiveframework/ && npx nyc instrument -x 'xhiveframework/public/dist/**' -x 'xhiveframework/public/js/lib/**' -x '**/*.bundle.js' --compact=false --in-place xhiveframework

- name: Build
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/xhiveframework-bench/ && bench build --apps xhiveframework

- name: Site Setup
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
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
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
cd ~/xhiveframework-bench/
bench --site test_site run-ui-tests xhiveframework --headless --parallel --ci-build-id $GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT -- --record
env:
CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb

- name: Show bench console if tests failed
if: ${{ failure() }}
run: cat ~/xhiveframework-bench/bench_start.log

+ 5
- 0
.github/xhive-framework-logo.svg
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


+ 196
- 0
.gitignore Zobrazit soubor

@@ -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/

+ 119
- 0
.mergify.yml Zobrazit soubor

@@ -0,0 +1,119 @@
pull_request_rules:
- name: Auto-close PRs on stable branch
conditions:
- and:
- and:
- author!=surajshetty3416
- author!=gavindsouza
- author!=deepeshgarg007
- author!=ankush
- author!=mergify[bot]
- or:
- 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://github.com/xhiveframework/xhiveerp/wiki/Pull-Request-Checklist#which-branch

- name: Automatic merge on CI success and review
conditions:
- status-success=Sider
- status-success=Check Commit Titles
- status-success=Python Unit Tests (MariaDB) (1)
- status-success=Python Unit Tests (MariaDB) (2)
- status-success=Python Unit Tests (Postgres) (1)
- status-success=Python Unit Tests (Postgres) (2)
- status-success=UI Tests (Cypress) (1)
- status-success=UI Tests (Cypress) (2)
- status-success=UI Tests (Cypress) (3)
- status-success=security/snyk (xhiveframework)
- label!=dont-merge
- label!=squash
- "#approved-reviews-by>=1"
actions:
merge:
method: merge
- name: Automatic squash on CI success and review
conditions:
- status-success=Sider
- status-success=Python Unit Tests (MariaDB) (1)
- status-success=Python Unit Tests (MariaDB) (2)
- status-success=Python Unit Tests (Postgres) (1)
- status-success=Python Unit Tests (Postgres) (2)
- status-success=UI Tests (Cypress) (1)
- status-success=UI Tests (Cypress) (2)
- status-success=UI Tests (Cypress) (3)
- status-success=security/snyk (xhiveframework)
- 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 }}"

- name: backport to develop
conditions:
- label="backport develop"
actions:
backport:
branches:
- develop
assignees:
- "{{ author }}"

- name: backport to version-13-pre-release
conditions:
- label="backport version-13-pre-release"
actions:
backport:
branches:
- version-13-pre-release
assignees:
- "{{ author }}"

- name: backport to version-12-hotfix
conditions:
- label="backport version-12-hotfix"
actions:
backport:
branches:
- version-12-hotfix
assignees:
- "{{ author }}"

+ 65
- 0
.pre-commit-config.yaml Zobrazit soubor

@@ -0,0 +1,65 @@
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/asottile/pyupgrade
rev: v2.34.0
hooks:
- id: pyupgrade
args: ['--py310-plus']

- repo: https://github.com/xhiveframework/black
rev: 951ccf4d5bb0d692b457a5ebc4215d755618eb68
hooks:
- id: black

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.7.1
hooks:
- id: prettier
types_or: [javascript]
# Ignore any files that might contain jinja / bundles
exclude: |
(?x)^(
xhiveframework/public/dist/.*|
.*node_modules.*|
.*boilerplate.*|
xhiveframework/www/website_script.js|
xhiveframework/templates/includes/.*|
xhiveframework/public/js/lib/.*
)$


- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort

- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
hooks:
- id: flake8
additional_dependencies: ['flake8-bugbear',]

ci:
autoupdate_schedule: weekly
skip: []
submodules: false

+ 24
- 0
.releaserc Zobrazit soubor

@@ -0,0 +1,24 @@
{
"branches": ["version-14"],
"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"
]
}

+ 101
- 0
.snyk Zobrazit soubor

@@ -0,0 +1,101 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.19.0
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
SNYK-JS-AWESOMPLETE-174474:
- awesomplete:
reason: No patch available
expires: '2019-06-11T14:12:04.995Z'
'npm:mem:20180117':
- showdown > yargs > os-locale > mem:
reason: No patch available
expires: '2019-06-11T14:12:04.995Z'
SNYK-PYTHON-PYYAML-550022:
- '*':
reason: Project is not directly dependant on the package
expires: 2021-04-01T18:02:21.256Z
# patches apply the minimum changes required to fix a vulnerability
patch:
'npm:extend:20180424':
- superagent > extend:
patched: '2019-05-09T10:14:19.246Z'
SNYK-JS-LODASH-450202:
- xhiveframework-datatable > lodash:
patched: '2020-01-31T01:33:09.889Z'
SNYK-JS-LODASH-567746:
- xhiveframework-datatable > lodash:
patched: '2020-04-30T23:02:32.330Z'
- quagga > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > lodash:
patched: '2020-04-30T23:02:32.330Z'
- tailwindcss > lodash:
patched: '2020-04-30T23:02:32.330Z'
- '@tailwindcss/ui > @tailwindcss/custom-forms > lodash':
patched: '2020-04-30T23:02:32.330Z'
- snyk > @snyk/dep-graph > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > inquirer > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > snyk-config > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > snyk-mvn-plugin > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > snyk-nodejs-lockfile-parser > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > snyk-nuget-plugin > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > @snyk/dep-graph > graphlib > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > snyk-go-plugin > graphlib > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > snyk-nodejs-lockfile-parser > graphlib > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > snyk-nuget-plugin > dotnet-deps-parser > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > snyk-php-plugin > @snyk/composer-lockfile-parser > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > graphlib > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/ruby-semver > lodash:
patched: '2020-04-30T23:02:32.330Z'
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/dep-graph > graphlib > lodash:
patched: '2020-04-30T23:02:32.330Z'
- quill-image-resize > lodash:
patched: '2020-08-24T23:06:37.710Z'
- node-sass > lodash:
patched: '2020-09-15T23:06:41.931Z'
- node-sass > sass-graph > lodash:
patched: '2020-09-15T23:06:41.931Z'
- node-sass > gaze > globule > lodash:
patched: '2020-09-15T23:06:41.931Z'
- snyk > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-cpp-plugin > @snyk/dep-graph > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-go-plugin > @snyk/dep-graph > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-gradle-plugin > @snyk/dep-graph > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-docker-plugin > snyk-nodejs-lockfile-parser > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-mvn-plugin > @snyk/java-call-graph-builder > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/dep-graph > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-php-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-gradle-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-mvn-plugin > @snyk/cli-interface > @snyk/dep-graph > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > @snyk/dep-graph > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-nodejs-lockfile-parser > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'
- snyk > snyk-go-plugin > graphlib > lodash:
patched: '2020-09-16T23:06:38.881Z'

+ 7
- 0
CODEOWNERS Zobrazit soubor

@@ -0,0 +1,7 @@
# 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
workspace @shariquerik

+ 46
- 0
CODE_OF_CONDUCT.md Zobrazit soubor

@@ -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.co. 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/

+ 21
- 0
LICENSE Zobrazit soubor

@@ -0,0 +1,21 @@
The MIT License

Copyright (c) 2016-2021 Xhive Global Ltd. <developers@xhiveframework.co>

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.

+ 74
- 0
README.md Zobrazit soubor

@@ -0,0 +1,74 @@
<div align="center">
<h1>
<br>
<a href="https://xhiveframework.co">
<img src=".github/xhive-framework-logo.svg" height="50">
</a>
</h1>
<h3>
a web framework with <a href="https://www.youtube.com/watch?v=LOjk3m0wTwg">"batteries included"</a>
</h3>
<h5>
it's pronounced - <em>fra-pay</em>
</h5>
</div>

<div align="center">
<a href="https://github.com/xhiveframework/xhiveframework/actions/workflows/server-mariadb-tests.yml">
<img src="https://github.com/xhiveframework/xhiveframework/actions/workflows/server-mariadb-tests.yml/badge.svg">
</a>
<a href="https://github.com/xhiveframework/xhiveframework/actions/workflows/ui-tests.yml">
<img src="https://github.com/xhiveframework/xhiveframework/actions/workflows/ui-tests.yml/badge.svg?branch=develop">
</a>
<a href='https://xhiveframework.co/docs'>
<img src='https://img.shields.io/badge/docs-📖-7575FF.svg?style=flat-square'/>
</a>
<a href='https://www.codetriage.com/xhiveframework/xhiveframework'>
<img src='https://www.codetriage.com/xhiveframework/xhiveframework/badges/users.svg'>
</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.co)

<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

* [Install via Docker](https://github.com/xhiveframework/xhiveframework_docker)
* [Install via XhiveFramework Bench](https://github.com/xhiveframework/bench)
* [Offical Documentation](https://xhiveframework.co/docs/user/en/installation)
* [Managed Hosting on XhiveFramework Cloud](https://xhiveframeworkcloud.com/xhiveframework/signup)

## Contributing

1. [Code of Conduct](CODE_OF_CONDUCT.md)
1. [Contribution Guidelines](https://github.com/xhiveframework/xhiveerp/wiki/Contribution-Guidelines)
1. [Security Policy](SECURITY.md)
1. [Translations](https://translate.xhiveerp.co)

## Resources

1. [xhiveframework.co](https://xhiveframework.co) - Official documentation of the XhiveFramework 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).

+ 7
- 0
SECURITY.md Zobrazit soubor

@@ -0,0 +1,7 @@
# Security Policy

The XhiveFramework team and community take security issues in the XhiveFramework Framework seriously. To report a security issue, fill out the form at [https://xhiveerp.co/security/report](https://xhiveerp.co/security/report).

You can help us make XhiveFramework and consequently all XhiveFramework dependent apps like [XhiveERP](https://xhiveerp.co) more secure by following the [Reporting guidelines](https://xhiveerp.co/security).

We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.

+ 31
- 0
attributions.md Zobrazit soubor

@@ -0,0 +1,31 @@
## 3rd-Party Software Report

The following 3rd-party software packages may be used by or distributed with <https://github.com/xhiveframework/xhiveframework>.

- 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

### 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

+ 35
- 0
codecov.yml Zobrazit soubor

@@ -0,0 +1,35 @@
codecov:
require_ci_to_pass: yes

coverage:
status:
project:
default: false
server:
target: auto
threshold: 0.5%
flags:
- server
patch:
default: false
server:
target: 85%
threshold: 0%
only_pulls: true
if_ci_failed: ignore
flags:
- server

comment:
layout: "diff, flags"
require_changes: true

flags:
server:
paths:
- ".*\\.py"
carryforward: true
ui-tests:
paths:
- ".*\\.js"
carryforward: true

+ 25
- 0
commitlint.config.js Zobrazit soubor

@@ -0,0 +1,25 @@
module.exports = {
parserPreset: "conventional-changelog-conventionalcommits",
rules: {
"subject-empty": [2, "never"],
"type-case": [2, "always", "lower-case"],
"type-empty": [2, "never"],
"type-enum": [
2,
"always",
[
"build",
"chore",
"ci",
"docs",
"feat",
"fix",
"perf",
"refactor",
"revert",
"style",
"test",
],
],
},
};

+ 24
- 0
cypress.config.js Zobrazit soubor

@@ -0,0 +1,24 @@
const { defineConfig } = require("cypress");

module.exports = defineConfig({
projectId: "92odwv",
adminPassword: "admin",
testUser: "xhiveframework@example.com",
defaultCommandTimeout: 20000,
pageLoadTimeout: 15000,
video: true,
videoUploadOnPasses: false,
retries: {
runMode: 2,
openMode: 2,
},
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require("./cypress/plugins/index.js")(on, config);
},
baseUrl: "http://test_site_ui:8000",
specPattern: ["./cypress/integration/*.js", "**/ui_test_*.js"],
},
});

+ 30
- 0
cypress/fixtures/child_table_doctype.js Zobrazit soubor

@@ -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,
};

+ 59
- 0
cypress/fixtures/child_table_doctype_1.js Zobrazit soubor

@@ -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,
};

+ 53
- 0
cypress/fixtures/custom_submittable_doctype.js Zobrazit soubor

@@ -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,
};

+ 65
- 0
cypress/fixtures/data_field_validation_doctype.js Zobrazit soubor

@@ -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.co",
},
],
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,
};

+ 48
- 0
cypress/fixtures/datetime_doctype.js Zobrazit soubor

@@ -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,
};

+ 45
- 0
cypress/fixtures/doctype_to_link.js Zobrazit soubor

@@ -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,
};

+ 52
- 0
cypress/fixtures/doctype_with_child_table.js Zobrazit soubor

@@ -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,
};

+ 46
- 0
cypress/fixtures/doctype_with_phone.js Zobrazit soubor

@@ -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,
};

+ 54
- 0
cypress/fixtures/doctype_with_tab_break.js Zobrazit soubor

@@ -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,
};

+ 5
- 0
cypress/fixtures/example.json Zobrazit soubor

@@ -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"
}

binární
cypress/fixtures/sample_image.jpg Zobrazit soubor

Před Za
Šířka: 1920  |  Výška: 1281  |  Velikost: 244 KiB

+ 44
- 0
cypress/integration/api.js Zobrazit soubor

@@ -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);
});
});
});
});

+ 57
- 0
cypress/integration/awesome_bar.js Zobrazit soubor

@@ -0,0 +1,57 @@
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)").clear();
});

it("navigates to doctype list", () => {
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type("todo", {
delay: 700,
});
cy.get(".awesomplete").findByRole("listbox").should("be.visible");
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type("{enter}", {
delay: 700,
});

cy.get(".title-text").should("contain", "To Do");

cy.location("pathname").should("eq", "/app/todo");
});

it("find text in doctype list", () => {
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type(
"test in todo{enter}",
{ delay: 700 }
);

cy.get(".title-text").should("contain", "To Do");

cy.findByPlaceholderText("ID").should("have.value", "%test%");
cy.clear_filters();
});

it("navigates to new form", () => {
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type(
"new blog post{enter}",
{ delay: 700 }
);

cy.get(".title-text:visible").should("have.text", "New Blog Post");
});

it("calculates math expressions", () => {
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").type(
"55 + 32{downarrow}{enter}",
{ delay: 700 }
);

cy.get(".modal-title").should("contain", "Result");
cy.get(".msgprint").should("contain", "55 + 32 = 87");
});
});

+ 95
- 0
cypress/integration/control_attach.js Zobrazit soubor

@@ -0,0 +1,95 @@
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"
);

//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 "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 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");
});
});

+ 64
- 0
cypress/integration/control_autocomplete.js Zobrazit soubor

@@ -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();
});
});
});

+ 57
- 0
cypress/integration/control_barcode.js Zobrazit soubor

@@ -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("");
});
});
});

+ 80
- 0
cypress/integration/control_color.js Zobrazit soubor

@@ -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, 144, 255)");

//Checking if the correct color is being selected
cy.get("@dialog").then((dialog) => {
let value = dialog.get_value("color");
expect(value).to.equal("#4F9DD9");
});

//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");
});
});

+ 145
- 0
cypress/integration/control_data.js Zobrazit soubor

@@ -0,0 +1,145 @@
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="2"] [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("eq", "/app/test-data-control/new-test-data-control-1");
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.be", "/app/test-data-control/new-test-data-control-1");
cy.get_field("name1").should("have.value", "Komal");
cy.get_field("email").should("have.value", "komal@test.com");
cy.get_field("phone").should("have.value", "9432380001");
});

it("Deleting the doc", () => {
//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");
});
});

+ 83
- 0
cypress/integration/control_date.js Zobrazit soubor

@@ -0,0 +1,83 @@
context("Date Control", () => {
before(() => {
cy.login();
cy.visit("/app");
});

function get_dialog(date_field_options) {
return cy.dialog({
title: "Date",
fields: [
{
label: "Date",
fieldname: "date",
fieldtype: "Date",
in_list_view: 1,
...date_field_options,
},
],
});
}

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(win.cur_dialog.fields_dict.date.value).to.be.equal(
win.xhiveframework.datetime.get_today()
);
});
});
});

+ 48
- 0
cypress/integration/control_date_range.js Zobrazit soubor

@@ -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");
});
});
});

+ 46
- 0
cypress/integration/control_duration.js Zobrazit soubor

@@ -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");
});
});

+ 159
- 0
cypress/integration/control_dynamic_link.js Zobrazit soubor

@@ -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"
);
});
});

+ 88
- 0
cypress/integration/control_float.js Zobrazit soubor

@@ -0,0 +1,88 @@
context("Control Float", () => {
before(() => {
cy.login();
cy.visit("/app/website");
});

function get_dialog_with_float() {
return cy.dialog({
title: "Float Check",
fields: [
{
fieldname: "float_number",
fieldtype: "Float",
Label: "Float",
},
],
});
}

it("check value changes", () => {
get_dialog_with_float().as("dialog");

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.get_field("float_number", "Float").focus();
cy.get_field("float_number", "Float").blur();
cy.get_field("float_number", "Float").focus();
cy.get_field("float_number", "Float").should("have.value", d.focus_expected);
});
});
});

function get_data() {
return [
{
number_format: "#.###,##",
values: [
{
input: "364.87,334",
blur_expected: "36.487,334",
focus_expected: "36487.334",
},
{
input: "36487,334",
blur_expected: "36.487,334",
focus_expected: "36487.334",
},
{
input: "100",
blur_expected: "100,000",
focus_expected: "100",
},
],
},
{
number_format: "#,###.##",
values: [
{
input: "364,87.334",
blur_expected: "36,487.334",
focus_expected: "36487.334",
},
{
input: "36487.334",
blur_expected: "36,487.334",
focus_expected: "36487.334",
},
{
input: "100",
blur_expected: "100.000",
focus_expected: "100",
},
],
},
];
}
});

+ 55
- 0
cypress/integration/control_icon.js Zobrazit soubor

@@ -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").findByRole("searchbox").click().type(search_text);
cy.get(".icon-section .icon-wrapper:not(.hidden)").then((i) => {
cy.get(`.icon-section .icon-wrapper[id*='${search_text}']`).then((icons) => {
expect(i.length).to.equal(icons.length);
});
});

cy.get(".icon-picker").findByRole("searchbox").clear().blur();
cy.get(".icon-section .icon-wrapper").should("not.have.class", "hidden");
});
});

+ 335
- 0
cypress/integration/control_link.js Zobrazit soubor

@@ -0,0 +1,335 @@
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.clear_cache();
cy.wait(500);

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");
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").type(cy.config("testUser"), { delay: 100 }).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: 100 });
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: 100 });
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");
});
});
});

+ 22
- 0
cypress/integration/control_markdown_editor.js Zobrazit soubor

@@ -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",
"![](/private/files/sample_image"
);
});
});

+ 92
- 0
cypress/integration/control_phone.js Zobrazit soubor

@@ -0,0 +1,92 @@
import doctype_with_phone from "../fixtures/doctype_with_phone";

context("Control Phone", () => {
before(() => {
cy.login();
cy.visit("/app/website");
});

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.get(".phone-picker .phone-wrapper[id='afghanistan']").click();
cy.get(".selected-phone").click();
cy.get(".phone-picker .phone-wrapper[id='india']").click();
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({ multiple: true });
cy.get(".xhiveframework-control[data-fieldname=phone]")
.findByRole("textbox")
.first()
.type(phone_number, { force: true });

cy.get_field("phone").first().should("have.value", phone_number);
cy.get_field("phone").first().blur({ force: true });
cy.wait(100);
cy.get("@dialog").then((dialog) => {
let value = dialog.get_value("phone");
expect(value).to.equal("+91-" + phone_number);
});
});

it("case insensitive search for country and clear search", () => {
let search_text = "india";
cy.get(".selected-phone").click().first();
cy.get(".phone-picker").findByRole("searchbox").click().type(search_text);
cy.get(".phone-section .phone-wrapper:not(.hidden)").then((i) => {
cy.get(`.phone-section .phone-wrapper[id*="${search_text.toLowerCase()}"]`).then(
(countries) => {
expect(i.length).to.equal(countries.length);
}
);
});

cy.get(".phone-picker").findByRole("searchbox").clear().blur();
cy.get(".phone-section .phone-wrapper").should("not.have.class", "hidden");
});

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");
});
});

+ 54
- 0
cypress/integration/control_rating.js Zobrazit soubor

@@ -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);
});
});

+ 41
- 0
cypress/integration/control_select.js Zobrazit soubor

@@ -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();
});
});
});

+ 57
- 0
cypress/integration/custom_buttons.js Zobrazit soubor

@@ -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);
});
});
}
);

+ 23
- 0
cypress/integration/customize_form.js Zobrazit soubor

@@ -0,0 +1,23 @@
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.click_form_section("Naming");
const naming_rule_default_autoname_map = {
"Set by user": "prompt",
"By fieldname": "field:",
'By "Naming Series" field': "naming_series:",
Expression: "format:",
"Expression (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);
});
});
});

+ 50
- 0
cypress/integration/dashboard.js Zobrazit soubor

@@ -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");
});
});

+ 22
- 0
cypress/integration/dashboard_chart.js Zobrazit soubor

@@ -0,0 +1,22 @@
context("Dashboard Chart", () => {
before(() => {
cy.login();
cy.visit("/app/website");
});

it("Check filter populate for child table doctype", () => {
cy.visit("/app/dashboard-chart/new-dashboard-chart-1");
cy.get('[data-fieldname="parent_document_type"]').should("have.css", "display", "none");

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();
});
});

+ 91
- 0
cypress/integration/dashboard_links.js Zobrazit soubor

@@ -0,0 +1,91 @@
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.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.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.get('[data-doctype="Contact"]').should("contain", "Contact");
cy.findByText("Connections");
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('[data-report="Website Analytics"]').contains("Website Analytics").click();
cy.findByText("Website Analytics");
});
});

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");
});
});

+ 45
- 0
cypress/integration/data_field_form_validation.js Zobrazit soubor

@@ -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.co");
validateField("url", "abcd.com", "http://google.com/home");
validateField("url", "&&http://google.uae", "gopher://xhiveframework.co");
validateField("url", "ftt2:://google.in?q=news", "ftps2://xhiveframework.co/__/#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");
});
});
});

+ 126
- 0
cypress/integration/datetime.js Zobrazit soubor

@@ -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);
});
});
});
});

+ 19
- 0
cypress/integration/datetime_field_form_validation.js Zobrazit soubor

@@ -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');
// });
// });
// });

+ 152
- 0
cypress/integration/depends_on.js Zobrazit soubor

@@ -0,0 +1,152 @@
context("Depends On", () => {
before(() => {
cy.login();
cy.visit("/app/website");
return cy
.window()
.its("xhiveframework")
.then((xhiveframework) => {
return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_child_doctype", {
name: "Child Test Depends On",
fields: [
{
label: "Child Test Field",
fieldname: "child_test_field",
fieldtype: "Data",
in_list_view: 1,
},
{
label: "Child Dependant Field",
fieldname: "child_dependant_field",
fieldtype: "Data",
in_list_view: 1,
},
{
label: "Child Display Dependant Field",
fieldname: "child_display_dependant_field",
fieldtype: "Data",
in_list_view: 1,
},
],
});
})
.then((xhiveframework) => {
return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_doctype", {
name: "Test Depends On",
fields: [
{
label: "Test Field",
fieldname: "test_field",
fieldtype: "Data",
},
{
label: "Dependant Field",
fieldname: "dependant_field",
fieldtype: "Data",
mandatory_depends_on: "eval:doc.test_field=='Some Value'",
read_only_depends_on: "eval:doc.test_field=='Some Other Value'",
},
{
label: "Display Dependant Field",
fieldname: "display_dependant_field",
fieldtype: "Data",
depends_on: "eval:doc.test_field=='Value'",
},
{
label: "Child Test Depends On Field",
fieldname: "child_test_depends_on_field",
fieldtype: "Table",
read_only_depends_on: "eval:doc.test_field=='Some Other Value'",
options: "Child Test Depends On",
},
{
label: "Dependent Tab",
fieldname: "dependent_tab",
fieldtype: "Tab Break",
depends_on: "eval:doc.test_field=='Show Tab'",
},
{
fieldname: "tab_section",
fieldtype: "Section Break",
},
{
label: "Field in Tab",
fieldname: "field_in_tab",
fieldtype: "Data",
},
],
});
});
});
it("should show the tab on other setting field value", () => {
cy.new_form("Test Depends On");
cy.fill_field("test_field", "Show Tab");
cy.get("body").click();
cy.findByRole("tab", { name: "Dependent Tab" }).should("be.visible");
});
it("should set the field as mandatory depending on other fields value", () => {
cy.new_form("Test Depends On");
cy.fill_field("test_field", "Some Value");
cy.findByRole("button", { name: "Save" }).click();
cy.get(".msgprint-dialog .modal-title").contains("Missing Fields").should("be.visible");
cy.hide_dialog();
cy.fill_field("test_field", "Random value");
cy.findByRole("button", { name: "Save" }).click();
cy.get(".msgprint-dialog .modal-title")
.contains("Missing Fields")
.should("not.be.visible");
});
it("should set the field as read only depending on other fields value", () => {
cy.new_form("Test Depends On");
cy.fill_field("dependant_field", "Some Value");
cy.fill_field("test_field", "Some Other Value");
cy.get("body").click();
cy.get('.control-input [data-fieldname="dependant_field"]').should("be.disabled");
cy.fill_field("test_field", "Random Value");
cy.get("body").click();
cy.get('.control-input [data-fieldname="dependant_field"]').should("not.be.disabled");
});
it("should set the table and its fields as read only depending on other fields value", () => {
cy.new_form("Test Depends On");
cy.fill_field("dependant_field", "Some Value");
//cy.fill_field('test_field', 'Some Other Value');
cy.get('.xhiveframework-control[data-fieldname="child_test_depends_on_field"]').as("table");
cy.get("@table").findByRole("button", { name: "Add Row" }).click();
cy.get("@table").find('[data-idx="1"]').as("row1");
cy.get("@row1").find(".btn-open-row").click();
cy.get("@row1").find(".form-in-grid").as("row1-form_in_grid");
//cy.get('@row1-form_in_grid').find('')
cy.fill_table_field("child_test_depends_on_field", "1", "child_test_field", "Some Value");
cy.fill_table_field(
"child_test_depends_on_field",
"1",
"child_dependant_field",
"Some Other Value"
);

cy.get("@row1-form_in_grid").find(".grid-collapse-row").click();

// set the table to read-only
cy.fill_field("test_field", "Some Other Value");

// grid row form fields should be read-only
cy.get("@row1").find(".btn-open-row").click();

cy.get("@row1-form_in_grid")
.find('.control-input [data-fieldname="child_test_field"]')
.should("be.disabled");
cy.get("@row1-form_in_grid")
.find('.control-input [data-fieldname="child_dependant_field"]')
.should("be.disabled");
});
it("should display the field depending on other fields value", () => {
cy.new_form("Test Depends On");
cy.get('.control-input [data-fieldname="display_dependant_field"]').should(
"not.be.visible"
);
cy.get('.control-input [data-fieldname="test_field"]').clear();
cy.fill_field("test_field", "Value");
cy.get("body").click();
cy.get('.control-input [data-fieldname="display_dependant_field"]').should("be.visible");
});
});

+ 101
- 0
cypress/integration/discussions.js Zobrazit soubor

@@ -0,0 +1,101 @@
context("Discussions", () => {
before(() => {
cy.login();
cy.visit("/app");
return cy
.window()
.its("xhiveframework")
.then((xhiveframework) => {
return xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_data_for_discussions");
});
});

const reply_through_modal = () => {
cy.visit("/test-page-discussions");

// Open the modal
cy.get(".reply").click();
cy.wait(500);
cy.get(".discussion-modal").should("be.visible");

// Enter title
cy.get(".modal .topic-title")
.type("Discussion from tests")
.should("have.value", "Discussion from tests");

// Enter comment
cy.get(".modal .comment-field")
.type("This is a discussion from the cypress ui tests.")
.should("have.value", "This is a discussion from the cypress ui tests.");

// Submit
cy.get(".modal .submit-discussion").click();
cy.wait(2000);

// Check if discussion is added to page and content is visible
cy.get(".sidebar-parent:first .discussion-topic-title").should(
"have.text",
"Discussion from tests"
);
cy.get(".discussion-on-page:visible").should("have.class", "show");
cy.get(".discussion-on-page:visible .reply-card .reply-text").should(
"have.text",
"This is a discussion from the cypress ui tests.\n"
);
};

const reply_through_comment_box = () => {
cy.get(".discussion-form:visible .comment-field")
.type(
"This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page."
)
.should(
"have.value",
"This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page."
);

cy.get(".discussion-form:visible .submit-discussion").click();
cy.wait(3000);
cy.get(".discussion-on-page:visible").should("have.class", "show");
cy.get(".discussion-on-page:visible")
.children(".reply-card")
.eq(1)
.find(".reply-text")
.should(
"have.text",
"This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page.\n"
);
};

const cancel_and_clear_comment_box = () => {
cy.get(".discussion-form:visible .comment-field")
.type("This is a discussion from the cypress ui tests.")
.should("have.value", "This is a discussion from the cypress ui tests.");

cy.get(".discussion-form:visible .cancel-comment").click();
cy.get(".discussion-form:visible .comment-field").should("have.value", "");
};

const single_thread_discussion = () => {
cy.visit("/test-single-thread");
cy.get(".discussions-sidebar").should("have.length", 0);
cy.get(".reply").should("have.length", 0);

cy.get(".discussion-form:visible .comment-field")
.type("This comment is being made on a single thread discussion.")
.should("have.value", "This comment is being made on a single thread discussion.");

cy.get(".discussion-form:visible .submit-discussion").click();
cy.wait(3000);
cy.get(".discussion-on-page")
.children(".reply-card")
.eq(-1)
.find(".reply-text")
.should("have.text", "This comment is being made on a single thread discussion.\n");
};

it("reply through modal", reply_through_modal);
it("reply through comment box", reply_through_comment_box);
it("cancel and clear comment box", cancel_and_clear_comment_box);
it("single thread discussion", single_thread_discussion);
});

+ 86
- 0
cypress/integration/file_uploader.js Zobrazit soubor

@@ -0,0 +1,86 @@
context("FileUploader", () => {
before(() => {
cy.login();
cy.visit("/app");
});

function open_upload_dialog() {
cy.window()
.its("xhiveframework")
.then((xhiveframework) => {
new xhiveframework.ui.FileUploader();
});
}

it("upload dialog api works", () => {
open_upload_dialog();
cy.get_open_dialog().should("contain", "Drag and drop files");
cy.hide_dialog();
});

it("should accept dropped files", () => {
open_upload_dialog();

cy.get_open_dialog()
.find(".file-upload-area")
.selectFile("cypress/fixtures/example.json", {
action: "drag-drop",
});

cy.get_open_dialog().find(".file-name").should("contain", "example.json");
cy.intercept("POST", "/api/method/upload_file").as("upload_file");
cy.get_open_dialog().findByRole("button", { name: "Upload" }).click();
cy.wait("@upload_file").its("response.statusCode").should("eq", 200);
cy.get(".modal:visible").should("not.exist");
});

it("should accept uploaded files", () => {
open_upload_dialog();

cy.get_open_dialog().findByRole("button", { name: "Library" }).click();
cy.findByPlaceholderText("Search by filename or extension").type("example.json");
cy.get_open_dialog().findAllByText("example.json").first().click();
cy.intercept("POST", "/api/method/upload_file").as("upload_file");
cy.get_open_dialog().findByRole("button", { name: "Upload" }).click();
cy.wait("@upload_file")
.its("response.body.message")
.should("have.property", "file_name", "example.json");
cy.get(".modal:visible").should("not.exist");
});

it("should accept web links", () => {
open_upload_dialog();

cy.get_open_dialog().findByRole("button", { name: "Link" }).click();
cy.get_open_dialog()
.findByPlaceholderText("Attach a web link")
.type("https://github.com", { delay: 100, force: true });
cy.intercept("POST", "/api/method/upload_file").as("upload_file");
cy.get_open_dialog().findByRole("button", { name: "Upload" }).click();
cy.wait("@upload_file")
.its("response.body.message")
.should("have.property", "file_url", "https://github.com");
cy.get(".modal:visible").should("not.exist");
});

it("should allow cropping and optimization for valid images", () => {
open_upload_dialog();

cy.get_open_dialog()
.find(".file-upload-area")
.selectFile("cypress/fixtures/sample_image.jpg", {
action: "drag-drop",
});

cy.get_open_dialog().findAllByText("sample_image.jpg").should("exist");
cy.get_open_dialog().find(".btn-crop").first().click();
cy.get_open_dialog().findByRole("button", { name: "Crop" }).click();
cy.get_open_dialog().findAllByRole("checkbox", { name: "Optimize" }).should("exist");
cy.get_open_dialog().findAllByLabelText("Optimize").first().click();

cy.intercept("POST", "/api/method/upload_file").as("upload_file");
cy.get_open_dialog().findByRole("button", { name: "Upload" }).click();
cy.wait("@upload_file").its("response.statusCode").should("eq", 200);
cy.get(".modal:visible").should("not.exist");
});
});

+ 51
- 0
cypress/integration/first_day_of_the_week.js Zobrazit soubor

@@ -0,0 +1,51 @@
context("First Day of the Week", () => {
before(() => {
cy.login();
});

beforeEach(() => {
cy.visit("/app/system-settings");
cy.findByText("Date and Number Format").click();
});

it("Date control starts with same day as selected in System Settings", () => {
cy.intercept(
"POST",
"/api/method/xhiveframework.core.doctype.system_settings.system_settings.load"
).as("load_settings");
cy.fill_field("first_day_of_the_week", "Tuesday", "Select");
cy.findByRole("button", { name: "Save" }).click();
cy.wait("@load_settings");
cy.dialog({
title: "Date",
fields: [
{
label: "Date",
fieldname: "date",
fieldtype: "Date",
},
],
});
cy.get_field("date").click();
cy.get(".datepicker--day-name").eq(0).should("have.text", "Tu");
});

it("Calendar view starts with same day as selected in System Settings", () => {
cy.intercept(
"POST",
"/api/method/xhiveframework.core.doctype.system_settings.system_settings.load"
).as("load_settings");
cy.fill_field("first_day_of_the_week", "Monday", "Select");
cy.findByRole("button", { name: "Save" }).click();
cy.wait("@load_settings");
cy.visit("app/todo/view/calendar/default");
cy.get(".fc-day-header > span").eq(0).should("have.text", "Mon");
});

after(() => {
cy.visit("/app/system-settings");
cy.findByText("Date and Number Format").click();
cy.fill_field("first_day_of_the_week", "Sunday", "Select");
cy.findByRole("button", { name: "Save" }).click();
});
});

+ 92
- 0
cypress/integration/folder_navigation.js Zobrazit soubor

@@ -0,0 +1,92 @@
context("Folder Navigation", () => {
before(() => {
cy.visit("/login");
cy.login();
cy.visit("/app/file");
});

it("Adding Folders", () => {
//Adding filter to go into the home folder
cy.get(".filter-selector > .btn").findByText("1 filter").click();
cy.findByRole("button", { name: "Clear Filters" }).click();
cy.get(".filter-action-buttons > .text-muted").findByText("+ Add a Filter").click();
cy.get(".fieldname-select-area > .awesomplete > .form-control:last").type("Fol{enter}");
cy.get(
".filter-field > .form-group > .link-field > .awesomplete > .input-with-feedback"
).type("Home{enter}");
cy.get(".filter-action-buttons > div > .btn-primary").findByText("Apply Filters").click();

//Adding folder (Test Folder)
cy.click_menu_button("New Folder");
cy.fill_field("value", "Test Folder");
cy.click_modal_primary_button("Create");
});

it("Navigating the nested folders, checking if the URL formed is correct, checking if the added content in the child folder is correct", () => {
//Navigating inside the Attachments folder
cy.wait(500);
cy.get('[title="Attachments"] > span').click();

//To check if the URL formed after visiting the attachments folder is correct
cy.location("pathname").should("eq", "/app/file/view/home/Attachments");
cy.visit("/app/file/view/home/Attachments");

//Adding folder inside the attachments folder
cy.click_menu_button("New Folder");
cy.fill_field("value", "Test Folder");
cy.click_modal_primary_button("Create");

//Navigating inside the added folder in the Attachments folder
cy.wait(500);
cy.get('[title="Test Folder"] > span').click();

//To check if the URL is correct after visiting the Test Folder
cy.location("pathname").should("eq", "/app/file/view/home/Attachments/Test%20Folder");
cy.visit("/app/file/view/home/Attachments/Test%20Folder");

//Adding a file inside the Test Folder
cy.findByRole("button", { name: "Add File" }).eq(0).click({ force: true });
cy.get(".file-uploader").findByText("Link").click();
cy.get(".input-group > .form-control").type(
"https://wallpaperplay.com/walls/full/8/2/b/72402.jpg"
);
cy.click_modal_primary_button("Upload");

//To check if the added file is present in the Test Folder
cy.visit("/app/file/view/home/Attachments");
cy.wait(500);
cy.get("span.level-item > a > span").should("contain", "Test Folder");
cy.visit("/app/file/view/home/Attachments/Test%20Folder");

cy.wait(500);
cy.get(".list-row-container").eq(0).should("contain.text", "72402.jpg");
cy.get(".list-row-checkbox").eq(0).click();

cy.intercept({
method: "POST",
url: "api/method/xhiveframework.desk.reportview.delete_items",
}).as("file_deleted");

//Deleting the added file from the Test folder
cy.click_action_button("Delete");
cy.click_modal_primary_button("Yes");
cy.wait("@file_deleted");

//Deleting the Test Folder
cy.visit("/app/file/view/home/Attachments");
cy.get(".list-row-checkbox").eq(0).click();
cy.click_action_button("Delete");
cy.click_modal_primary_button("Yes");
cy.wait("@file_deleted");
});

it("Deleting Test Folder from the home", () => {
//Deleting the Test Folder added in the home directory
cy.visit("/app/file/view/home");
cy.get(".level-left > .list-subject > .file-select >.list-row-checkbox")
.eq(0)
.click({ force: true, delay: 500 });
cy.click_action_button("Delete");
cy.click_modal_primary_button("Yes");
});
});

+ 166
- 0
cypress/integration/form.js Zobrazit soubor

@@ -0,0 +1,166 @@
context("Form", () => {
before(() => {
cy.login();
cy.visit("/app/website");
return cy
.window()
.its("xhiveframework")
.then((xhiveframework) => {
return xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_contact_records");
});
});

it("create a new form", () => {
cy.visit("/app/todo/new");
cy.get_field("description", "Text Editor")
.type("this is a test todo", { force: true })
.wait(200);
cy.get(".page-title").should("contain", "Not Saved");
cy.intercept({
method: "POST",
url: "api/method/xhiveframework.desk.form.save.savedocs",
}).as("form_save");
cy.get(".primary-action").click();
cy.wait("@form_save").its("response.statusCode").should("eq", 200);

cy.go_to_list("ToDo");
cy.clear_filters();
cy.get(".page-head").findByTitle("To Do").should("exist");
cy.get(".list-row").should("contain", "this is a test todo");
});

it("navigates between documents with child table list filters applied", () => {
cy.visit("/app/contact");

cy.clear_filters();
cy.get('.standard-filter-section [data-fieldname="name"] input')
.type("Test Form Contact 3")
.blur();
cy.click_listview_row_item_with_text("Test Form Contact 3");

cy.get("#page-Contact .page-head").findByTitle("Test Form Contact 3").should("exist");
cy.get(".prev-doc").should("be.visible").click();
cy.get(".msgprint-dialog .modal-body").contains("No further records").should("be.visible");
cy.hide_dialog();

cy.get("#page-Contact .page-head").findByTitle("Test Form Contact 3").should("exist");
cy.get(".next-doc").should("be.visible").click();
cy.get(".msgprint-dialog .modal-body").contains("No further records").should("be.visible");
cy.hide_dialog();

cy.get("#page-Contact .page-head").findByTitle("Test Form Contact 3").should("exist");

// clear filters
cy.visit("/app/contact");
cy.clear_filters();
});

it("validates behaviour of Data options validations in child table", () => {
// test email validations for set_invalid controller
let website_input = "website.in";
let valid_email = "user@email.com";
let expectBackgroundColor = "rgb(255, 245, 245)";

cy.visit("/app/contact/new");
cy.get('.xhiveframework-control[data-fieldname="email_ids"]').as("table");
cy.get("@table").find("button.grid-add-row").click();
cy.get("@table").find("button.grid-add-row").click();
cy.get("@table").find('[data-idx="1"]').as("row1");
cy.get("@table").find('[data-idx="2"]').as("row2");
cy.get("@row1").click();
cy.get("@row1").find("input.input-with-feedback.form-control").as("email_input1");

cy.get("@email_input1").type(website_input, { waitForAnimations: false });
cy.fill_field("company_name", "Test Company");

cy.get("@row2").click();
cy.get("@row2").find("input.input-with-feedback.form-control").as("email_input2");
cy.get("@email_input2").type(valid_email, { waitForAnimations: false });

cy.get("@row1").click();
cy.get("@email_input1").should(($div) => {
const style = window.getComputedStyle($div[0]);
expect(style.backgroundColor).to.equal(expectBackgroundColor);
});
cy.get("@email_input1").should("have.class", "invalid");

cy.get("@row2").click();
cy.get("@email_input2").should("not.have.class", "invalid");
});

it("Shows version conflict warning", { scrollBehavior: false }, () => {
cy.visit("/app/todo");

cy.insert_doc("ToDo", { description: "old" }).then((doc) => {
cy.visit(`/app/todo/${doc.name}`);
// make form dirty
cy.fill_field("status", "Cancelled", "Select");

// update doc using api - simulating parallel change by another user
cy.update_doc("ToDo", doc.name, { status: "Closed" }).then(() => {
cy.findByRole("button", { name: "Refresh" }).click();
cy.get_field("status", "Select").should("have.value", "Closed");
});
});
});

it("let user undo/redo field value changes", { scrollBehavior: false }, () => {
const jump_to_field = (field_label) => {
cy.get("body")
.type("{esc}") // lose focus if any
.type("{ctrl+j}") // jump to field
.type(field_label)
.wait(500)
.type("{enter}")
.wait(200)
.type("{enter}")
.wait(500);
};

const type_value = (value) => {
cy.focused().clear().type(value).type("{esc}");
};

const undo = () => cy.get("body").type("{esc}").type("{ctrl+z}").wait(500);
const redo = () => cy.get("body").type("{esc}").type("{ctrl+y}").wait(500);

cy.new_form("User");

jump_to_field("Email");
type_value("admin@example.com");

jump_to_field("Username");
type_value("admin42");

jump_to_field("Send Welcome Email");
cy.focused().uncheck();

// make a mistake
jump_to_field("Username");
type_value("admin24");

// undo behaviour
undo();
cy.get_field("username").should("have.value", "admin42");

// redo behaviour
redo();
cy.get_field("username").should("have.value", "admin24");

// undo everything & redo everything, ensure same values at the end
undo();
undo();
undo();
undo();
redo();
redo();
redo();
redo();

cy.compare_document({
username: "admin24",
email: "admin@example.com",
send_welcome_email: 0,
});
});
});

+ 30
- 0
cypress/integration/form_tab_break.js Zobrazit soubor

@@ -0,0 +1,30 @@
import doctype_with_tab_break from "../fixtures/doctype_with_tab_break";
const doctype_name = doctype_with_tab_break.name;
context("Form Tab Break", () => {
before(() => {
cy.login();
cy.visit("/app/website");
return cy.insert_doc("DocType", doctype_with_tab_break, true);
});
it("Should switch tab and open correct tabs on validation error", () => {
cy.new_form(doctype_name);
// test tab switch
cy.findByRole("tab", { name: "Tab 2" }).click();
cy.findByText("Phone");
cy.findByRole("tab", { name: "Details" }).click();
cy.findByText("Name");

// form should switch to the tab with un-filled mandatory field
cy.fill_field("username", "Test");
cy.findByRole("button", { name: "Save" }).click();
cy.findByText("Missing Fields");
cy.hide_dialog();
cy.findByText("Phone");
cy.fill_field("phone", "12345678");
cy.findByRole("button", { name: "Save" }).click();

// After save, first tab should have dashboard
cy.get(".form-tabs > .nav-item").eq(0).click();
cy.findByText("Connections");
});
});

+ 94
- 0
cypress/integration/form_tour.js Zobrazit soubor

@@ -0,0 +1,94 @@
context.skip("Form Tour", () => {
before(() => {
cy.login();
cy.visit("/app");
return cy
.window()
.its("xhiveframework")
.then((xhiveframework) => {
return xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_form_tour");
});
});

const open_test_form_tour = () => {
cy.visit("/app/form-tour/Test Form Tour");
cy.findByRole("button", { name: "Show Tour" }).should("be.visible").as("show_tour");
cy.get("@show_tour").click();
cy.wait(500);
cy.url().should("include", "/app/contact");
};

it("jump to a form tour", open_test_form_tour);

it("navigates a form tour", () => {
open_test_form_tour();

cy.get(".xhiveframework-driver").should("be.visible");
cy.get('.xhiveframework-control[data-fieldname="first_name"]').as("first_name");
cy.get("@first_name").should("have.class", "driver-highlighted-element");
cy.get(".xhiveframework-driver").findByRole("button", { name: "Next" }).as("next_btn");

// next btn shouldn't move to next step, if first name is not entered
cy.get("@next_btn").click();
cy.wait(500);
cy.get("@first_name").should("have.class", "driver-highlighted-element");

// after filling the field, next step should be highlighted
cy.fill_field("first_name", "Test Name", "Data");
cy.wait(500);
cy.get("@next_btn").click();
cy.wait(500);

// assert field is highlighted
cy.get('.xhiveframework-control[data-fieldname="last_name"]').as("last_name");
cy.get("@last_name").should("have.class", "driver-highlighted-element");

// after filling the field, next step should be highlighted
cy.fill_field("last_name", "Test Last Name", "Data");
cy.wait(500);
cy.get("@next_btn").click();
cy.wait(500);

// assert field is highlighted
cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("phone_nos");
cy.get("@phone_nos").should("have.class", "driver-highlighted-element");

// move to next step
cy.wait(500);
cy.get("@next_btn").click();
cy.wait(500);

// assert add row btn is highlighted
cy.get("@phone_nos").find(".grid-add-row").as("add_row");
cy.get("@add_row").should("have.class", "driver-highlighted-element");

// add a row & move to next step
cy.wait(500);
cy.get("@add_row").click();
cy.wait(500);

// assert table field is highlighted
cy.get('.grid-row-open .xhiveframework-control[data-fieldname="phone"]').as("phone");
cy.get("@phone").should("have.class", "driver-highlighted-element");
// enter value in a table field
let field = cy.fill_table_field("phone_nos", "1", "phone", "1234567890");
field.blur();

// move to collapse row step
cy.wait(500);
cy.get(".driver-popover-title")
.contains("Test Title 4")
.siblings()
.get("@next_btn")
.click();
cy.wait(500);
// collapse row
cy.get(".grid-row-open .grid-collapse-row").click();
cy.wait(500);

// assert save btn is highlighted
cy.get(".primary-action").should("have.class", "driver-highlighted-element");
cy.wait(500);
cy.get(".xhiveframework-driver").findByRole("button", { name: "Save" }).should("be.visible");
});
});

+ 114
- 0
cypress/integration/grid.js Zobrazit soubor

@@ -0,0 +1,114 @@
context("Grid", () => {
beforeEach(() => {
cy.login();
cy.visit("/app/website");
});
before(() => {
cy.login();
cy.visit("/app/website");
return cy
.window()
.its("xhiveframework")
.then((xhiveframework) => {
return xhiveframework.call(
"xhiveframework.tests.ui_test_helpers.create_contact_phone_nos_records"
);
});
});
it("update docfield property using update_docfield_property", () => {
cy.visit("/app/contact/Test Contact");
cy.window()
.its("cur_frm")
.then((frm) => {
cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table");
let field = frm.get_field("phone_nos");
field.grid.update_docfield_property("is_primary_phone", "hidden", true);

cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
cy.get(".grid-row-open").as("table-form");
cy.get("@table-form")
.find('.xhiveframework-control[data-fieldname="is_primary_phone"]')
.should("be.hidden");
cy.get("@table-form").find(".grid-footer-toolbar").click();

cy.get("@table").find('[data-idx="2"] .edit-grid-row').click();
cy.get(".grid-row-open").as("table-form");
cy.get("@table-form")
.find('.xhiveframework-control[data-fieldname="is_primary_phone"]')
.should("be.hidden");
cy.get("@table-form").find(".grid-footer-toolbar").click();
});
});
it("update docfield property using toggle_display", () => {
cy.visit("/app/contact/Test Contact");
cy.window()
.its("cur_frm")
.then((frm) => {
cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table");
let field = frm.get_field("phone_nos");
field.grid.toggle_display("is_primary_mobile_no", false);

cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
cy.get(".grid-row-open").as("table-form");
cy.get("@table-form")
.find('.xhiveframework-control[data-fieldname="is_primary_mobile_no"]')
.should("be.hidden");
cy.get("@table-form").find(".grid-footer-toolbar").click();

cy.get("@table").find('[data-idx="2"] .edit-grid-row').click();
cy.get(".grid-row-open").as("table-form");
cy.get("@table-form")
.find('.xhiveframework-control[data-fieldname="is_primary_mobile_no"]')
.should("be.hidden");
cy.get("@table-form").find(".grid-footer-toolbar").click();
});
});
it("update docfield property using toggle_enable", () => {
cy.visit("/app/contact/Test Contact");
cy.window()
.its("cur_frm")
.then((frm) => {
cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table");
let field = frm.get_field("phone_nos");
field.grid.toggle_enable("phone", false);

cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
cy.get(".grid-row-open").as("table-form");
cy.get("@table-form")
.find('.xhiveframework-control[data-fieldname="phone"] .control-value')
.should("have.class", "like-disabled-input");
cy.get("@table-form").find(".grid-footer-toolbar").click();

cy.get("@table").find('[data-idx="2"] .edit-grid-row').click();
cy.get(".grid-row-open").as("table-form");
cy.get("@table-form")
.find('.xhiveframework-control[data-fieldname="phone"] .control-value')
.should("have.class", "like-disabled-input");
cy.get("@table-form").find(".grid-footer-toolbar").click();
});
});
it("update docfield property using toggle_reqd", () => {
cy.visit("/app/contact/Test Contact");
cy.window()
.its("cur_frm")
.then((frm) => {
cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table");
let field = frm.get_field("phone_nos");
field.grid.toggle_reqd("phone", false);

cy.get("@table").find('[data-idx="1"] .edit-grid-row').click();
cy.get(".grid-row-open").as("table-form");
cy.get_field("phone").as("phone-field");
cy.get("@phone-field").focus().clear().wait(500).blur();
cy.get("@phone-field").should("not.have.class", "has-error");
cy.get("@table-form").find(".grid-footer-toolbar").click();

cy.get("@table").find('[data-idx="2"] .edit-grid-row').click();
cy.get(".grid-row-open").as("table-form");
cy.get_field("phone").as("phone-field");
cy.get("@phone-field").focus().clear().wait(500).blur();
cy.get("@phone-field").should("not.have.class", "has-error");
cy.get("@table-form").find(".grid-footer-toolbar").click();
});
});
});

+ 23
- 0
cypress/integration/grid_configuration.js Zobrazit soubor

@@ -0,0 +1,23 @@
context("Grid Configuration", () => {
beforeEach(() => {
cy.login();
cy.visit("/app/doctype/User");
});
it("Set user wise grid settings", () => {
cy.wait(100);
cy.get('.xhiveframework-control[data-fieldname="fields"]').as("table");
cy.get("@table").find(".icon-sm").click();
cy.wait(100);
cy.get('.xhiveframework-control[data-fieldname="fields_html"]').as("modal");
cy.get("@modal").find(".add-new-fields").click();
cy.wait(100);
cy.get('[type="checkbox"][data-unit="read_only"]').check();
cy.findByRole("button", { name: "Add" }).click();
cy.wait(100);
cy.get('[data-fieldname="options"]').invoke("attr", "value", "1");
cy.get('.form-control.column-width[data-fieldname="options"]').trigger("change");
cy.findByRole("button", { name: "Update" }).click();
cy.wait(200);
cy.get('[title="Read Only"').should("be.visible");
});
});

+ 47
- 0
cypress/integration/grid_keyboard_shortcut.js Zobrazit soubor

@@ -0,0 +1,47 @@
context("Grid Keyboard Shortcut", () => {
let total_count = 0;
before(() => {
cy.login();
});
beforeEach(() => {
cy.reload();
cy.visit("/app/contact/new-contact-1");
cy.get('.xhiveframework-control[data-fieldname="email_ids"]').find(".grid-add-row").click();
});
it("Insert new row at the end", () => {
cy.add_new_row_in_grid(
"{ctrl}{shift}{downarrow}",
(cy, total_count) => {
cy.get('[data-name="new-contact-email-1"]').should(
"have.attr",
"data-idx",
`${total_count + 1}`
);
},
total_count
);
});
it("Insert new row at the top", () => {
cy.add_new_row_in_grid("{ctrl}{shift}{uparrow}", (cy) => {
cy.get('[data-name="new-contact-email-1"]').should("have.attr", "data-idx", "2");
});
});
it("Insert new row below", () => {
cy.add_new_row_in_grid("{ctrl}{downarrow}", (cy) => {
cy.get('[data-name="new-contact-email-1"]').should("have.attr", "data-idx", "1");
});
});
it("Insert new row above", () => {
cy.add_new_row_in_grid("{ctrl}{uparrow}", (cy) => {
cy.get('[data-name="new-contact-email-1"]').should("have.attr", "data-idx", "2");
});
});
});

Cypress.Commands.add("add_new_row_in_grid", (shortcut_keys, callbackFn, total_count) => {
cy.get('.xhiveframework-control[data-fieldname="email_ids"]').as("table");
cy.get("@table").find('.grid-body [data-fieldname="email_id"]').first().click();
cy.get("@table").find('.grid-body [data-fieldname="email_id"]').first().type(shortcut_keys);

callbackFn(cy, total_count);
});

+ 80
- 0
cypress/integration/grid_pagination.js Zobrazit soubor

@@ -0,0 +1,80 @@
context("Grid Pagination", () => {
beforeEach(() => {
cy.login();
cy.visit("/app/website");
});
before(() => {
cy.login();
cy.visit("/app/website");
return cy
.window()
.its("xhiveframework")
.then((xhiveframework) => {
return xhiveframework.call(
"xhiveframework.tests.ui_test_helpers.create_contact_phone_nos_records"
);
});
});
it("creates pages for child table", () => {
cy.visit("/app/contact/Test Contact");
cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table");
cy.get("@table").find(".current-page-number").should("have.value", "1");
cy.get("@table").find(".total-page-number").should("contain", "20");
cy.get("@table").find(".grid-body .grid-row").should("have.length", 50);
});
it("goes to the next and previous page", () => {
cy.visit("/app/contact/Test Contact");
cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table");
cy.get("@table").find(".next-page").click();
cy.get("@table").find(".current-page-number").should("have.value", "2");
cy.get("@table")
.find(".grid-body .grid-row")
.first()
.should("have.attr", "data-idx", "51");
cy.get("@table").find(".prev-page").click();
cy.get("@table").find(".current-page-number").should("have.value", "1");
cy.get("@table").find(".grid-body .grid-row").first().should("have.attr", "data-idx", "1");
});
it("adds and deletes rows and changes page", () => {
cy.visit("/app/contact/Test Contact");
cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table");
cy.get("@table").findByRole("button", { name: "Add Row" }).click();
cy.get("@table").find(".grid-body .row-index").should("contain", 1001);
cy.get("@table").find(".current-page-number").should("have.value", "21");
cy.get("@table").find(".total-page-number").should("contain", "21");
cy.get("@table").find(".grid-body .grid-row .grid-row-check").click({ force: true });
cy.get("@table").findByRole("button", { name: "Delete" }).click();
cy.get("@table").find(".grid-body .row-index").last().should("contain", 1000);
cy.get("@table").find(".current-page-number").should("have.value", "20");
cy.get("@table").find(".total-page-number").should("contain", "20");
});
it("go to specific page, use up and down arrow, type characters, 0 page and more than existing page", () => {
cy.visit("/app/contact/Test Contact");
cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table");
cy.get("@table").find(".current-page-number").focus().clear().type("17").blur();
cy.get("@table").find(".grid-body .row-index").should("contain", 801);

cy.get("@table").find(".current-page-number").focus().type("{uparrow}{uparrow}");
cy.get("@table").find(".current-page-number").should("have.value", "19");

cy.get("@table").find(".current-page-number").focus().type("{downarrow}{downarrow}");
cy.get("@table").find(".current-page-number").should("have.value", "17");

cy.get("@table").find(".current-page-number").focus().clear().type("700").blur();
cy.get("@table").find(".current-page-number").should("have.value", "20");

cy.get("@table").find(".current-page-number").focus().clear().type("0").blur();
cy.get("@table").find(".current-page-number").should("have.value", "1");

cy.get("@table").find(".current-page-number").focus().clear().type("abc").blur();
cy.get("@table").find(".current-page-number").should("have.value", "1");
});
// it('deletes all rows', ()=> {
// cy.visit('/app/contact/Test Contact');
// cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as('table');
// cy.get('@table').find('.grid-heading-row .grid-row-check').click({force: true});
// cy.get('@table').find('button.grid-remove-all-rows').click();
// cy.get('.modal-dialog .btn-primary').contains('Yes').click();
// cy.get('@table').find('.grid-body .grid-row').should('have.length', 0);
// });
});

+ 133
- 0
cypress/integration/grid_search.js Zobrazit soubor

@@ -0,0 +1,133 @@
import doctype_with_child_table from "../fixtures/doctype_with_child_table";
import child_table_doctype from "../fixtures/child_table_doctype";
import child_table_doctype_1 from "../fixtures/child_table_doctype_1";
const doctype_with_child_table_name = doctype_with_child_table.name;

context("Grid Search", () => {
before(() => {
cy.visit("/login");
cy.login();
cy.visit("/app/website");
cy.insert_doc("DocType", child_table_doctype, true);
cy.insert_doc("DocType", child_table_doctype_1, true);
cy.insert_doc("DocType", doctype_with_child_table, true);
return cy
.window()
.its("xhiveframework")
.then((xhiveframework) => {
return xhiveframework.xcall(
"xhiveframework.tests.ui_test_helpers.insert_doctype_with_child_table_record",
{
name: doctype_with_child_table_name,
}
);
});
});

it("Test search row visibility", () => {
cy.window()
.its("xhiveframework")
.then((xhiveframework) => {
xhiveframework.model.user_settings.save("Doctype With Child Table", "GridView", {
"Child Table Doctype 1": [
{ fieldname: "data", columns: 2 },
{ fieldname: "barcode", columns: 1 },
{ fieldname: "check", columns: 1 },
{ fieldname: "rating", columns: 2 },
{ fieldname: "duration", columns: 2 },
{ fieldname: "date", columns: 2 },
],
});
});

cy.visit(`/app/doctype-with-child-table/Test Grid Search`);

cy.get('.xhiveframework-control[data-fieldname="child_table_1"]').as("table");
cy.get("@table").find(".grid-row-check:last").click();
cy.get("@table").find(".grid-footer").contains("Delete").click();
cy.get(".grid-heading-row .grid-row .search").should("not.exist");
});

it("test search field for different fieldtypes", () => {
cy.visit(`/app/doctype-with-child-table/Test Grid Search`);

cy.get('.xhiveframework-control[data-fieldname="child_table_1"]').as("table");

// Index Column
cy.get("@table").find(".grid-heading-row .row-index.search input").type("3");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 2);
cy.get("@table").find(".grid-heading-row .row-index.search input").clear();

// Data Column
cy.get("@table")
.find('.grid-heading-row .search input[data-fieldtype="Data"]')
.type("Data");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 1);
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Data"]').clear();

// Barcode Column
cy.get("@table")
.find('.grid-heading-row .search input[data-fieldtype="Barcode"]')
.type("092");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 4);
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Barcode"]').clear();

// Check Column
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').type("1");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 9);
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').clear();

cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').type("0");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 11);
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').clear();

// Rating Column
cy.get("@table")
.find('.grid-heading-row .search input[data-fieldtype="Rating"]')
.type("3");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 3);
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Rating"]').clear();

// Duration Column
cy.get("@table")
.find('.grid-heading-row .search input[data-fieldtype="Duration"]')
.type("3d");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 3);
cy.get("@table")
.find('.grid-heading-row .search input[data-fieldtype="Duration"]')
.clear();

// Date Column
cy.get("@table")
.find('.grid-heading-row .search input[data-fieldtype="Date"]')
.type("2022");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 4);
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Date"]').clear();
});

it("test with multiple filter", () => {
cy.get('.xhiveframework-control[data-fieldname="child_table_1"]').as("table");

// Data Column
cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Data"]').type("a");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 10);

// Barcode Column
cy.get("@table")
.find('.grid-heading-row .search input[data-fieldtype="Barcode"]')
.type("0");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 8);

// Duration Column
cy.get("@table")
.find('.grid-heading-row .search input[data-fieldtype="Duration"]')
.type("d");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 5);

// Date Column
cy.get("@table")
.find('.grid-heading-row .search input[data-fieldtype="Date"]')
.type("02-");
cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 2);
});
});

+ 99
- 0
cypress/integration/kanban.js Zobrazit soubor

@@ -0,0 +1,99 @@
context("Kanban Board", () => {
before(() => {
cy.login();
cy.visit("/app");
});

it("Create ToDo Kanban", () => {
cy.visit("/app/todo");

cy.get(".page-actions .custom-btn-group button").click();
cy.get(".page-actions .custom-btn-group ul.dropdown-menu li").contains("Kanban").click();

cy.focused().blur();
cy.fill_field("board_name", "ToDo Kanban", "Data");
cy.fill_field("field_name", "Status", "Select");
cy.click_modal_primary_button("Save");

cy.get(".title-text").should("contain", "ToDo Kanban");
});

it("Create ToDo from kanban", () => {
cy.intercept({
method: "POST",
url: "api/method/xhiveframework.client.save",
}).as("save-todo");

cy.click_listview_primary_button("Add ToDo");

cy.fill_field("description", "Test Kanban ToDo", "Text Editor").wait(300);
cy.get(".modal-footer .btn-primary").last().click();

cy.wait("@save-todo");
});

it("Add and Remove fields", () => {
cy.visit("/app/todo/view/kanban/ToDo Kanban");

cy.intercept(
"POST",
"/api/method/xhiveframework.desk.doctype.kanban_board.kanban_board.save_settings"
).as("save-kanban");
cy.intercept(
"POST",
"/api/method/xhiveframework.desk.doctype.kanban_board.kanban_board.update_order"
).as("update-order");

cy.get(".page-actions .menu-btn-group > .btn").click();
cy.get(".page-actions .menu-btn-group .dropdown-menu li")
.contains("Kanban Settings")
.click();
cy.get(".add-new-fields").click();

cy.get(".checkbox-options .checkbox").contains("ID").click();
cy.get(".checkbox-options .checkbox").contains("Status").first().click();
cy.get(".checkbox-options .checkbox").contains("Priority").click();

cy.get(".modal-footer .btn-primary").last().click();

cy.get(".xhiveframework-control .label-area").contains("Show Labels").click();
cy.click_modal_primary_button("Save");

cy.wait("@save-kanban");

cy.get('.kanban-column[data-column-value="Open"] .kanban-cards').as("open-cards");
cy.get("@open-cards")
.find(".kanban-card .kanban-card-doc")
.first()
.should("contain", "ID:");
cy.get("@open-cards")
.find(".kanban-card .kanban-card-doc")
.first()
.should("contain", "Status:");
cy.get("@open-cards")
.find(".kanban-card .kanban-card-doc")
.first()
.should("contain", "Priority:");

cy.get(".page-actions .menu-btn-group > .btn").click();
cy.get(".page-actions .menu-btn-group .dropdown-menu li")
.contains("Kanban Settings")
.click();
cy.get_open_dialog()
.find(
'.xhiveframework-control[data-fieldname="fields_html"] div[data-label="ID"] .remove-field'
)
.click();

cy.wait("@update-order");
cy.get_open_dialog().find(".xhiveframework-control .label-area").contains("Show Labels").click();
cy.get(".modal-footer .btn-primary").last().click();

cy.wait("@save-kanban");

cy.get("@open-cards")
.find(".kanban-card .kanban-card-doc")
.first()
.should("not.contain", "ID:");
});
});

+ 42
- 0
cypress/integration/list_paging.js Zobrazit soubor

@@ -0,0 +1,42 @@
context("List Paging", () => {
before(() => {
cy.login();
cy.visit("/app/website");
return cy
.window()
.its("xhiveframework")
.then((xhiveframework) => {
return xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_multiple_todo_records");
});
});

it("test load more with count selection buttons", () => {
cy.visit("/app/todo/view/report");
cy.clear_filters();

cy.get(".list-paging-area .list-count").should("contain.text", "20 of");
cy.get(".list-paging-area .btn-more").click();
cy.get(".list-paging-area .list-count").should("contain.text", "40 of");
cy.get(".list-paging-area .btn-more").click();
cy.get(".list-paging-area .list-count").should("contain.text", "60 of");

cy.get('.list-paging-area .btn-group .btn-paging[data-value="100"]').click();

cy.get(".list-paging-area .list-count").should("contain.text", "100 of");
cy.get(".list-paging-area .btn-more").click();
cy.get(".list-paging-area .list-count").should("contain.text", "200 of");
cy.get(".list-paging-area .btn-more").click();
cy.get(".list-paging-area .list-count").should("contain.text", "300 of");

// check if refresh works after load more
cy.get('.page-head .standard-actions [data-original-title="Refresh"]').click();
cy.get(".list-paging-area .list-count").should("contain.text", "300 of");

cy.get('.list-paging-area .btn-group .btn-paging[data-value="500"]').click();

cy.get(".list-paging-area .list-count").should("contain.text", "500 of");
cy.get(".list-paging-area .btn-more").click();

cy.get(".list-paging-area .list-count").should("contain.text", "1000 of");
});
});

+ 70
- 0
cypress/integration/list_view.js Zobrazit soubor

@@ -0,0 +1,70 @@
context("List View", () => {
before(() => {
cy.login();
cy.visit("/app/website");
return cy
.window()
.its("xhiveframework")
.then((xhiveframework) => {
return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.setup_workflow");
});
});

it("Keep checkbox checked after Refresh", { scrollBehavior: false }, () => {
cy.go_to_list("ToDo");
cy.clear_filters();
cy.get(".list-row-container .list-row-checkbox").click({
multiple: true,
force: true,
});
cy.get(".actions-btn-group button").contains("Actions").should("be.visible");
cy.intercept("/api/method/xhiveframework.desk.reportview.get").as("list-refresh");
cy.wait(3000); // wait before you hit another refresh
cy.get('button[data-original-title="Refresh"]').click();
cy.wait("@list-refresh");
cy.get(".list-row-container .list-row-checkbox:checked").should("be.visible");
});

it('enables "Actions" button', { scrollBehavior: false }, () => {
const actions = [
"Approve",
"Reject",
"Edit",
"Export",
"Assign To",
"Apply Assignment Rule",
"Add Tags",
"Print",
"Delete",
];
cy.go_to_list("ToDo");
cy.clear_filters();
cy.get('.list-row-container:contains("Pending") .list-row-checkbox').click({
multiple: true,
force: true,
});
cy.get(".actions-btn-group button").contains("Actions").should("be.visible").click();
cy.get(".dropdown-menu li:visible .dropdown-item")
.should("have.length", 9)
.each((el, index) => {
cy.wrap(el).contains(actions[index]);
})
.then((elements) => {
cy.intercept({
method: "POST",
url: "api/method/xhiveframework.model.workflow.bulk_workflow_approval",
}).as("bulk-approval");
cy.intercept({
method: "POST",
url: "api/method/xhiveframework.desk.reportview.get",
}).as("real-time-update");
cy.wrap(elements).contains("Approve").click();
cy.wait(["@bulk-approval", "@real-time-update"]);
cy.wait(300);
cy.get_open_dialog().find(".btn-modal-close").click();
cy.reload();
cy.clear_filters();
cy.get(".list-row-container:visible").should("contain", "Approved");
});
});
});

+ 38
- 0
cypress/integration/list_view_settings.js Zobrazit soubor

@@ -0,0 +1,38 @@
context("List View Settings", () => {
beforeEach(() => {
cy.login();
cy.visit("/app/website");
});
it("Default settings", () => {
cy.visit("/app/List/DocType/List");
cy.clear_filters();
cy.get(".list-count").should("contain", "20 of");
cy.get(".list-stats").should("contain", "Tags");
});
it("disable count and sidebar stats then verify", () => {
cy.wait(300);
cy.visit("/app/List/DocType/List");
cy.clear_filters();
cy.wait(300);
cy.get(".list-count").should("contain", "20 of");
cy.get(".menu-btn-group button").click();
cy.get(".dropdown-menu li").filter(":visible").contains("List Settings").click();
cy.get(".modal-dialog").should("contain", "DocType Settings");

cy.findByLabelText("Disable Count").check({ force: true });
cy.findByLabelText("Disable Sidebar Stats").check({ force: true });
cy.findByRole("button", { name: "Save" }).click();

cy.reload({ force: true });

cy.get(".list-count").should("be.empty");
cy.get(".list-sidebar .list-tags").should("not.exist");

cy.get(".menu-btn-group button").click({ force: true });
cy.get(".dropdown-menu li").filter(":visible").contains("List Settings").click();
cy.get(".modal-dialog").should("contain", "DocType Settings");
cy.findByLabelText("Disable Count").uncheck({ force: true });
cy.findByLabelText("Disable Sidebar Stats").uncheck({ force: true });
cy.findByRole("button", { name: "Save" }).click();
});
});

Některé soubory nejsou zobrazny, neboť je v této revizi změněno mnoho souborů

Načítá se…
Zrušit
Uložit