Browse Source

Merge branch 'develop' into compare-columns-db_query

version-14
Gavin D'souza 3 years ago
parent
commit
9f4f096bf1
100 changed files with 698 additions and 275 deletions
  1. +1
    -1
      .editorconfig
  2. +3
    -0
      .git-blame-ignore-revs
  3. +3
    -3
      .github/helper/documentation.py
  4. +0
    -5
      .github/helper/install_dependencies.sh
  5. +56
    -40
      .github/helper/roulette.py
  6. +22
    -0
      .github/workflows/patch-mariadb-tests.yml
  7. +30
    -31
      .github/workflows/server-mariadb-tests.yml
  8. +33
    -1
      .github/workflows/server-postgres-tests.yml
  9. +24
    -0
      .github/workflows/ui-tests.yml
  10. +16
    -0
      .mergify.yml
  11. +4
    -1
      CODEOWNERS
  12. +1
    -1
      LICENSE
  13. +2
    -2
      README.md
  14. +9
    -0
      codecov.yml
  15. +7
    -7
      cypress/integration/awesome_bar.js
  16. +3
    -3
      cypress/integration/control_barcode.js
  17. +5
    -5
      cypress/integration/control_icon.js
  18. +2
    -2
      cypress/integration/control_link.js
  19. +2
    -0
      cypress/integration/control_select.js
  20. +63
    -0
      cypress/integration/dashboard_links.js
  21. +19
    -0
      cypress/integration/datetime_field_form_validation.js
  22. +3
    -3
      cypress/integration/depends_on.js
  23. +15
    -14
      cypress/integration/file_uploader.js
  24. +79
    -0
      cypress/integration/folder_navigation.js
  25. +1
    -1
      cypress/integration/form.js
  26. +12
    -12
      cypress/integration/form_tour.js
  27. +2
    -2
      cypress/integration/grid_pagination.js
  28. +6
    -6
      cypress/integration/list_view_settings.js
  29. +6
    -6
      cypress/integration/login.js
  30. +8
    -8
      cypress/integration/recorder.js
  31. +1
    -1
      cypress/integration/report_view.js
  32. +20
    -20
      cypress/integration/timeline.js
  33. +9
    -10
      cypress/integration/timeline_email.js
  34. +90
    -0
      cypress/integration/workspace.js
  35. +1
    -0
      cypress/support/commands.js
  36. +5
    -1
      frappe/__init__.py
  37. +1
    -1
      frappe/api.py
  38. +1
    -1
      frappe/app.py
  39. +1
    -1
      frappe/automation/doctype/assignment_rule/assignment_rule.py
  40. +5
    -5
      frappe/automation/doctype/assignment_rule/test_assignment_rule.py
  41. +1
    -1
      frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py
  42. +1
    -1
      frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py
  43. +1
    -1
      frappe/automation/doctype/auto_repeat/auto_repeat.js
  44. +1
    -1
      frappe/automation/doctype/auto_repeat/auto_repeat.py
  45. +1
    -1
      frappe/automation/doctype/auto_repeat/test_auto_repeat.py
  46. +1
    -1
      frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py
  47. +1
    -1
      frappe/automation/doctype/milestone/milestone.py
  48. +1
    -1
      frappe/automation/doctype/milestone/test_milestone.py
  49. +1
    -1
      frappe/automation/doctype/milestone_tracker/milestone_tracker.py
  50. +3
    -3
      frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py
  51. +34
    -4
      frappe/automation/workspace/tools/tools.json
  52. +3
    -3
      frappe/boot.py
  53. +1
    -1
      frappe/build.py
  54. +1
    -1
      frappe/cache_manager.py
  55. +1
    -1
      frappe/chat/doctype/chat_token/chat_token.py
  56. +1
    -1
      frappe/client.py
  57. +1
    -1
      frappe/commands/__init__.py
  58. +25
    -10
      frappe/commands/utils.py
  59. +2
    -2
      frappe/contacts/address_and_contact.py
  60. +1
    -1
      frappe/contacts/doctype/address/address.py
  61. +1
    -1
      frappe/contacts/doctype/address/test_address.py
  62. +1
    -1
      frappe/contacts/doctype/address_template/address_template.py
  63. +1
    -1
      frappe/contacts/doctype/address_template/test_address_template.py
  64. +2
    -2
      frappe/contacts/doctype/contact/contact.py
  65. +1
    -1
      frappe/contacts/doctype/contact/test_contact.py
  66. +1
    -1
      frappe/contacts/doctype/contact_email/contact_email.py
  67. +1
    -1
      frappe/contacts/doctype/contact_phone/contact_phone.py
  68. +1
    -1
      frappe/contacts/doctype/gender/gender.py
  69. +1
    -1
      frappe/contacts/doctype/gender/test_gender.py
  70. +1
    -1
      frappe/contacts/doctype/salutation/salutation.py
  71. +1
    -1
      frappe/contacts/doctype/salutation/test_salutation.py
  72. +1
    -1
      frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py
  73. +1
    -1
      frappe/core/__init__.py
  74. +1
    -1
      frappe/core/doctype/__init__.py
  75. +3
    -2
      frappe/core/doctype/access_log/access_log.py
  76. +1
    -1
      frappe/core/doctype/access_log/test_access_log.py
  77. +1
    -1
      frappe/core/doctype/activity_log/activity_log.py
  78. +1
    -1
      frappe/core/doctype/activity_log/feed.py
  79. +1
    -1
      frappe/core/doctype/activity_log/test_activity_log.py
  80. +1
    -1
      frappe/core/doctype/block_module/block_module.py
  81. +1
    -1
      frappe/core/doctype/comment/comment.py
  82. +3
    -3
      frappe/core/doctype/comment/test_comment.py
  83. +1
    -1
      frappe/core/doctype/communication/__init__.py
  84. +1
    -1
      frappe/core/doctype/communication/communication.py
  85. +1
    -1
      frappe/core/doctype/communication/email.py
  86. +1
    -1
      frappe/core/doctype/communication/test_communication.py
  87. +1
    -1
      frappe/core/doctype/communication_link/communication_link.py
  88. +1
    -1
      frappe/core/doctype/custom_docperm/custom_docperm.py
  89. +1
    -1
      frappe/core/doctype/custom_docperm/test_custom_docperm.py
  90. +1
    -1
      frappe/core/doctype/custom_role/custom_role.py
  91. +1
    -1
      frappe/core/doctype/custom_role/test_custom_role.py
  92. +1
    -1
      frappe/core/doctype/data_export/data_export.py
  93. +1
    -1
      frappe/core/doctype/data_export/exporter.py
  94. +1
    -1
      frappe/core/doctype/data_import/data_import.py
  95. +1
    -1
      frappe/core/doctype/data_import/exporter.py
  96. +1
    -1
      frappe/core/doctype/data_import/importer.py
  97. +1
    -1
      frappe/core/doctype/data_import/test_data_import.py
  98. +1
    -1
      frappe/core/doctype/data_import/test_exporter.py
  99. +1
    -1
      frappe/core/doctype/data_import/test_importer.py
  100. +1
    -1
      frappe/core/doctype/defaultvalue/__init__.py

+ 1
- 1
.editorconfig View File

@@ -9,6 +9,6 @@ trim_trailing_whitespace = true
charset = utf-8 charset = utf-8


# python, js indentation settings # python, js indentation settings
[{*.py,*.js}]
[{*.py,*.js,*.vue}]
indent_style = tab indent_style = tab
indent_size = 4 indent_size = 4

+ 3
- 0
.git-blame-ignore-revs View File

@@ -10,3 +10,6 @@


# Replace use of Class.extend with native JS class # Replace use of Class.extend with native JS class
fe20515c23a3ac41f1092bf0eaf0a0a452ec2e85 fe20515c23a3ac41f1092bf0eaf0a0a452ec2e85

# Updating license headers
34460265554242a8d05fb09f049033b1117e1a2b

+ 3
- 3
.github/helper/documentation.py View File

@@ -32,9 +32,9 @@ if __name__ == "__main__":


if response.ok: if response.ok:
payload = response.json() payload = response.json()
title = payload.get("title", "").lower()
head_sha = payload.get("head", {}).get("sha")
body = payload.get("body", "").lower()
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 title.startswith("feat") and head_sha and "no-docs" not in body:
if docs_link_exists(body): if docs_link_exists(body):


+ 0
- 5
.github/helper/install_dependencies.sh View File

@@ -2,11 +2,6 @@


set -e set -e


# python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
# if [[ $? != 2 ]];then
# exit;
# fi

# install wkhtmltopdf # install wkhtmltopdf
wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
tar -xf /tmp/wkhtmltox.tar.xz -C /tmp tar -xf /tmp/wkhtmltox.tar.xz -C /tmp


+ 56
- 40
.github/helper/roulette.py View File

@@ -1,56 +1,72 @@
# if the script ends with exit code 0, then no tests are run further, else all tests are run
import json
import os import os
import re import re
import shlex import shlex
import subprocess import subprocess
import sys import sys
import urllib.request




def get_files_list(pr_number, repo="frappe/frappe"):
req = urllib.request.Request(f"https://api.github.com/repos/{repo}/pulls/{pr_number}/files")
res = urllib.request.urlopen(req)
dump = json.loads(res.read().decode('utf8'))
return [change["filename"] for change in dump]

def get_output(command, shell=True): def get_output(command, shell=True):
print(command)
command = shlex.split(command)
return subprocess.check_output(command, shell=shell, encoding="utf8").strip()
print(command)
command = shlex.split(command)
return subprocess.check_output(command, shell=shell, encoding="utf8").strip()


def is_py(file): def is_py(file):
return file.endswith("py")
return file.endswith("py")

def is_ci(file):
return ".github" in file


def is_js(file):
return file.endswith("js")
def is_frontend_code(file):
return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue"))


def is_docs(file): def is_docs(file):
regex = re.compile(r'\.(md|png|jpg|jpeg)$|^.github|LICENSE')
return bool(regex.search(file))
regex = re.compile(r'\.(md|png|jpg|jpeg|csv)$|^.github|LICENSE')
return bool(regex.search(file))




if __name__ == "__main__": if __name__ == "__main__":
build_type = os.environ.get("TYPE")
before = os.environ.get("BEFORE")
after = os.environ.get("AFTER")
commit_range = before + '...' + after
print("Build Type: {}".format(build_type))
print("Commit Range: {}".format(commit_range))

try:
files_changed = get_output("git diff --name-only {}".format(commit_range), shell=False)
except Exception:
sys.exit(2)

if "fatal" not in files_changed:
files_list = files_changed.split()
only_docs_changed = len(list(filter(is_docs, files_list))) == len(files_list)
only_js_changed = len(list(filter(is_js, files_list))) == len(files_list)
only_py_changed = len(list(filter(is_py, files_list))) == len(files_list)

if only_docs_changed:
print("Only docs were updated, stopping build process.")
sys.exit(0)

if only_js_changed and build_type == "server":
print("Only JavaScript code was updated; Stopping Python build process.")
sys.exit(0)

if only_py_changed and build_type == "ui":
print("Only Python code was updated, stopping Cypress build process.")
sys.exit(0)

sys.exit(2)
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"')
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)
only_py_changed = len(list(filter(is_py, files_list))) == len(files_list)

if 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":
print("Only Frontend code was updated; Stopping Python build process.")
sys.exit(0)

elif only_py_changed and build_type == "ui":
print("Only Python code was updated, stopping Cypress build process.")
sys.exit(0)

os.system('echo "::set-output name=build::strawberry"')

+ 22
- 0
.github/workflows/patch-mariadb-tests.yml View File

@@ -2,6 +2,11 @@ name: Patch


on: [pull_request, workflow_dispatch] on: [pull_request, workflow_dispatch]



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

jobs: jobs:
test: test:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
@@ -26,10 +31,21 @@ jobs:
with: with:
python-version: 3.7 python-version: 3.7


- name: Check if build should be run
id: check-build
run: |
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
env:
TYPE: "server"
PR_NUMBER: ${{ github.event.number }}
REPO_NAME: ${{ github.repository }}

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


- name: Cache pip - name: Cache pip
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ~/.cache/pip path: ~/.cache/pip
@@ -39,6 +55,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-


- name: Cache node modules - name: Cache node modules
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v2 uses: actions/cache@v2
env: env:
cache-name: cache-node-modules cache-name: cache-node-modules
@@ -51,10 +68,12 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-


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


- uses: actions/cache@v2 - uses: actions/cache@v2
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache id: yarn-cache
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -63,6 +82,7 @@ jobs:
${{ runner.os }}-yarn- ${{ runner.os }}-yarn-


- name: Install Dependencies - name: Install Dependencies
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
env: env:
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
@@ -70,12 +90,14 @@ jobs:
TYPE: server TYPE: server


- name: Install - name: Install
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env: env:
DB: mariadb DB: mariadb
TYPE: server TYPE: server


- name: Run Patch Tests - name: Run Patch Tests
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: | run: |
cd ~/frappe-bench/ cd ~/frappe-bench/
wget https://frappeframework.com/files/v10-frappe.sql.gz wget https://frappeframework.com/files/v10-frappe.sql.gz


+ 30
- 31
.github/workflows/server-mariadb-tests.yml View File

@@ -6,6 +6,11 @@ on:
push: push:
branches: [ develop ] branches: [ develop ]


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


jobs: jobs:
test: test:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
@@ -35,17 +40,29 @@ jobs:
with: with:
python-version: 3.7 python-version: 3.7


- name: Check if build should be run
id: check-build
run: |
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
env:
TYPE: "server"
PR_NUMBER: ${{ github.event.number }}
REPO_NAME: ${{ github.repository }}

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


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


- name: Cache pip - name: Cache pip
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ~/.cache/pip path: ~/.cache/pip
@@ -55,6 +72,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-


- name: Cache node modules - name: Cache node modules
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v2 uses: actions/cache@v2
env: env:
cache-name: cache-node-modules cache-name: cache-node-modules
@@ -67,10 +85,12 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-


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


- uses: actions/cache@v2 - uses: actions/cache@v2
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache id: yarn-cache
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -79,6 +99,7 @@ jobs:
${{ runner.os }}-yarn- ${{ runner.os }}-yarn-


- name: Install Dependencies - name: Install Dependencies
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
env: env:
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
@@ -86,45 +107,23 @@ jobs:
TYPE: server TYPE: server


- name: Install - name: Install
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env: env:
DB: mariadb DB: mariadb
TYPE: server TYPE: server


- name: Run Tests - name: Run Tests
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage
env: env:
CI_BUILD_ID: ${{ github.run_id }} CI_BUILD_ID: ${{ github.run_id }}
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io ORCHESTRATOR_URL: http://test-orchestrator.frappe.io


- name: Upload Coverage Data
run: |
cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE}
cd ${GITHUB_WORKSPACE}
pip3 install coverage==5.5
pip3 install coveralls==3.0.1
coveralls
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
COVERALLS_FLAG_NAME: run-${{ matrix.container }}
COVERALLS_SERVICE_NAME: ${{ github.event_name == 'pull_request' && 'github' || 'github-actions' }}
COVERALLS_PARALLEL: true

coveralls:
name: Coverage Wrap Up
needs: test
container: python:3-slim
runs-on: ubuntu-18.04
steps:
- name: Clone
uses: actions/checkout@v2

- name: Coveralls Finished
run: |
cd ${GITHUB_WORKSPACE}
pip3 install coverage==5.5
pip3 install coveralls==3.0.1
coveralls --finish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload coverage data
uses: codecov/codecov-action@v2
with:
name: MariaDB
fail_ci_if_error: true
files: /home/runner/frappe-bench/sites/coverage.xml
verbose: true

+ 33
- 1
.github/workflows/server-postgres-tests.yml View File

@@ -3,6 +3,12 @@ name: Server
on: on:
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
push:
branches: [ develop ]

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


jobs: jobs:
test: test:
@@ -37,17 +43,29 @@ jobs:
with: with:
python-version: 3.7 python-version: 3.7


- name: Check if build should be run
id: check-build
run: |
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
env:
TYPE: "server"
PR_NUMBER: ${{ github.event.number }}
REPO_NAME: ${{ github.repository }}

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


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


- name: Cache pip - name: Cache pip
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ~/.cache/pip path: ~/.cache/pip
@@ -57,6 +75,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-


- name: Cache node modules - name: Cache node modules
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v2 uses: actions/cache@v2
env: env:
cache-name: cache-node-modules cache-name: cache-node-modules
@@ -69,10 +88,12 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-


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


- uses: actions/cache@v2 - uses: actions/cache@v2
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache id: yarn-cache
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -81,6 +102,7 @@ jobs:
${{ runner.os }}-yarn- ${{ runner.os }}-yarn-


- name: Install Dependencies - name: Install Dependencies
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
env: env:
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
@@ -88,13 +110,23 @@ jobs:
TYPE: server TYPE: server


- name: Install - name: Install
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env: env:
DB: postgres DB: postgres
TYPE: server TYPE: server


- name: Run Tests - name: Run Tests
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage
env: env:
CI_BUILD_ID: ${{ github.run_id }} CI_BUILD_ID: ${{ github.run_id }}
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io ORCHESTRATOR_URL: http://test-orchestrator.frappe.io

- name: Upload coverage data
uses: codecov/codecov-action@v2
with:
name: Postgres
fail_ci_if_error: true
files: /home/runner/frappe-bench/sites/coverage.xml
verbose: true

+ 24
- 0
.github/workflows/ui-tests.yml View File

@@ -6,6 +6,10 @@ on:
push: push:
branches: [ develop ] branches: [ develop ]


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

jobs: jobs:
test: test:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
@@ -35,17 +39,29 @@ jobs:
with: with:
python-version: 3.7 python-version: 3.7


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

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


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


- name: Cache pip - name: Cache pip
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ~/.cache/pip path: ~/.cache/pip
@@ -55,6 +71,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-


- name: Cache node modules - name: Cache node modules
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v2 uses: actions/cache@v2
env: env:
cache-name: cache-node-modules cache-name: cache-node-modules
@@ -67,10 +84,12 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-


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


- uses: actions/cache@v2 - uses: actions/cache@v2
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
id: yarn-cache id: yarn-cache
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -79,6 +98,7 @@ jobs:
${{ runner.os }}-yarn- ${{ runner.os }}-yarn-


- name: Cache cypress binary - name: Cache cypress binary
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ~/.cache path: ~/.cache
@@ -88,6 +108,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-


- name: Install Dependencies - name: Install Dependencies
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
env: env:
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
@@ -95,15 +116,18 @@ jobs:
TYPE: ui TYPE: ui


- name: Install - name: Install
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env: env:
DB: mariadb DB: mariadb
TYPE: ui TYPE: ui


- name: Site Setup - name: Site Setup
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/frappe-bench/ && bench --site test_site execute frappe.utils.install.complete_setup_wizard run: cd ~/frappe-bench/ && bench --site test_site execute frappe.utils.install.complete_setup_wizard


- name: UI Tests - name: UI Tests
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests frappe --headless --parallel --ci-build-id $GITHUB_RUN_ID run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests frappe --headless --parallel --ci-build-id $GITHUB_RUN_ID
env: env:
CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb

+ 16
- 0
.mergify.yml View File

@@ -1,4 +1,20 @@
pull_request_rules: pull_request_rules:
- name: Auto-close PRs on stable branch
conditions:
- and:
- and:
- author!=surajshetty3416
- author!=gavindsouza
- 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/frappe/erpnext/wiki/Pull-Request-Checklist#which-branch

- name: Automatic merge on CI success and review - name: Automatic merge on CI success and review
conditions: conditions:
- status-success=Sider - status-success=Sider


+ 4
- 1
CODEOWNERS View File

@@ -7,10 +7,13 @@
templates/ @surajshetty3416 templates/ @surajshetty3416
www/ @surajshetty3416 www/ @surajshetty3416
integrations/ @leela integrations/ @leela
patches/ @surajshetty3416
patches/ @surajshetty3416 @gavindsouza
email/ @leela email/ @leela
event_streaming/ @ruchamahabal event_streaming/ @ruchamahabal
data_import* @netchampfaris data_import* @netchampfaris
core/ @surajshetty3416 core/ @surajshetty3416
database @gavindsouza
model @gavindsouza
requirements.txt @gavindsouza requirements.txt @gavindsouza
commands/ @gavindsouza commands/ @gavindsouza
workspace @shariquerik

+ 1
- 1
LICENSE View File

@@ -1,6 +1,6 @@
The MIT License The MIT License


Copyright (c) 2016-2018 Frappe Technologies Pvt. Ltd. <developers@frappe.io>
Copyright (c) 2016-2021 Frappe Technologies Pvt. Ltd. <developers@frappe.io>


Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal


+ 2
- 2
README.md View File

@@ -26,8 +26,8 @@
<a href='https://www.codetriage.com/frappe/frappe'> <a href='https://www.codetriage.com/frappe/frappe'>
<img src='https://www.codetriage.com/frappe/frappe/badges/users.svg'> <img src='https://www.codetriage.com/frappe/frappe/badges/users.svg'>
</a> </a>
<a href='https://coveralls.io/github/frappe/frappe?branch=develop'>
<img src='https://coveralls.io/repos/github/frappe/frappe/badge.svg?branch=develop'>
<a href="https://codecov.io/gh/frappe/frappe">
<img src="https://codecov.io/gh/frappe/frappe/branch/develop/graph/badge.svg?token=XoTa679hIj"/>
</a> </a>
</div> </div>




+ 9
- 0
codecov.yml View File

@@ -0,0 +1,9 @@
codecov:
require_ci_to_pass: yes
status:
project:
default:
threshold: 0.5%
comment:
layout: "diff, flags, files"
require_changes: true

+ 7
- 7
cypress/integration/awesome_bar.js View File

@@ -10,9 +10,9 @@ context('Awesome Bar', () => {
}); });


it('navigates to doctype list', () => { it('navigates to doctype list', () => {
cy.get('#navbar-search').type('todo', { delay: 200 });
cy.get('#navbar-search + ul').should('be.visible');
cy.get('#navbar-search').type('{downarrow}{enter}', { delay: 100 });
cy.findByPlaceholderText('Search or type a command (Ctrl + G)').type('todo', { delay: 200 });
cy.get('.awesomplete').findByRole('listbox').should('be.visible');
cy.findByPlaceholderText('Search or type a command (Ctrl + G)').type('{downarrow}{enter}', { delay: 100 });


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


@@ -20,24 +20,24 @@ context('Awesome Bar', () => {
}); });


it('find text in doctype list', () => { it('find text in doctype list', () => {
cy.get('#navbar-search')
cy.findByPlaceholderText('Search or type a command (Ctrl + G)')
.type('test in todo{downarrow}{enter}', { delay: 200 }); .type('test in todo{downarrow}{enter}', { delay: 200 });


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


cy.get('[data-original-title="Name"] > .input-with-feedback')
cy.findByPlaceholderText('Name')
.should('have.value', '%test%'); .should('have.value', '%test%');
}); });


it('navigates to new form', () => { it('navigates to new form', () => {
cy.get('#navbar-search')
cy.findByPlaceholderText('Search or type a command (Ctrl + G)')
.type('new blog post{downarrow}{enter}', { delay: 200 }); .type('new blog post{downarrow}{enter}', { delay: 200 });


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


it('calculates math expressions', () => { it('calculates math expressions', () => {
cy.get('#navbar-search')
cy.findByPlaceholderText('Search or type a command (Ctrl + G)')
.type('55 + 32{downarrow}{enter}', { delay: 200 }); .type('55 + 32{downarrow}{enter}', { delay: 200 });


cy.get('.modal-title').should('contain', 'Result'); cy.get('.modal-title').should('contain', 'Result');


+ 3
- 3
cypress/integration/control_barcode.js View File

@@ -20,7 +20,7 @@ context('Control Barcode', () => {
it('should generate barcode on setting a value', () => { it('should generate barcode on setting a value', () => {
get_dialog_with_barcode().as('dialog'); get_dialog_with_barcode().as('dialog');


cy.get('.frappe-control[data-fieldname=barcode] input')
cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox')
.focus() .focus()
.type('123456789') .type('123456789')
.blur(); .blur();
@@ -37,11 +37,11 @@ context('Control Barcode', () => {
it('should reset when input is cleared', () => { it('should reset when input is cleared', () => {
get_dialog_with_barcode().as('dialog'); get_dialog_with_barcode().as('dialog');


cy.get('.frappe-control[data-fieldname=barcode] input')
cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox')
.focus() .focus()
.type('123456789') .type('123456789')
.blur(); .blur();
cy.get('.frappe-control[data-fieldname=barcode] input')
cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox')
.clear() .clear()
.blur(); .blur();
cy.get('.frappe-control[data-fieldname=barcode] svg[data-barcode-value="123456789"]') cy.get('.frappe-control[data-fieldname=barcode] svg[data-barcode-value="123456789"]')


+ 5
- 5
cypress/integration/control_icon.js View File

@@ -17,17 +17,17 @@ context('Control Icon', () => {


it('should set icon', () => { it('should set icon', () => {
get_dialog_with_icon().as('dialog'); get_dialog_with_icon().as('dialog');
cy.get('.frappe-control[data-fieldname=icon] input').first().click();
cy.get('.frappe-control[data-fieldname=icon]').findByRole('textbox').click();


cy.get('.icon-picker .icon-wrapper[id=active]').first().click(); cy.get('.icon-picker .icon-wrapper[id=active]').first().click();
cy.get('.frappe-control[data-fieldname=icon] input').first().should('have.value', 'active');
cy.get('.frappe-control[data-fieldname=icon]').findByRole('textbox').should('have.value', 'active');
cy.get('@dialog').then(dialog => { cy.get('@dialog').then(dialog => {
let value = dialog.get_value('icon'); let value = dialog.get_value('icon');
expect(value).to.equal('active'); expect(value).to.equal('active');
}); });


cy.get('.icon-picker .icon-wrapper[id=resting]').first().click(); cy.get('.icon-picker .icon-wrapper[id=resting]').first().click();
cy.get('.frappe-control[data-fieldname=icon] input').first().should('have.value', 'resting');
cy.get('.frappe-control[data-fieldname=icon]').findByRole('textbox').should('have.value', 'resting');
cy.get('@dialog').then(dialog => { cy.get('@dialog').then(dialog => {
let value = dialog.get_value('icon'); let value = dialog.get_value('icon');
expect(value).to.equal('resting'); expect(value).to.equal('resting');
@@ -36,14 +36,14 @@ context('Control Icon', () => {


it('search for icon and clear search input', () => { it('search for icon and clear search input', () => {
let search_text = 'ed'; let search_text = 'ed';
cy.get('.icon-picker input[type=search]').first().click().type(search_text);
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:not(.hidden)').then(i => {
cy.get(`.icon-section .icon-wrapper[id*='${search_text}']`).then(icons => { cy.get(`.icon-section .icon-wrapper[id*='${search_text}']`).then(icons => {
expect(i.length).to.equal(icons.length); expect(i.length).to.equal(icons.length);
}); });
}); });


cy.get('.icon-picker input[type=search]').clear().blur();
cy.get('.icon-picker').findByRole('searchbox').clear().blur();
cy.get('.icon-section .icon-wrapper').should('not.have.class', 'hidden'); cy.get('.icon-section .icon-wrapper').should('not.have.class', 'hidden');
}); });



+ 2
- 2
cypress/integration/control_link.js View File

@@ -35,7 +35,7 @@ context('Control Link', () => {
cy.wait('@search_link'); cy.wait('@search_link');
cy.get('@input').type('todo for link', { delay: 200 }); cy.get('@input').type('todo for link', { delay: 200 });
cy.wait('@search_link'); cy.wait('@search_link');
cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible');
cy.get('.frappe-control[data-fieldname=link]').findByRole('listbox').should('be.visible');
cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 }); cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 });
cy.get('.frappe-control[data-fieldname=link] input').blur(); cy.get('.frappe-control[data-fieldname=link] input').blur();
cy.get('@dialog').then(dialog => { cy.get('@dialog').then(dialog => {
@@ -71,7 +71,7 @@ context('Control Link', () => {
cy.get('@input').type(todos[0]).blur(); cy.get('@input').type(todos[0]).blur();
cy.wait('@validate_link'); cy.wait('@validate_link');
cy.get('@input').focus(); cy.get('@input').focus();
cy.get('.frappe-control[data-fieldname=link] .link-btn')
cy.findByTitle('Open Link')
.should('be.visible') .should('be.visible')
.click(); .click();
cy.location('pathname').should('eq', `/app/todo/${todos[0]}`); cy.location('pathname').should('eq', `/app/todo/${todos[0]}`);


+ 2
- 0
cypress/integration/control_select.js View File

@@ -24,8 +24,10 @@ context('Control Select', () => {
cy.get('@control').get('.select-icon').should('exist'); cy.get('@control').get('.select-icon').should('exist');
cy.get('@control').get('.placeholder').should('have.css', 'display', 'block'); cy.get('@control').get('.placeholder').should('have.css', 'display', 'block');
cy.get('@select').select('Option 1'); cy.get('@select').select('Option 1');
cy.findByDisplayValue('Option 1').should('exist');
cy.get('@control').get('.placeholder').should('have.css', 'display', 'none'); cy.get('@control').get('.placeholder').should('have.css', 'display', 'none');
cy.get('@select').invoke('val', ''); cy.get('@select').invoke('val', '');
cy.findByDisplayValue('Option 1').should('not.exist');
cy.get('@control').get('.placeholder').should('have.css', 'display', 'block'); cy.get('@control').get('.placeholder').should('have.css', 'display', 'block');






+ 63
- 0
cypress/integration/dashboard_links.js View File

@@ -0,0 +1,63 @@
context('Dashboard links', () => {
before(() => {
cy.visit('/login');
cy.login();
});

it('Adding a new contact, checking for the counter on the dashboard and deleting the created contact', () => {
cy.visit('/app/contact');
cy.clear_filters();

cy.visit('/app/user');
cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click();

//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('.btn[data-doctype="Contact"]').click();
cy.get('[data-doctype="Contact"][data-fieldname="first_name"]').type('Admin');
cy.findByRole('button', {name: 'Save'}).click();
cy.visit('/app/user');
cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click();

//To check if the counter for contact doc is "1" after adding the contact
cy.get('[data-doctype="Contact"] > .count').should('contain', '1');
cy.get('[data-doctype="Contact"]').contains('Contact').click();

//Deleting the newly created contact
cy.visit('/app/contact');
cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click();
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();
cy.get('[data-doctype="Contact"]').should('contain', 'Contact');
});

it('Report link in dashboard', () => {
cy.visit('/app/user');
cy.visit('/app/user/Administrator');
cy.get('[data-doctype="Contact"]').should('contain', 'Contact');
cy.findByText('Connections');
cy.window()
.its('cur_frm')
.then(cur_frm => {
cur_frm.dashboard.data.reports = [
{
'label': 'Reports',
'items': ['Permitted Documents For User']
}
];
cur_frm.dashboard.render_report_links();
cy.get('[data-report="Permitted Documents For User"]').contains('Permitted Documents For User').click();
cy.findByText('Permitted Documents For User');
cy.findByPlaceholderText('User').should("have.value", "Administrator");
});
});
});

+ 19
- 0
cypress/integration/datetime_field_form_validation.js View File

@@ -0,0 +1,19 @@
context('Datetime Field Validation', () => {
before(() => {
cy.login();
cy.visit('/app/communication');
cy.window().its('frappe').then(frappe => {
frappe.call("frappe.tests.ui_test_helpers.create_communication_records");
});
});

// validating datetime field value when value is set from backend and get validated on form load.
it('datetime field form validation', () => {
cy.visit('/app/communication');
cy.get('a[title="Test Form Communication 1"]').invoke('attr', 'data-name')
.then((name) => {
cy.visit(`/app/communication/${name}`);
cy.get('.indicator-pill').should('contain', 'Open').should('have.class', 'red');
});
});
});

+ 3
- 3
cypress/integration/depends_on.js View File

@@ -62,11 +62,11 @@ context('Depends On', () => {
it('should set the field as mandatory depending on other fields value', () => { it('should set the field as mandatory depending on other fields value', () => {
cy.new_form('Test Depends On'); cy.new_form('Test Depends On');
cy.fill_field('test_field', 'Some Value'); cy.fill_field('test_field', 'Some Value');
cy.get('button.primary-action').contains('Save').click();
cy.findByRole('button', {name: 'Save'}).click();
cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('be.visible'); cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('be.visible');
cy.hide_dialog(); cy.hide_dialog();
cy.fill_field('test_field', 'Random value'); cy.fill_field('test_field', 'Random value');
cy.get('button.primary-action').contains('Save').click();
cy.findByRole('button', {name: 'Save'}).click();
cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('not.be.visible'); 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', () => { it('should set the field as read only depending on other fields value', () => {
@@ -84,7 +84,7 @@ context('Depends On', () => {
cy.fill_field('dependant_field', 'Some Value'); cy.fill_field('dependant_field', 'Some Value');
//cy.fill_field('test_field', 'Some Other Value'); //cy.fill_field('test_field', 'Some Other Value');
cy.get('.frappe-control[data-fieldname="child_test_depends_on_field"]').as('table'); cy.get('.frappe-control[data-fieldname="child_test_depends_on_field"]').as('table');
cy.get('@table').find('button.grid-add-row').click();
cy.get('@table').findByRole('button', {name: 'Add Row'}).click();
cy.get('@table').find('[data-idx="1"]').as('row1'); cy.get('@table').find('[data-idx="1"]').as('row1');
cy.get('@row1').find('.btn-open-row').click(); cy.get('@row1').find('.btn-open-row').click();
cy.get('@row1').find('.form-in-grid').as('row1-form_in_grid'); cy.get('@row1').find('.form-in-grid').as('row1-form_in_grid');


+ 15
- 14
cypress/integration/file_uploader.js View File

@@ -25,7 +25,7 @@ context('FileUploader', () => {


cy.get_open_dialog().find('.file-name').should('contain', 'example.json'); cy.get_open_dialog().find('.file-name').should('contain', 'example.json');
cy.intercept('POST', '/api/method/upload_file').as('upload_file'); cy.intercept('POST', '/api/method/upload_file').as('upload_file');
cy.get_open_dialog().find('.btn-modal-primary').click();
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
cy.wait('@upload_file').its('response.statusCode').should('eq', 200); cy.wait('@upload_file').its('response.statusCode').should('eq', 200);
cy.get('.modal:visible').should('not.exist'); cy.get('.modal:visible').should('not.exist');
}); });
@@ -33,11 +33,11 @@ context('FileUploader', () => {
it('should accept uploaded files', () => { it('should accept uploaded files', () => {
open_upload_dialog(); open_upload_dialog();


cy.get_open_dialog().find('.btn-file-upload div:contains("Library")').click();
cy.get('.file-filter').type('example.json');
cy.get_open_dialog().find('.tree-label:contains("example.json")').first().click();
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.intercept('POST', '/api/method/upload_file').as('upload_file');
cy.get_open_dialog().find('.btn-primary').click();
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
cy.wait('@upload_file').its('response.body.message') cy.wait('@upload_file').its('response.body.message')
.should('have.property', 'file_name', 'example.json'); .should('have.property', 'file_name', 'example.json');
cy.get('.modal:visible').should('not.exist'); cy.get('.modal:visible').should('not.exist');
@@ -46,10 +46,12 @@ context('FileUploader', () => {
it('should accept web links', () => { it('should accept web links', () => {
open_upload_dialog(); open_upload_dialog();


cy.get_open_dialog().find('.btn-file-upload div:contains("Link")').click();
cy.get_open_dialog().find('.file-web-link input').type('https://github.com', { delay: 100, force: true });
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.intercept('POST', '/api/method/upload_file').as('upload_file');
cy.get_open_dialog().find('.btn-primary').click();
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
cy.wait('@upload_file').its('response.body.message') cy.wait('@upload_file').its('response.body.message')
.should('have.property', 'file_url', 'https://github.com'); .should('have.property', 'file_url', 'https://github.com');
cy.get('.modal:visible').should('not.exist'); cy.get('.modal:visible').should('not.exist');
@@ -62,15 +64,14 @@ context('FileUploader', () => {
subjectType: 'drag-n-drop', subjectType: 'drag-n-drop',
}); });


cy.get_open_dialog().find('.file-name').should('contain', 'sample_image.jpg');
cy.get_open_dialog().findAllByText('sample_image.jpg').should('exist');
cy.get_open_dialog().find('.btn-crop').first().click(); cy.get_open_dialog().find('.btn-crop').first().click();
cy.get_open_dialog().find('.image-cropper-actions > .btn-primary').should('contain', 'Crop');
cy.get_open_dialog().find('.image-cropper-actions > .btn-primary').click();
cy.get_open_dialog().find('.optimize-checkbox').first().should('contain', 'Optimize');
cy.get_open_dialog().find('.optimize-checkbox').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.intercept('POST', '/api/method/upload_file').as('upload_file');
cy.get_open_dialog().find('.btn-modal-primary').click();
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
cy.wait('@upload_file').its('response.statusCode').should('eq', 200); cy.wait('@upload_file').its('response.statusCode').should('eq', 200);
cy.get('.modal:visible').should('not.exist'); cy.get('.modal:visible').should('not.exist');
}); });


+ 79
- 0
cypress/integration/folder_navigation.js View File

@@ -0,0 +1,79 @@
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').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.get('.menu-btn-group > .btn').click();
cy.get('.menu-btn-group [data-label="New Folder"]').click();
cy.get('form > [data-fieldname="value"]').type('Test Folder');
cy.findByRole('button', {name: 'Create'}).click();
});

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.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.get('.menu-btn-group > .btn').click();
cy.get('.menu-btn-group [data-label="New Folder"]').click();
cy.get('form > [data-fieldname="value"]').type('Test Folder');
cy.findByRole('button', {name: 'Create'}).click();

//Navigating inside the added folder in the Attachments folder
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.findByRole('button', {name: 'Upload'}).click();

//To check if the added file is present in the Test Folder
cy.get('span.level-item > span').should('contain', 'Test Folder');
cy.get('.list-row-container').eq(0).should('contain.text', '72402.jpg');
cy.get('.list-row-checkbox').eq(0).click();

//Deleting the added file from the Test folder
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group [data-label="Delete"]').click();
cy.wait(700);
cy.findByRole('button', {name: 'Yes'}).click();
cy.wait(700);

//Deleting the Test Folder
cy.visit('/app/file/view/home/Attachments');
cy.get('.list-row-checkbox').eq(0).click();
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group [data-label="Delete"]').click();
cy.findByRole('button', {name: 'Yes'}).click();
});

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 > .list-row-checkbox').eq(0).click({force: true, delay: 500});
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group [data-label="Delete"]').click();
cy.findByRole('button', {name: 'Yes'}).click();
});
});

+ 1
- 1
cypress/integration/form.js View File

@@ -26,7 +26,7 @@ context('Form', () => {
cy.visit('/app/contact'); cy.visit('/app/contact');
cy.add_filter(); cy.add_filter();
cy.get('.filter-field .input-with-feedback.form-control').type('123', { force: true }); cy.get('.filter-field .input-with-feedback.form-control').type('123', { force: true });
cy.get('.filter-popover .apply-filters').click({ force: true });
cy.findByRole('button', {name: 'Apply Filters'}).click({ force: true });
cy.visit('/app/contact/Test Form Contact 3'); cy.visit('/app/contact/Test Form Contact 3');
cy.get('.prev-doc').should('be.visible').click(); cy.get('.prev-doc').should('be.visible').click();
cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible'); cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible');


+ 12
- 12
cypress/integration/form_tour.js View File

@@ -9,7 +9,7 @@ context('Form Tour', () => {


const open_test_form_tour = () => { const open_test_form_tour = () => {
cy.visit('/app/form-tour/Test Form Tour'); cy.visit('/app/form-tour/Test Form Tour');
cy.get('button[data-label="Show%20Tour"]').should('be.visible').and('contain', 'Show Tour').as('show_tour');
cy.findByRole('button', {name: 'Show Tour'}).should('be.visible').as('show_tour');
cy.get('@show_tour').click(); cy.get('@show_tour').click();
cy.wait(500); cy.wait(500);
cy.url().should('include', '/app/contact'); cy.url().should('include', '/app/contact');
@@ -20,10 +20,10 @@ context('Form Tour', () => {
it('navigates a form tour', () => { it('navigates a form tour', () => {
open_test_form_tour(); open_test_form_tour();


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


// next btn shouldn't move to next step, if first name is not entered // next btn shouldn't move to next step, if first name is not entered
cy.get('@next_btn').click(); cy.get('@next_btn').click();
@@ -39,7 +39,7 @@ context('Form Tour', () => {
// assert field is highlighted // assert field is highlighted
cy.get('.frappe-control[data-fieldname="last_name"]').as('last_name'); cy.get('.frappe-control[data-fieldname="last_name"]').as('last_name');
cy.get('@last_name').should('have.class', 'driver-highlighted-element'); cy.get('@last_name').should('have.class', 'driver-highlighted-element');
// after filling the field, next step should be highlighted // after filling the field, next step should be highlighted
cy.fill_field('last_name', 'Test Last Name', 'Data'); cy.fill_field('last_name', 'Test Last Name', 'Data');
cy.wait(500); cy.wait(500);
@@ -49,12 +49,12 @@ context('Form Tour', () => {
// assert field is highlighted // assert field is highlighted
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('phone_nos'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('phone_nos');
cy.get('@phone_nos').should('have.class', 'driver-highlighted-element'); cy.get('@phone_nos').should('have.class', 'driver-highlighted-element');
// move to next step // move to next step
cy.wait(500); cy.wait(500);
cy.get('@next_btn').click(); cy.get('@next_btn').click();
cy.wait(500); cy.wait(500);
// assert add row btn is highlighted // assert add row btn is highlighted
cy.get('@phone_nos').find('.grid-add-row').as('add_row'); cy.get('@phone_nos').find('.grid-add-row').as('add_row');
cy.get('@add_row').should('have.class', 'driver-highlighted-element'); cy.get('@add_row').should('have.class', 'driver-highlighted-element');
@@ -68,21 +68,21 @@ context('Form Tour', () => {
cy.get('.grid-row-open .frappe-control[data-fieldname="phone"]').as('phone'); cy.get('.grid-row-open .frappe-control[data-fieldname="phone"]').as('phone');
cy.get('@phone').should('have.class', 'driver-highlighted-element'); cy.get('@phone').should('have.class', 'driver-highlighted-element');
// enter value in a table field // enter value in a table field
cy.fill_table_field('phone_nos', '1', 'phone', '1234567890');
let field = cy.fill_table_field('phone_nos', '1', 'phone', '1234567890');
field.blur();


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

// collapse row // collapse row
cy.get('.grid-row-open .grid-collapse-row').click(); cy.get('.grid-row-open .grid-collapse-row').click();
cy.wait(500); cy.wait(500);
// assert save btn is highlighted // assert save btn is highlighted
cy.get('.primary-action').should('have.class', 'driver-highlighted-element'); cy.get('.primary-action').should('have.class', 'driver-highlighted-element');
cy.get('@next_btn').should('contain', 'Save');
cy.wait(500);
cy.get('.frappe-driver').findByRole('button', {name: 'Save'}).should('be.visible');


}); });
}); });

+ 2
- 2
cypress/integration/grid_pagination.js View File

@@ -30,12 +30,12 @@ context('Grid Pagination', () => {
it('adds and deletes rows and changes page', () => { it('adds and deletes rows and changes page', () => {
cy.visit('/app/contact/Test Contact'); cy.visit('/app/contact/Test Contact');
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
cy.get('@table').find('button.grid-add-row').click();
cy.get('@table').findByRole('button', {name: 'Add Row'}).click();
cy.get('@table').find('.grid-body .row-index').should('contain', 1001); cy.get('@table').find('.grid-body .row-index').should('contain', 1001);
cy.get('@table').find('.current-page-number').should('contain', '21'); cy.get('@table').find('.current-page-number').should('contain', '21');
cy.get('@table').find('.total-page-number').should('contain', '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').find('.grid-body .grid-row .grid-row-check').click({ force: true });
cy.get('@table').find('button.grid-remove-rows').click();
cy.get('@table').findByRole('button', {name: 'Delete'}).click();
cy.get('@table').find('.grid-body .row-index').last().should('contain', 1000); cy.get('@table').find('.grid-body .row-index').last().should('contain', 1000);
cy.get('@table').find('.current-page-number').should('contain', '20'); cy.get('@table').find('.current-page-number').should('contain', '20');
cy.get('@table').find('.total-page-number').should('contain', '20'); cy.get('@table').find('.total-page-number').should('contain', '20');


+ 6
- 6
cypress/integration/list_view_settings.js View File

@@ -17,9 +17,9 @@ context('List View Settings', () => {
cy.get('.dropdown-menu li').filter(':visible').contains('List Settings').click(); cy.get('.dropdown-menu li').filter(':visible').contains('List Settings').click();
cy.get('.modal-dialog').should('contain', 'DocType Settings'); cy.get('.modal-dialog').should('contain', 'DocType Settings');


cy.get('input[data-fieldname="disable_count"]').check({ force: true });
cy.get('input[data-fieldname="disable_sidebar_stats"]').check({ force: true });
cy.get('button').filter(':visible').contains('Save').click();
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.reload({ force: true });


@@ -29,8 +29,8 @@ context('List View Settings', () => {
cy.get('.menu-btn-group button').click({ force: true }); cy.get('.menu-btn-group button').click({ force: true });
cy.get('.dropdown-menu li').filter(':visible').contains('List Settings').click(); cy.get('.dropdown-menu li').filter(':visible').contains('List Settings').click();
cy.get('.modal-dialog').should('contain', 'DocType Settings'); cy.get('.modal-dialog').should('contain', 'DocType Settings');
cy.get('input[data-fieldname="disable_count"]').uncheck({ force: true });
cy.get('input[data-fieldname="disable_sidebar_stats"]').uncheck({ force: true });
cy.get('button').filter(':visible').contains('Save').click();
cy.findByLabelText('Disable Count').uncheck({ force: true });
cy.findByLabelText('Disable Sidebar Stats').uncheck({ force: true });
cy.findByRole('button', {name: 'Save'}).click();
}); });
}); });

+ 6
- 6
cypress/integration/login.js View File

@@ -11,13 +11,13 @@ context('Login', () => {


it('validates password', () => { it('validates password', () => {
cy.get('#login_email').type('Administrator'); cy.get('#login_email').type('Administrator');
cy.get('.btn-login:visible').click();
cy.findByRole('button', {name: 'Login'}).click();
cy.location('pathname').should('eq', '/login'); cy.location('pathname').should('eq', '/login');
}); });


it('validates email', () => { it('validates email', () => {
cy.get('#login_password').type('qwe'); cy.get('#login_password').type('qwe');
cy.get('.btn-login:visible').click();
cy.findByRole('button', {name: 'Login'}).click();
cy.location('pathname').should('eq', '/login'); cy.location('pathname').should('eq', '/login');
}); });


@@ -25,8 +25,8 @@ context('Login', () => {
cy.get('#login_email').type('Administrator'); cy.get('#login_email').type('Administrator');
cy.get('#login_password').type('qwer'); cy.get('#login_password').type('qwer');


cy.get('.btn-login:visible').click();
cy.get('.btn-login:visible').contains('Invalid Login. Try again.');
cy.findByRole('button', {name: 'Login'}).click();
cy.findByRole('button', {name: 'Invalid Login. Try again.'}).should('exist');
cy.location('pathname').should('eq', '/login'); cy.location('pathname').should('eq', '/login');
}); });


@@ -34,7 +34,7 @@ context('Login', () => {
cy.get('#login_email').type('Administrator'); cy.get('#login_email').type('Administrator');
cy.get('#login_password').type(Cypress.config('adminPassword')); cy.get('#login_password').type(Cypress.config('adminPassword'));


cy.get('.btn-login:visible').click();
cy.findByRole('button', {name: 'Login'}).click();
cy.location('pathname').should('eq', '/app'); cy.location('pathname').should('eq', '/app');
cy.window().its('frappe.session.user').should('eq', 'Administrator'); cy.window().its('frappe.session.user').should('eq', 'Administrator');
}); });
@@ -60,7 +60,7 @@ context('Login', () => {
cy.get('#login_email').type('Administrator'); cy.get('#login_email').type('Administrator');
cy.get('#login_password').type(Cypress.config('adminPassword')); cy.get('#login_password').type(Cypress.config('adminPassword'));


cy.get('.btn-login:visible').click();
cy.findByRole('button', {name: 'Login'}).click();


// verify redirected location and url params after login // verify redirected location and url params after login
cy.url().should('include', '/me?' + payload.toString().replace('+', '%20')); cy.url().should('include', '/me?' + payload.toString().replace('+', '%20'));


+ 8
- 8
cypress/integration/recorder.js View File

@@ -16,24 +16,24 @@ context('Recorder', () => {
it('Navigate to Recorder', () => { it('Navigate to Recorder', () => {
cy.visit('/app'); cy.visit('/app');
cy.awesomebar('recorder'); cy.awesomebar('recorder');
cy.get('h3').should('contain', 'Recorder');
cy.findByTitle('Recorder').should('exist');
cy.url().should('include', '/recorder/detail'); cy.url().should('include', '/recorder/detail');
}); });


it('Recorder Empty State', () => { it('Recorder Empty State', () => {
cy.get('.title-text').should('contain', 'Recorder');
cy.findByTitle('Recorder').should('exist');


cy.get('.indicator-pill').should('contain', 'Inactive').should('have.class', 'red'); cy.get('.indicator-pill').should('contain', 'Inactive').should('have.class', 'red');


cy.get('.primary-action').should('contain', 'Start');
cy.get('.btn-secondary').should('contain', 'Clear');
cy.findByRole('button', {name: 'Start'}).should('exist');
cy.findByRole('button', {name: 'Clear'}).should('exist');


cy.get('.msg-box').should('contain', 'Inactive'); cy.get('.msg-box').should('contain', 'Inactive');
cy.get('.msg-box .btn-primary').should('contain', 'Start Recording');
cy.findByRole('button', {name: 'Start Recording'}).should('exist');
}); });


it('Recorder Start', () => { it('Recorder Start', () => {
cy.get('.primary-action').should('contain', 'Start').click();
cy.findByRole('button', {name: 'Start'}).click();
cy.get('.indicator-pill').should('contain', 'Active').should('have.class', 'green'); cy.get('.indicator-pill').should('contain', 'Active').should('have.class', 'green');


cy.get('.msg-box').should('contain', 'No Requests'); cy.get('.msg-box').should('contain', 'No Requests');
@@ -46,12 +46,12 @@ context('Recorder', () => {
cy.get('.list-count').should('contain', '20 of '); cy.get('.list-count').should('contain', '20 of ');


cy.visit('/app/recorder'); cy.visit('/app/recorder');
cy.get('.title-text').should('contain', 'Recorder');
cy.findByTitle('Recorder').should('exist');
cy.get('.result-list').should('contain', '/api/method/frappe.desk.reportview.get'); cy.get('.result-list').should('contain', '/api/method/frappe.desk.reportview.get');
}); });


it('Recorder View Request', () => { it('Recorder View Request', () => {
cy.get('.primary-action').should('contain', 'Start').click();
cy.findByRole('button', {name: 'Start'}).click();


cy.visit('/app/List/DocType/List'); cy.visit('/app/List/DocType/List');
cy.intercept('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); cy.intercept('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh');


+ 1
- 1
cypress/integration/report_view.js View File

@@ -23,7 +23,7 @@ context('Report View', () => {
let cell = cy.get('.dt-row-0 > .dt-cell--col-4'); let cell = cy.get('.dt-row-0 > .dt-cell--col-4');
// select the cell // select the cell
cell.dblclick(); cell.dblclick();
cell.find('input[data-fieldname="enabled"]').check({ force: true });
cell.findByRole('checkbox').check({ force: true });
cy.get('.dt-row-0 > .dt-cell--col-5').click(); cy.get('.dt-row-0 > .dt-cell--col-5').click();
cy.wait('@value-update'); cy.wait('@value-update');
cy.get('@doc').then(doc => { cy.get('@doc').then(doc => {


+ 20
- 20
cypress/integration/timeline.js View File

@@ -10,26 +10,26 @@ context('Timeline', () => {
it('Adding new ToDo, adding new comment, verifying comment addition & deletion and deleting ToDo', () => { it('Adding new ToDo, adding new comment, verifying comment addition & deletion and deleting ToDo', () => {
//Adding new ToDo //Adding new ToDo
cy.click_listview_primary_button('Add ToDo'); cy.click_listview_primary_button('Add ToDo');
cy.get('.modal-footer > .custom-actions > .btn').contains('Edit in full page').click();
cy.get('.row > .section-body > .form-column > form > .frappe-control > .form-group > .control-input-wrapper > .control-input > .ql-container > .ql-editor').eq(0).type('Test ToDo', {force: true});
cy.findByRole('button', {name: 'Edit in full page'}).click();
cy.get('[data-fieldname="description"] .ql-editor').eq(0).type('Test ToDo', {force: true});
cy.wait(200); cy.wait(200);
cy.get('#page-ToDo > .page-head > .container > .row > .col > .standard-actions > .primary-action').contains('Save').click();
cy.findByRole('button', {name: 'Save'}).click();
cy.wait(700); cy.wait(700);
cy.visit('/app/todo'); cy.visit('/app/todo');
cy.get('.list-row > .level-left > .list-subject > .level-item.ellipsis > .ellipsis').eq(0).click();
cy.get('.level-item.ellipsis').eq(0).click();


//To check if the comment box is initially empty and tying some text into it //To check if the comment box is initially empty and tying some text into it
cy.get('.comment-input-container > .frappe-control > .ql-container > .ql-editor').should('contain', '').type('Testing Timeline');
cy.get('[data-fieldname="comment"] .ql-editor').should('contain', '').type('Testing Timeline');


//Adding new comment //Adding new comment
cy.get('.comment-input-wrapper > .btn').contains('Comment').click();
cy.findByRole('button', {name: 'Comment'}).click();


//To check if the commented text is visible in the timeline content //To check if the commented text is visible in the timeline content
cy.get('.timeline-content').should('contain', 'Testing Timeline'); cy.get('.timeline-content').should('contain', 'Testing Timeline');


//Editing comment //Editing comment
cy.click_timeline_action_btn(0); cy.click_timeline_action_btn(0);
cy.get('.timeline-content > .timeline-message-box > .comment-edit-box > .frappe-control > .ql-container > .ql-editor').first().type(' 123');
cy.get('.timeline-content [data-fieldname="comment"] .ql-editor').first().type(' 123');
cy.click_timeline_action_btn(0); cy.click_timeline_action_btn(0);


//To check if the edited comment text is visible in timeline content //To check if the edited comment text is visible in timeline content
@@ -37,20 +37,20 @@ context('Timeline', () => {


//Discarding comment //Discarding comment
cy.click_timeline_action_btn(0); cy.click_timeline_action_btn(0);
cy.get('.actions > .btn').eq(1).first().click();
cy.findByRole('button', {name: 'Dismiss'}).click();


//To check if after discarding the timeline content is same as previous //To check if after discarding the timeline content is same as previous
cy.get('.timeline-content').should('contain', 'Testing Timeline 123'); cy.get('.timeline-content').should('contain', 'Testing Timeline 123');


//Deleting the added comment //Deleting the added comment
cy.get('.actions > .btn > .icon').first().click(); cy.get('.actions > .btn > .icon').first().click();
cy.get('.modal-footer > .standard-actions > .btn-primary').contains('Yes').click();
cy.findByRole('button', {name: 'Yes'}).click();
cy.click_modal_primary_button('Yes'); cy.click_modal_primary_button('Yes');


//Deleting the added ToDo //Deleting the added ToDo
cy.get('#page-ToDo > .page-head > .container > .row > .col > .standard-actions > .menu-btn-group > .btn').click({force: true});
cy.get('.menu-btn-group > .dropdown-menu > li > .grey-link').eq(17).click({force: true});
cy.get('.modal.show > .modal-dialog > .modal-content > .modal-footer > .standard-actions > .btn-primary').contains('Yes').click({force: true});
cy.get('.menu-btn-group button').eq(1).click();
cy.get('.menu-btn-group [data-label="Delete"]').click();
cy.findByRole('button', {name: 'Yes'}).click();
}); });


it('Timeline should have submit and cancel activity information', () => { it('Timeline should have submit and cancel activity information', () => {
@@ -64,31 +64,31 @@ context('Timeline', () => {


//Adding a new entry for the created custom doctype //Adding a new entry for the created custom doctype
cy.fill_field('title', 'Test'); cy.fill_field('title', 'Test');
cy.get('.modal-footer > .standard-actions > .btn-primary').contains('Save').click();
cy.get('.modal-footer > .standard-actions > .btn-primary').contains('Submit').click();
cy.findByRole('button', {name: 'Save'}).click();
cy.findByRole('button', {name: 'Submit'}).click();
cy.visit('/app/custom-submittable-doctype'); cy.visit('/app/custom-submittable-doctype');
cy.get('.list-subject > .bold > .ellipsis').eq(0).click(); cy.get('.list-subject > .bold > .ellipsis').eq(0).click();


//To check if the submission of the documemt is visible in the timeline content //To check if the submission of the documemt is visible in the timeline content
cy.get('.timeline-content').should('contain', 'Administrator submitted this document'); cy.get('.timeline-content').should('contain', 'Administrator submitted this document');
cy.get('.page-actions > .standard-actions > .btn-secondary').contains('Cancel').click({delay: 900});
cy.get('.modal-footer > .standard-actions > .btn-primary').contains('Yes').click();
cy.findByRole('button', {name: 'Cancel'}).click({delay: 900});
cy.findByRole('button', {name: 'Yes'}).click();
//To check if the cancellation of the documemt is visible in the timeline content //To check if the cancellation of the documemt is visible in the timeline content
cy.get('.timeline-content').should('contain', 'Administrator cancelled this document'); cy.get('.timeline-content').should('contain', 'Administrator cancelled this document');


//Deleting the document //Deleting the document
cy.visit('/app/custom-submittable-doctype'); cy.visit('/app/custom-submittable-doctype');
cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click(); cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click();
cy.get('.page-actions > .standard-actions > .actions-btn-group > .btn').contains('Actions').click();
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group > .dropdown-menu > li > .grey-link').eq(7).click(); cy.get('.actions-btn-group > .dropdown-menu > li > .grey-link').eq(7).click();
cy.click_modal_primary_button('Yes', {force: true, delay: 700}); cy.click_modal_primary_button('Yes', {force: true, delay: 700});


//Deleting the custom doctype //Deleting the custom doctype
cy.visit('/app/doctype'); cy.visit('/app/doctype');
cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click(); cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click();
cy.get('.page-actions > .standard-actions > .actions-btn-group > .btn').contains('Actions').click();
cy.get('.actions-btn-group > .dropdown-menu > li > .grey-link').eq(5).click();
cy.findByRole('button', {name: 'Actions'}).click();
cy.get('.actions-btn-group [data-label="Delete"]').click();
cy.click_modal_primary_button('Yes'); cy.click_modal_primary_button('Yes');
}); });
}); });

+ 9
- 10
cypress/integration/timeline_email.js View File

@@ -8,14 +8,13 @@ context('Timeline Email', () => {
it('Adding new ToDo, adding email and verifying timeline content for email attachment, deleting attachment and ToDo', () => { it('Adding new ToDo, adding email and verifying timeline content for email attachment, deleting attachment and ToDo', () => {
//Adding new ToDo //Adding new ToDo
cy.click_listview_primary_button('Add ToDo'); cy.click_listview_primary_button('Add ToDo');
cy.get('.custom-actions > .btn').trigger('click', {delay: 500});
cy.get('.row > .section-body > .form-column > form > .frappe-control > .form-group > .control-input-wrapper > .control-input > .ql-container > .ql-editor').eq(0).type('Test ToDo', {force: true});
cy.get('.custom-actions:visible > .btn').contains("Edit in full page").click({delay: 500});
cy.fill_field("description", "Test ToDo", "Text Editor");
cy.wait(500); cy.wait(500);
//cy.click_listview_primary_button('Save');
cy.get('.primary-action').contains('Save').click({force: true}); cy.get('.primary-action').contains('Save').click({force: true});
cy.wait(700); cy.wait(700);
cy.visit('/app/todo'); cy.visit('/app/todo');
cy.get('.list-row > .level-left > .list-subject > .level-item.ellipsis > .ellipsis').eq(0).click();
cy.get('.list-row > .level-left > .list-subject').eq(0).click();


//Creating a new email //Creating a new email
cy.get('.timeline-actions > .btn').click(); cy.get('.timeline-actions > .btn').click();
@@ -47,7 +46,7 @@ context('Timeline Email', () => {


//Removing the added attachment //Removing the added attachment
cy.get('.attachment-row > .data-pill > .remove-btn > .icon').click(); cy.get('.attachment-row > .data-pill > .remove-btn > .icon').click();
cy.get('.modal-dialog > .modal-content > .modal-footer > .standard-actions > .btn-primary').contains('Yes').click();
cy.get('.modal-footer:visible > .standard-actions > .btn-primary').contains('Yes').click();


//To check if the removed attachment is shown in the timeline content //To check if the removed attachment is shown in the timeline content
cy.get('.timeline-content').should('contain', 'Removed 72402.jpg'); cy.get('.timeline-content').should('contain', 'Removed 72402.jpg');
@@ -55,17 +54,17 @@ context('Timeline Email', () => {


//To check if the discard button functionality in email is working correctly //To check if the discard button functionality in email is working correctly
cy.get('.timeline-actions > .btn').click(); cy.get('.timeline-actions > .btn').click();
cy.fill_field('recipients', 'test@example.com', 'MultiSelect');
cy.fill_field('recipients', 'test@example.com', 'MultiSelect');
cy.get('.modal-footer > .standard-actions > .btn-secondary').contains('Discard').click(); cy.get('.modal-footer > .standard-actions > .btn-secondary').contains('Discard').click();
cy.wait(500); cy.wait(500);
cy.get('.timeline-actions > .btn').click(); cy.get('.timeline-actions > .btn').click();
cy.wait(500); cy.wait(500);
cy.get_field('recipients', 'MultiSelect').should('have.text', ''); cy.get_field('recipients', 'MultiSelect').should('have.text', '');
cy.get('.modal.show > .modal-dialog > .modal-content > .modal-header > .modal-actions > .btn-modal-close > .icon').click();
cy.get('.modal-header:visible > .modal-actions > .btn-modal-close > .icon').click();


//Deleting the added ToDo //Deleting the added ToDo
cy.get('#page-ToDo > .page-head > .container > .row > .col > .standard-actions > .menu-btn-group > .btn').click();
cy.get('.menu-btn-group > .dropdown-menu > li > .grey-link').eq(17).click();
cy.get('.modal.show > .modal-dialog > .modal-content > .modal-footer > .standard-actions > .btn-primary').click();
cy.get('.menu-btn-group:visible > .btn').click();
cy.get('.menu-btn-group:visible > .dropdown-menu > li > .dropdown-item').contains('Delete').click();
cy.get('.modal-footer:visible > .standard-actions > .btn-primary').click();
}); });
}); });

+ 90
- 0
cypress/integration/workspace.js View File

@@ -0,0 +1,90 @@
context('Workspace 2.0', () => {
before(() => {
cy.visit('/login');
cy.login();
cy.visit('/app/website');
});

it('Navigate to page from sidebar', () => {
cy.visit('/app/build');
cy.get('.codex-editor__redactor .ce-block');
cy.get('.sidebar-item-container[item-name="Settings"]').first().click();
cy.location('pathname').should('eq', '/app/settings');
});

it('Create Private Page', () => {
cy.get('.codex-editor__redactor .ce-block');
cy.get('.custom-actions button[data-label="Create%20Workspace"]').click();
cy.fill_field('title', 'Test Private Page', 'Data');
cy.fill_field('icon', 'edit', 'Icon');
cy.get_open_dialog().find('.modal-header').click();
cy.get_open_dialog().find('.btn-primary').click();

// check if sidebar item is added in pubic section
cy.get('.sidebar-item-container[item-name="Test Private Page"]').should('have.attr', 'item-public', '0');

cy.get('.standard-actions .btn-primary[data-label="Save Customizations"]').click();
cy.wait(300);
cy.get('.sidebar-item-container[item-name="Test Private Page"]').should('have.attr', 'item-public', '0');

cy.wait(500);
cy.get('.codex-editor__redactor .ce-block');
cy.get('.standard-actions .btn-secondary[data-label=Edit]').click();
});

it('Add New Block', () => {
cy.get('.codex-editor__redactor .ce-block');
cy.get('.custom-actions .inner-group-button[data-label="Add%20Block"]').click();
cy.get('.custom-actions .inner-group-button .dropdown-menu .block-menu-item-label').contains('Heading').click();
cy.get(":focus").type('Header');
cy.get(".ce-block:last").find('.ce-header').should('exist');

cy.get('.custom-actions .inner-group-button[data-label="Add%20Block"]').click();
cy.get('.custom-actions .inner-group-button .dropdown-menu .block-menu-item-label').contains('Text').click();
cy.get(":focus").type('Paragraph text');
cy.get(".ce-block:last").find('.ce-paragraph').should('exist');
});

it('Delete A Block', () => {
cy.get(".ce-block:last").find('.delete-paragraph').click();
cy.get(".ce-block:last").find('.ce-paragraph').should('not.exist');
});

it('Shrink and Expand A Block', () => {
cy.get(".ce-block:last").find('.tune-btn').click();
cy.get('.ce-settings--opened .ce-shrink-button').click();
cy.get(".ce-block:last").should('have.class', 'col-11');
cy.get('.ce-settings--opened .ce-shrink-button').click();
cy.get(".ce-block:last").should('have.class', 'col-10');
cy.get('.ce-settings--opened .ce-shrink-button').click();
cy.get(".ce-block:last").should('have.class', 'col-9');
cy.get('.ce-settings--opened .ce-expand-button').click();
cy.get(".ce-block:last").should('have.class', 'col-10');
cy.get('.ce-settings--opened .ce-expand-button').click();
cy.get(".ce-block:last").should('have.class', 'col-11');
cy.get('.ce-settings--opened .ce-expand-button').click();
cy.get(".ce-block:last").should('have.class', 'col-12');
});

it('Change Header Text Size', () => {
cy.get('.ce-settings--opened .cdx-settings-button[data-level="3"]').click();
cy.get(".ce-block:last").find('.widget-head h3').should('exist');
cy.get('.ce-settings--opened .cdx-settings-button[data-level="4"]').click();
cy.get(".ce-block:last").find('.widget-head h4').should('exist');

cy.get('.standard-actions .btn-primary[data-label="Save Customizations"]').click();
});

it('Delete Private Page', () => {
cy.get('.codex-editor__redactor .ce-block');
cy.get('.standard-actions .btn-secondary[data-label=Edit]').click();

cy.get('.sidebar-item-container[item-name="Test Private Page"]').find('.sidebar-item-control .delete-page').click();
cy.wait(300);
cy.get('.modal-footer > .standard-actions > .btn-modal-primary:visible').first().click();
cy.get('.standard-actions .btn-primary[data-label="Save Customizations"]').click();
cy.get('.codex-editor__redactor .ce-block');
cy.get('.sidebar-item-container[item-name="Test Private Page"]').should('not.exist');
});

});

+ 1
- 0
cypress/support/commands.js View File

@@ -1,4 +1,5 @@
import 'cypress-file-upload'; import 'cypress-file-upload';
import '@testing-library/cypress/add-commands';
// *********************************************** // ***********************************************
// This example commands.js shows you how to // This example commands.js shows you how to
// create various custom commands and overwrite // create various custom commands and overwrite


+ 5
- 1
frappe/__init__.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE
""" """
Frappe - Low Code Open Source Framework in Python and JS Frappe - Low Code Open Source Framework in Python and JS


@@ -140,7 +140,11 @@ lang = local("lang")
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from frappe.database.mariadb.database import MariaDBDatabase from frappe.database.mariadb.database import MariaDBDatabase
from frappe.database.postgres.database import PostgresDatabase from frappe.database.postgres.database import PostgresDatabase
from pypika import Query

db: typing.Union[MariaDBDatabase, PostgresDatabase] db: typing.Union[MariaDBDatabase, PostgresDatabase]
qb: Query

# end: static analysis hack # end: static analysis hack


def init(site, sites_path=None, new_site=False): def init(site, sites_path=None, new_site=False):


+ 1
- 1
frappe/api.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE
import base64 import base64
import binascii import binascii
import json import json


+ 1
- 1
frappe/app.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE


import os import os
import logging import logging


+ 1
- 1
frappe/automation/doctype/assignment_rule/assignment_rule.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 5
- 5
frappe/automation/doctype/assignment_rule/test_assignment_rule.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors # Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe import frappe
import unittest import unittest
from frappe.utils import random_string from frappe.utils import random_string
@@ -76,7 +76,7 @@ class TestAutoAssign(unittest.TestCase):
# clear 5 assignments for first user # clear 5 assignments for first user
# can't do a limit in "delete" since postgres does not support it # can't do a limit in "delete" since postgres does not support it
for d in frappe.get_all('ToDo', dict(reference_type = 'Note', owner = 'test@example.com'), limit=5): for d in frappe.get_all('ToDo', dict(reference_type = 'Note', owner = 'test@example.com'), limit=5):
frappe.db.sql("delete from tabToDo where name = %s", d.name)
frappe.db.delete("ToDo", {"name": d.name})


# add 5 more assignments # add 5 more assignments
for i in range(5): for i in range(5):
@@ -177,7 +177,7 @@ class TestAutoAssign(unittest.TestCase):
), 'owner'), 'test@example.com') ), 'owner'), 'test@example.com')


def check_assignment_rule_scheduling(self): def check_assignment_rule_scheduling(self):
frappe.db.sql("DELETE FROM `tabAssignment Rule`")
frappe.db.delete("Assignment Rule")


days_1 = [dict(day = 'Sunday'), dict(day = 'Monday'), dict(day = 'Tuesday')] days_1 = [dict(day = 'Sunday'), dict(day = 'Monday'), dict(day = 'Tuesday')]


@@ -204,7 +204,7 @@ class TestAutoAssign(unittest.TestCase):
), 'owner'), ['test3@example.com']) ), 'owner'), ['test3@example.com'])


def test_assignment_rule_condition(self): def test_assignment_rule_condition(self):
frappe.db.sql("DELETE FROM `tabAssignment Rule`")
frappe.db.delete("Assignment Rule")


# Add expiry_date custom field # Add expiry_date custom field
from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.custom.doctype.custom_field.custom_field import create_custom_field
@@ -253,7 +253,7 @@ class TestAutoAssign(unittest.TestCase):
assignment_rule.delete() assignment_rule.delete()


def clear_assignments(): def clear_assignments():
frappe.db.sql("delete from tabToDo where reference_type = 'Note'")
frappe.db.delete("ToDo", {"reference_type": "Note"})


def get_assignment_rule(days, assign=None): def get_assignment_rule(days, assign=None):
frappe.delete_doc_if_exists('Assignment Rule', 'For Note 1') frappe.delete_doc_if_exists('Assignment Rule', 'For Note 1')


+ 1
- 1
frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


# import frappe # import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


# import frappe # import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/automation/doctype/auto_repeat/auto_repeat.js View File

@@ -30,7 +30,7 @@ frappe.ui.form.on('Auto Repeat', {
refresh: function(frm) { refresh: function(frm) {
// auto repeat message // auto repeat message
if (frm.is_new()) { if (frm.is_new()) {
let customize_form_link = `<a href="/app/customize form">${__('Customize Form')}</a>`;
let customize_form_link = `<a href="/app/customize-form">${__('Customize Form')}</a>`;
frm.dashboard.set_headline(__('To configure Auto Repeat, enable "Allow Auto Repeat" from {0}.', [customize_form_link])); frm.dashboard.set_headline(__('To configure Auto Repeat, enable "Allow Auto Repeat" from {0}.', [customize_form_link]));
} }




+ 1
- 1
frappe/automation/doctype/auto_repeat/auto_repeat.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe import _ from frappe import _


+ 1
- 1
frappe/automation/doctype/auto_repeat/test_auto_repeat.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and Contributors # Copyright (c) 2018, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import unittest import unittest


import frappe import frappe


+ 1
- 1
frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and contributors # Copyright (c) 2020, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


# import frappe # import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/automation/doctype/milestone/milestone.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/automation/doctype/milestone/test_milestone.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors # Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
#import frappe #import frappe
import unittest import unittest




+ 1
- 1
frappe/automation/doctype/milestone_tracker/milestone_tracker.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 3
- 3
frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py View File

@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors # Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe import frappe
import frappe.cache_manager import frappe.cache_manager
import unittest import unittest


class TestMilestoneTracker(unittest.TestCase): class TestMilestoneTracker(unittest.TestCase):
def test_milestone(self): def test_milestone(self):
frappe.db.sql('delete from `tabMilestone Tracker`')
frappe.db.delete("Milestone Tracker")


frappe.cache().delete_key('milestone_tracker_map') frappe.cache().delete_key('milestone_tracker_map')


@@ -44,5 +44,5 @@ class TestMilestoneTracker(unittest.TestCase):
self.assertEqual(milestones[0].value, 'Closed') self.assertEqual(milestones[0].value, 'Closed')


# cleanup # cleanup
frappe.db.sql('delete from tabMilestone')
frappe.db.delete("Milestone")
milestone_tracker.delete() milestone_tracker.delete()

+ 34
- 4
frappe/automation/workspace/tools/tools.json View File

@@ -1,22 +1,27 @@
{ {
"category": "Administration",
"category": "",
"charts": [], "charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"ToDo\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Note\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"File\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Assignment Rule\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Auto Repeat\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Email\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Automation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Event Streaming\", \"col\": 4}}]",
"creation": "2020-03-02 14:53:24.980279", "creation": "2020-03-02 14:53:24.980279",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 0, "disable_user_customization": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
"extends": "",
"extends_another_page": 0, "extends_another_page": 0,
"for_user": "",
"hide_custom": 0, "hide_custom": 0,
"icon": "tool", "icon": "tool",
"idx": 0, "idx": 0,
"is_standard": 1,
"is_default": 0,
"is_standard": 0,
"label": "Tools", "label": "Tools",
"links": [ "links": [
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Tools", "label": "Tools",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@@ -25,6 +30,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "To Do", "label": "To Do",
"link_count": 0,
"link_to": "ToDo", "link_to": "ToDo",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@@ -35,6 +41,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Calendar", "label": "Calendar",
"link_count": 0,
"link_to": "Event", "link_to": "Event",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@@ -45,6 +52,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Note", "label": "Note",
"link_count": 0,
"link_to": "Note", "link_to": "Note",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@@ -55,6 +63,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Files", "label": "Files",
"link_count": 0,
"link_to": "File", "link_to": "File",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@@ -65,6 +74,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Activity", "label": "Activity",
"link_count": 0,
"link_to": "activity", "link_to": "activity",
"link_type": "Page", "link_type": "Page",
"onboard": 0, "onboard": 0,
@@ -74,6 +84,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Email", "label": "Email",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@@ -82,6 +93,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Newsletter", "label": "Newsletter",
"link_count": 0,
"link_to": "Newsletter", "link_to": "Newsletter",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@@ -92,6 +104,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Email Group", "label": "Email Group",
"link_count": 0,
"link_to": "Email Group", "link_to": "Email Group",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@@ -101,6 +114,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Automation", "label": "Automation",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@@ -109,6 +123,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Assignment Rule", "label": "Assignment Rule",
"link_count": 0,
"link_to": "Assignment Rule", "link_to": "Assignment Rule",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@@ -119,6 +134,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Milestone", "label": "Milestone",
"link_count": 0,
"link_to": "Milestone", "link_to": "Milestone",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@@ -129,6 +145,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Auto Repeat", "label": "Auto Repeat",
"link_count": 0,
"link_to": "Auto Repeat", "link_to": "Auto Repeat",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@@ -138,6 +155,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Streaming", "label": "Event Streaming",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@@ -146,6 +164,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Producer", "label": "Event Producer",
"link_count": 0,
"link_to": "Event Producer", "link_to": "Event Producer",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@@ -156,6 +175,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Consumer", "label": "Event Consumer",
"link_count": 0,
"link_to": "Event Consumer", "link_to": "Event Consumer",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@@ -166,6 +186,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Update Log", "label": "Event Update Log",
"link_count": 0,
"link_to": "Event Update Log", "link_to": "Event Update Log",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@@ -176,6 +197,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Sync Log", "label": "Event Sync Log",
"link_count": 0,
"link_to": "Event Sync Log", "link_to": "Event Sync Log",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@@ -186,19 +208,26 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Document Type Mapping", "label": "Document Type Mapping",
"link_count": 0,
"link_to": "Document Type Mapping", "link_to": "Document Type Mapping",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
} }
], ],
"modified": "2020-12-01 13:38:39.950350",
"modified": "2021-08-05 12:16:02.839180",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Automation", "module": "Automation",
"name": "Tools", "name": "Tools",
"onboarding": "",
"owner": "Administrator", "owner": "Administrator",
"parent_page": "",
"pin_to_bottom": 0, "pin_to_bottom": 0,
"pin_to_top": 0, "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
"sequence_id": 26,
"shortcuts": [ "shortcuts": [
{ {
"label": "ToDo", "label": "ToDo",
@@ -225,5 +254,6 @@
"link_to": "Auto Repeat", "link_to": "Auto Repeat",
"type": "DocType" "type": "DocType"
} }
]
],
"title": "Tools"
} }

+ 3
- 3
frappe/boot.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE
""" """
bootstrap client session bootstrap client session
""" """
@@ -105,8 +105,8 @@ def load_conf_settings(bootinfo):
if key in conf: bootinfo[key] = conf.get(key) if key in conf: bootinfo[key] = conf.get(key)


def load_desktop_data(bootinfo): def load_desktop_data(bootinfo):
from frappe.desk.desktop import get_desk_sidebar_items
bootinfo.allowed_workspaces = get_desk_sidebar_items()
from frappe.desk.desktop import get_wspace_sidebar_items
bootinfo.allowed_workspaces = get_wspace_sidebar_items().get('pages')
bootinfo.module_page_map = get_controller("Workspace").get_module_page_map() bootinfo.module_page_map = get_controller("Workspace").get_module_page_map()
bootinfo.dashboards = frappe.get_all("Dashboard") bootinfo.dashboards = frappe.get_all("Dashboard")




+ 1
- 1
frappe/build.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE
import os import os
import re import re
import json import json


+ 1
- 1
frappe/cache_manager.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE


import frappe, json import frappe, json
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/chat/doctype/chat_token/chat_token.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and contributors # Copyright (c) 2018, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/client.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE
import frappe import frappe
from frappe import _ from frappe import _
import frappe.model import frappe.model


+ 1
- 1
frappe/commands/__init__.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE


import sys import sys
import click import click


+ 25
- 10
frappe/commands/utils.py View File

@@ -486,15 +486,26 @@ frappe.db.connect()




@click.command('console') @click.command('console')
@click.option(
'--autoreload',
is_flag=True,
help="Reload changes to code automatically"
)
@pass_context @pass_context
def console(context):
def console(context, autoreload=False):
"Start ipython console for a site" "Start ipython console for a site"
site = get_site(context) site = get_site(context)
frappe.init(site=site) frappe.init(site=site)
frappe.connect() frappe.connect()
frappe.local.lang = frappe.db.get_default("lang") frappe.local.lang = frappe.db.get_default("lang")


import IPython
from IPython.terminal.embed import InteractiveShellEmbed

terminal = InteractiveShellEmbed()
if autoreload:
terminal.extension_manager.load_extension("autoreload")
terminal.run_line_magic("autoreload", "2")

all_apps = frappe.get_installed_apps() all_apps = frappe.get_installed_apps()
failed_to_import = [] failed_to_import = []


@@ -509,7 +520,9 @@ def console(context):
if failed_to_import: if failed_to_import:
print("\nFailed to import:\n{}".format(", ".join(failed_to_import))) print("\nFailed to import:\n{}".format(", ".join(failed_to_import)))


IPython.embed(display_banner="", header="", colors="neutral")
terminal.colors = "neutral"
terminal.display_banner = False
terminal()




@click.command('run-tests') @click.command('run-tests')
@@ -524,7 +537,7 @@ def console(context):
@click.option('--skip-test-records', is_flag=True, default=False, help="Don't create test records") @click.option('--skip-test-records', is_flag=True, default=False, help="Don't create test records")
@click.option('--skip-before-tests', is_flag=True, default=False, help="Don't run before tests hook") @click.option('--skip-before-tests', is_flag=True, default=False, help="Don't run before tests hook")
@click.option('--junit-xml-output', help="Destination file path for junit xml report") @click.option('--junit-xml-output', help="Destination file path for junit xml report")
@click.option('--failfast', is_flag=True, default=False)
@click.option('--failfast', is_flag=True, default=False, help="Stop the test run on the first error or failure")
@pass_context @pass_context
def run_tests(context, app=None, module=None, doctype=None, test=(), profile=False, def run_tests(context, app=None, module=None, doctype=None, test=(), profile=False,
coverage=False, junit_xml_output=False, ui_tests = False, doctype_list_path=None, coverage=False, junit_xml_output=False, ui_tests = False, doctype_list_path=None,
@@ -589,24 +602,26 @@ def run_ui_tests(context, app, headless=False, parallel=True, ci_build_id=None):
admin_password = frappe.get_conf(site).admin_password admin_password = frappe.get_conf(site).admin_password


# override baseUrl using env variable # override baseUrl using env variable
site_env = 'CYPRESS_baseUrl={}'.format(site_url)
password_env = 'CYPRESS_adminPassword={}'.format(admin_password) if admin_password else ''
site_env = f'CYPRESS_baseUrl={site_url}'
password_env = f'CYPRESS_adminPassword={admin_password}' if admin_password else ''


os.chdir(app_base_path) os.chdir(app_base_path)


node_bin = subprocess.getoutput("npm bin") node_bin = subprocess.getoutput("npm bin")
cypress_path = "{0}/cypress".format(node_bin)
plugin_path = "{0}/../cypress-file-upload".format(node_bin)
cypress_path = f"{node_bin}/cypress"
plugin_path = f"{node_bin}/../cypress-file-upload"
testing_library_path = f"{node_bin}/../@testing-library"


# check if cypress in path...if not, install it. # check if cypress in path...if not, install it.
if not ( if not (
os.path.exists(cypress_path) os.path.exists(cypress_path)
and os.path.exists(plugin_path) and os.path.exists(plugin_path)
and os.path.exists(testing_library_path)
and cint(subprocess.getoutput("npm view cypress version")[:1]) >= 6 and cint(subprocess.getoutput("npm view cypress version")[:1]) >= 6
): ):
# install cypress # install cypress
click.secho("Installing Cypress...", fg="yellow") click.secho("Installing Cypress...", fg="yellow")
frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 --no-lockfile")
frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 @testing-library/cypress@^8 --no-lockfile")


# run for headless mode # run for headless mode
run_or_open = 'run --browser firefox --record' if headless else 'open' run_or_open = 'run --browser firefox --record' if headless else 'open'
@@ -617,7 +632,7 @@ def run_ui_tests(context, app, headless=False, parallel=True, ci_build_id=None):
formatted_command += ' --parallel' formatted_command += ' --parallel'


if ci_build_id: if ci_build_id:
formatted_command += ' --ci-build-id {}'.format(ci_build_id)
formatted_command += f' --ci-build-id {ci_build_id}'


click.secho("Running Cypress...", fg="yellow") click.secho("Running Cypress...", fg="yellow")
frappe.commands.popen(formatted_command, cwd=app_base_path, raise_err=True) frappe.commands.popen(formatted_command, cwd=app_base_path, raise_err=True)


+ 2
- 2
frappe/contacts/address_and_contact.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE


import frappe import frappe




+ 1
- 1
frappe/contacts/doctype/address/address.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors # Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe




+ 1
- 1
frappe/contacts/doctype/address/test_address.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors # Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe, unittest import frappe, unittest
from frappe.contacts.doctype.address.address import get_address_display from frappe.contacts.doctype.address.address import get_address_display




+ 1
- 1
frappe/contacts/doctype/address_template/address_template.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors # Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/contacts/doctype/address_template/test_address_template.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors # Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe, unittest import frappe, unittest


class TestAddressTemplate(unittest.TestCase): class TestAddressTemplate(unittest.TestCase):


+ 2
- 2
frappe/contacts/doctype/contact/contact.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
import frappe import frappe
from frappe.utils import cstr, has_gravatar from frappe.utils import cstr, has_gravatar
from frappe import _ from frappe import _


+ 1
- 1
frappe/contacts/doctype/contact/test_contact.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and Contributors # Copyright (c) 2017, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe import frappe
import unittest import unittest




+ 1
- 1
frappe/contacts/doctype/contact_email/contact_email.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


# import frappe # import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/contacts/doctype/contact_phone/contact_phone.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


# import frappe # import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/contacts/doctype/gender/gender.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and contributors # Copyright (c) 2017, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


from frappe.model.document import Document from frappe.model.document import Document




+ 1
- 1
frappe/contacts/doctype/gender/test_gender.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and Contributors # Copyright (c) 2017, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import unittest import unittest


class TestGender(unittest.TestCase): class TestGender(unittest.TestCase):


+ 1
- 1
frappe/contacts/doctype/salutation/salutation.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and contributors # Copyright (c) 2017, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


from frappe.model.document import Document from frappe.model.document import Document




+ 1
- 1
frappe/contacts/doctype/salutation/test_salutation.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and Contributors # Copyright (c) 2017, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import unittest import unittest


class TestSalutation(unittest.TestCase): class TestSalutation(unittest.TestCase):


+ 1
- 1
frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE
import frappe import frappe
from frappe import _ from frappe import _




+ 1
- 1
frappe/core/__init__.py View File

@@ -1,2 +1,2 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE

+ 1
- 1
frappe/core/doctype/__init__.py View File

@@ -1,3 +1,3 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE



+ 3
- 2
frappe/core/doctype/access_log/access_log.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE
import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


@@ -29,4 +29,5 @@ def make_access_log(doctype=None, document=None, method=None, file_type=None,
doc.insert(ignore_permissions=True) doc.insert(ignore_permissions=True)


# `frappe.db.commit` added because insert doesnt `commit` when called in GET requests like `printview` # `frappe.db.commit` added because insert doesnt `commit` when called in GET requests like `printview`
frappe.db.commit()
if frappe.request and frappe.request.method == 'GET':
frappe.db.commit()

+ 1
- 1
frappe/core/doctype/access_log/test_access_log.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors # Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE


# imports - standard imports # imports - standard imports
import unittest import unittest


+ 1
- 1
frappe/core/doctype/activity_log/activity_log.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies and contributors # Copyright (c) 2017, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


from frappe import _ from frappe import _
from frappe.utils import get_fullname, now from frappe.utils import get_fullname, now


+ 1
- 1
frappe/core/doctype/activity_log/feed.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: See license.txt
# License: MIT. See LICENSE


import frappe import frappe
import frappe.permissions import frappe.permissions


+ 1
- 1
frappe/core/doctype/activity_log/test_activity_log.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors # Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe import frappe
import unittest import unittest
import time import time


+ 1
- 1
frappe/core/doctype/block_module/block_module.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/core/doctype/comment/comment.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE
import frappe import frappe
from frappe import _ from frappe import _
import json import json


+ 3
- 3
frappe/core/doctype/comment/test_comment.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors # Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe, json import frappe, json
import unittest import unittest


@@ -30,7 +30,7 @@ class TestComment(unittest.TestCase):
from frappe.website.doctype.blog_post.test_blog_post import make_test_blog from frappe.website.doctype.blog_post.test_blog_post import make_test_blog
test_blog = make_test_blog() test_blog = make_test_blog()


frappe.db.sql("delete from `tabComment` where reference_doctype = 'Blog Post'")
frappe.db.delete("Comment", {"reference_doctype": "Blog Post"})


from frappe.templates.includes.comments.comments import add_comment from frappe.templates.includes.comments.comments import add_comment
add_comment('Good comment with 10 chars', 'test@test.com', 'Good Tester', add_comment('Good comment with 10 chars', 'test@test.com', 'Good Tester',
@@ -41,7 +41,7 @@ class TestComment(unittest.TestCase):
reference_name = test_blog.name reference_name = test_blog.name
))[0].published, 1) ))[0].published, 1)


frappe.db.sql("delete from `tabComment` where reference_doctype = 'Blog Post'")
frappe.db.delete("Comment", {"reference_doctype": "Blog Post"})


add_comment('pleez vizits my site http://mysite.com', 'test@test.com', 'bad commentor', add_comment('pleez vizits my site http://mysite.com', 'test@test.com', 'bad commentor',
'Blog Post', test_blog.name, test_blog.route) 'Blog Post', test_blog.name, test_blog.route)


+ 1
- 1
frappe/core/doctype/communication/__init__.py View File

@@ -1,3 +1,3 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE



+ 1
- 1
frappe/core/doctype/communication/communication.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE


from collections import Counter from collections import Counter
import frappe import frappe


+ 1
- 1
frappe/core/doctype/communication/email.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE


import frappe import frappe
import json import json


+ 1
- 1
frappe/core/doctype/communication/test_communication.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# License: MIT. See LICENSE
import unittest import unittest
from urllib.parse import quote from urllib.parse import quote




+ 1
- 1
frappe/core/doctype/communication_link/communication_link.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/core/doctype/custom_docperm/custom_docperm.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors # Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/core/doctype/custom_docperm/test_custom_docperm.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors # Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe import frappe
import unittest import unittest




+ 1
- 1
frappe/core/doctype/custom_role/custom_role.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and contributors # Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document


+ 1
- 1
frappe/core/doctype/custom_role/test_custom_role.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors # Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe import frappe
import unittest import unittest




+ 1
- 1
frappe/core/doctype/data_export/data_export.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


from frappe.model.document import Document from frappe.model.document import Document




+ 1
- 1
frappe/core/doctype/data_export/exporter.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE


import frappe import frappe
from frappe import _ from frappe import _


+ 1
- 1
frappe/core/doctype/data_import/data_import.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors # Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE


import os import os




+ 1
- 1
frappe/core/doctype/data_import/exporter.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE


import typing import typing




+ 1
- 1
frappe/core/doctype/data_import/importer.py View File

@@ -1,5 +1,5 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE


import os import os
import io import io


+ 1
- 1
frappe/core/doctype/data_import/test_data_import.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and Contributors # Copyright (c) 2020, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
# import frappe # import frappe
import unittest import unittest




+ 1
- 1
frappe/core/doctype/data_import/test_exporter.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors # Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import unittest import unittest
import frappe import frappe
from frappe.core.doctype.data_import.exporter import Exporter from frappe.core.doctype.data_import.exporter import Exporter


+ 1
- 1
frappe/core/doctype/data_import/test_importer.py View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors # Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import unittest import unittest
import frappe import frappe
from frappe.core.doctype.data_import.importer import Importer from frappe.core.doctype.data_import.importer import Importer


+ 1
- 1
frappe/core/doctype/defaultvalue/__init__.py View File

@@ -1,3 +1,3 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# License: MIT. See LICENSE



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

Loading…
Cancel
Save