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

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

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

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

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

# Updating license headers
34460265554242a8d05fb09f049033b1117e1a2b

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

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

if response.ok:
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 docs_link_exists(body):


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

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

set -e

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

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


+ 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 re
import shlex
import subprocess
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):
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):
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):
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__":
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]


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

jobs:
test:
runs-on: ubuntu-18.04
@@ -26,10 +31,21 @@ jobs:
with:
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
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts

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

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

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

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

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

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

- name: Run Patch Tests
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: |
cd ~/frappe-bench/
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:
branches: [ develop ]

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


jobs:
test:
runs-on: ubuntu-18.04
@@ -35,17 +40,29 @@ jobs:
with:
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
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
with:
node-version: 14
check-latest: true

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

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

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

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

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

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

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

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

- name: Upload Coverage Data
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:
pull_request:
workflow_dispatch:
push:
branches: [ develop ]

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

jobs:
test:
@@ -37,17 +43,29 @@ jobs:
with:
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
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
with:
node-version: '14'
check-latest: true

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

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

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

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

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

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

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

- 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:
CI_BUILD_ID: ${{ github.run_id }}
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:
branches: [ develop ]

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

jobs:
test:
runs-on: ubuntu-18.04
@@ -35,17 +39,29 @@ jobs:
with:
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
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
with:
node-version: 14
check-latest: true

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

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

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

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

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

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

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

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

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

- 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
env:
CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb

+ 16
- 0
.mergify.yml View File

@@ -1,4 +1,20 @@
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
conditions:
- status-success=Sider


+ 4
- 1
CODEOWNERS View File

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

+ 1
- 1
LICENSE View File

@@ -1,6 +1,6 @@
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
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'>
<img src='https://www.codetriage.com/frappe/frappe/badges/users.svg'>
</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>
</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', () => {
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');

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

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

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

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

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

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

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

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', () => {
get_dialog_with_barcode().as('dialog');

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

cy.get('.frappe-control[data-fieldname=barcode] input')
cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox')
.focus()
.type('123456789')
.blur();
cy.get('.frappe-control[data-fieldname=barcode] input')
cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox')
.clear()
.blur();
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', () => {
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('.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 => {
let value = dialog.get_value('icon');
expect(value).to.equal('active');
});

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 => {
let value = dialog.get_value('icon');
expect(value).to.equal('resting');
@@ -36,14 +36,14 @@ context('Control Icon', () => {

it('search for icon and clear search input', () => {
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[id*='${search_text}']`).then(icons => {
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');
});


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

@@ -35,7 +35,7 @@ context('Control Link', () => {
cy.wait('@search_link');
cy.get('@input').type('todo for link', { delay: 200 });
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').blur();
cy.get('@dialog').then(dialog => {
@@ -71,7 +71,7 @@ context('Control Link', () => {
cy.get('@input').type(todos[0]).blur();
cy.wait('@validate_link');
cy.get('@input').focus();
cy.get('.frappe-control[data-fieldname=link] .link-btn')
cy.findByTitle('Open Link')
.should('be.visible')
.click();
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('.placeholder').should('have.css', 'display', 'block');
cy.get('@select').select('Option 1');
cy.findByDisplayValue('Option 1').should('exist');
cy.get('@control').get('.placeholder').should('have.css', 'display', 'none');
cy.get('@select').invoke('val', '');
cy.findByDisplayValue('Option 1').should('not.exist');
cy.get('@control').get('.placeholder').should('have.css', 'display', 'block');




+ 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', () => {
cy.new_form('Test Depends On');
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.hide_dialog();
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');
});
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('test_field', 'Some Other Value');
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('@row1').find('.btn-open-row').click();
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.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.get('.modal:visible').should('not.exist');
});
@@ -33,11 +33,11 @@ context('FileUploader', () => {
it('should accept uploaded files', () => {
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.get_open_dialog().find('.btn-primary').click();
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
cy.wait('@upload_file').its('response.body.message')
.should('have.property', 'file_name', 'example.json');
cy.get('.modal:visible').should('not.exist');
@@ -46,10 +46,12 @@ context('FileUploader', () => {
it('should accept web links', () => {
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.get_open_dialog().find('.btn-primary').click();
cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click();
cy.wait('@upload_file').its('response.body.message')
.should('have.property', 'file_url', 'https://github.com');
cy.get('.modal:visible').should('not.exist');
@@ -62,15 +64,14 @@ context('FileUploader', () => {
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('.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.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.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.add_filter();
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.get('.prev-doc').should('be.visible').click();
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 = () => {
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.wait(500);
cy.url().should('include', '/app/contact');
@@ -20,10 +20,10 @@ context('Form Tour', () => {
it('navigates a 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('@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
cy.get('@next_btn').click();
@@ -39,7 +39,7 @@ context('Form Tour', () => {
// assert field is highlighted
cy.get('.frappe-control[data-fieldname="last_name"]').as('last_name');
cy.get('@last_name').should('have.class', 'driver-highlighted-element');
// after filling the field, next step should be highlighted
cy.fill_field('last_name', 'Test Last Name', 'Data');
cy.wait(500);
@@ -49,12 +49,12 @@ context('Form Tour', () => {
// assert field is highlighted
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('phone_nos');
cy.get('@phone_nos').should('have.class', 'driver-highlighted-element');
// move to next step
cy.wait(500);
cy.get('@next_btn').click();
cy.wait(500);
// assert add row btn is highlighted
cy.get('@phone_nos').find('.grid-add-row').as('add_row');
cy.get('@add_row').should('have.class', 'driver-highlighted-element');
@@ -68,21 +68,21 @@ context('Form Tour', () => {
cy.get('.grid-row-open .frappe-control[data-fieldname="phone"]').as('phone');
cy.get('@phone').should('have.class', 'driver-highlighted-element');
// enter value in a table field
cy.fill_table_field('phone_nos', '1', 'phone', '1234567890');
let field = cy.fill_table_field('phone_nos', '1', 'phone', '1234567890');
field.blur();

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

// collapse row
cy.get('.grid-row-open .grid-collapse-row').click();
cy.wait(500);
// assert save btn is highlighted
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', () => {
cy.visit('/app/contact/Test Contact');
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('.current-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('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('.current-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('.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 });

@@ -29,8 +29,8 @@ context('List View Settings', () => {
cy.get('.menu-btn-group button').click({ force: true });
cy.get('.dropdown-menu li').filter(':visible').contains('List Settings').click();
cy.get('.modal-dialog').should('contain', 'DocType Settings');
cy.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', () => {
cy.get('#login_email').type('Administrator');
cy.get('.btn-login:visible').click();
cy.findByRole('button', {name: 'Login'}).click();
cy.location('pathname').should('eq', '/login');
});

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

@@ -25,8 +25,8 @@ context('Login', () => {
cy.get('#login_email').type('Administrator');
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');
});

@@ -34,7 +34,7 @@ context('Login', () => {
cy.get('#login_email').type('Administrator');
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.window().its('frappe.session.user').should('eq', 'Administrator');
});
@@ -60,7 +60,7 @@ context('Login', () => {
cy.get('#login_email').type('Administrator');
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
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', () => {
cy.visit('/app');
cy.awesomebar('recorder');
cy.get('h3').should('contain', 'Recorder');
cy.findByTitle('Recorder').should('exist');
cy.url().should('include', '/recorder/detail');
});

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('.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 .btn-primary').should('contain', 'Start Recording');
cy.findByRole('button', {name: 'Start Recording'}).should('exist');
});

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

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

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.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');
// select the cell
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.wait('@value-update');
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', () => {
//Adding new 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.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.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
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
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
cy.get('.timeline-content').should('contain', 'Testing Timeline');

//Editing comment
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);

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

//Discarding comment
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
cy.get('.timeline-content').should('contain', 'Testing Timeline 123');

//Deleting the added comment
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');

//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', () => {
@@ -64,31 +64,31 @@ context('Timeline', () => {

//Adding a new entry for the created custom doctype
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.get('.list-subject > .bold > .ellipsis').eq(0).click();

//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('.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
cy.get('.timeline-content').should('contain', 'Administrator cancelled this document');

//Deleting the document
cy.visit('/app/custom-submittable-doctype');
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.click_modal_primary_button('Yes', {force: true, delay: 700});

//Deleting the custom doctype
cy.visit('/app/doctype');
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');
});
});

+ 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', () => {
//Adding new 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.click_listview_primary_button('Save');
cy.get('.primary-action').contains('Save').click({force: true});
cy.wait(700);
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
cy.get('.timeline-actions > .btn').click();
@@ -47,7 +46,7 @@ context('Timeline Email', () => {

//Removing the added attachment
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
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
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.wait(500);
cy.get('.timeline-actions > .btn').click();
cy.wait(500);
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
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 '@testing-library/cypress/add-commands';
// ***********************************************
// This example commands.js shows you how to
// 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
# MIT License. See license.txt
# License: MIT. See LICENSE
"""
Frappe - Low Code Open Source Framework in Python and JS

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

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

# end: static analysis hack

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
# MIT License. See license.txt
# License: MIT. See LICENSE
import base64
import binascii
import json


+ 1
- 1
frappe/app.py View File

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

import os
import logging


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

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

import frappe
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 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe
import unittest
from frappe.utils import random_string
@@ -76,7 +76,7 @@ class TestAutoAssign(unittest.TestCase):
# clear 5 assignments for first user
# 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):
frappe.db.sql("delete from tabToDo where name = %s", d.name)
frappe.db.delete("ToDo", {"name": d.name})

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

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')]

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

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

# Add expiry_date 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()

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):
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 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE

# import frappe
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 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE

# import frappe
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) {
// auto repeat message
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]));
}



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

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

import frappe
from frappe import _


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

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

import frappe


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

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

# import frappe
from frappe.model.document import Document


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

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

import frappe
from frappe.model.document import Document


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

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



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

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

import frappe
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 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe
import frappe.cache_manager
import unittest

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

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

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

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

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

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

+ 3
- 3
frappe/boot.py View File

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

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.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
# MIT License. See license.txt
# License: MIT. See LICENSE
import os
import re
import json


+ 1
- 1
frappe/cache_manager.py View File

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

import frappe, json
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 -*-
# Copyright (c) 2018, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE

import frappe
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
# MIT License. See license.txt
# License: MIT. See LICENSE
import frappe
from frappe import _
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
# MIT License. See license.txt
# License: MIT. See LICENSE

import sys
import click


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

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


@click.command('console')
@click.option(
'--autoreload',
is_flag=True,
help="Reload changes to code automatically"
)
@pass_context
def console(context):
def console(context, autoreload=False):
"Start ipython console for a site"
site = get_site(context)
frappe.init(site=site)
frappe.connect()
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()
failed_to_import = []

@@ -509,7 +520,9 @@ def console(context):
if 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')
@@ -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-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('--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
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,
@@ -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

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

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.
if not (
os.path.exists(cypress_path)
and os.path.exists(plugin_path)
and os.path.exists(testing_library_path)
and cint(subprocess.getoutput("npm view cypress version")[:1]) >= 6
):
# install cypress
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_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'

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



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

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

import frappe



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

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe, unittest
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 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE

import frappe
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 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe, unittest

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
from frappe.utils import cstr, has_gravatar
from frappe import _


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

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



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

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

# import frappe
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 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE

# import frappe
from frappe.model.document import Document


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

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

from frappe.model.document import Document



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

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

class TestGender(unittest.TestCase):


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

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

from frappe.model.document import Document



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

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

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
# For license information, please see license.txt
# License: MIT. See LICENSE
import frappe
from frappe import _



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

@@ -1,2 +1,2 @@
# 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
# 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
# For license information, please see license.txt
# License: MIT. See LICENSE
import frappe
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)

# `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 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE

# imports - standard imports
import unittest


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

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

from frappe import _
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
# License: See license.txt
# License: MIT. See LICENSE

import frappe
import frappe.permissions


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

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


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

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

import frappe
from frappe.model.document import Document


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

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


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

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

@@ -30,7 +30,7 @@ class TestComment(unittest.TestCase):
from frappe.website.doctype.blog_post.test_blog_post import 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
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
))[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',
'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
# 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
# MIT License. See license.txt
# License: MIT. See LICENSE

from collections import Counter
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
# MIT License. See license.txt
# License: MIT. See LICENSE

import frappe
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
# See license.txt
# License: MIT. See LICENSE
import unittest
from urllib.parse import quote



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

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

import frappe
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 -*-
# Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
# License: MIT. See LICENSE

import frappe
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 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe
import unittest



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

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

import frappe
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 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
# License: MIT. See LICENSE
import frappe
import unittest



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

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# 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



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

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

import frappe
from frappe import _


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

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

import os



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

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

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
# MIT License. See license.txt
# License: MIT. See LICENSE

import os
import io


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

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



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

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