mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-08-03 18:54:40 -05:00
Compare commits
136 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2080fde4f9 | ||
![]() |
d10e67ce09 | ||
![]() |
74fe7c586b | ||
![]() |
05188aed6d | ||
![]() |
865efb7752 | ||
![]() |
4782b4da07 | ||
![]() |
4693632c7d | ||
![]() |
4c4b571a88 | ||
![]() |
328c87995b | ||
![]() |
a1d10e7d4a | ||
![]() |
77d9a7e9d3 | ||
![]() |
981b090088 | ||
![]() |
6d1c788ee0 | ||
![]() |
02de773d5b | ||
![]() |
2a240d83fd | ||
![]() |
25cdf7916d | ||
![]() |
11b5983a0d | ||
![]() |
4964987245 | ||
![]() |
ed129d6074 | ||
![]() |
37e928d869 | ||
![]() |
06def8c11e | ||
![]() |
10571676a4 | ||
![]() |
af5160237d | ||
![]() |
b86842ba73 | ||
![]() |
bfc271e743 | ||
![]() |
ee88140fdd | ||
![]() |
51249a1dce | ||
![]() |
57ec9e6b13 | ||
![]() |
1324d17d87 | ||
![]() |
26b438a888 | ||
![]() |
70f3f98363 | ||
![]() |
71e4be2d5e | ||
![]() |
5740806a28 | ||
![]() |
9b50a1b7a6 | ||
![]() |
19caad832e | ||
![]() |
dd6ae13281 | ||
![]() |
077abbe961 | ||
![]() |
3d85dc1127 | ||
![]() |
e3ea5dd13c | ||
![]() |
714b2ecd9c | ||
![]() |
883937bfd7 | ||
![]() |
0ebe08d796 | ||
![]() |
36b4fff5c7 | ||
![]() |
0684c8c388 | ||
![]() |
67744c877d | ||
![]() |
45d8c945e2 | ||
![]() |
ee19307ea2 | ||
![]() |
2c1cd25be4 | ||
![]() |
6e65558ea4 | ||
![]() |
304324ebd0 | ||
![]() |
97cd06d2ba | ||
![]() |
df948065a3 | ||
![]() |
f92126b44f | ||
![]() |
e329f6cdf1 | ||
![]() |
2c96438d61 | ||
![]() |
41a9aac75d | ||
![]() |
8768168536 | ||
![]() |
325809fbbf | ||
![]() |
3dd47a9f5b | ||
![]() |
00f16ef8f0 | ||
![]() |
5e67aae83b | ||
![]() |
ae5c603c98 | ||
![]() |
c62aa3cb55 | ||
![]() |
7073cb6d5c | ||
![]() |
a495ad58d0 | ||
![]() |
569165371c | ||
![]() |
ea14fa5251 | ||
![]() |
4a02865697 | ||
![]() |
3a2a20cefd | ||
![]() |
f2f42de701 | ||
![]() |
6d60d4897c | ||
![]() |
9f71ce8083 | ||
![]() |
50f8f7da93 | ||
![]() |
d475344b51 | ||
![]() |
2630863409 | ||
![]() |
e120f4a3f7 | ||
![]() |
6aff4c986c | ||
![]() |
2e891b1634 | ||
![]() |
35a0c5d36f | ||
![]() |
82d786b94c | ||
![]() |
de49d602a1 | ||
![]() |
3cb6511b66 | ||
![]() |
1d3ae777d5 | ||
![]() |
dbb2ea39d2 | ||
![]() |
fb607332b9 | ||
![]() |
b956f627b0 | ||
![]() |
2ac64ab573 | ||
![]() |
482e00970c | ||
![]() |
d62340efb5 | ||
![]() |
eb0f35219c | ||
![]() |
243598ae50 | ||
![]() |
74c965d21d | ||
![]() |
30316179a0 | ||
![]() |
f16a1101e6 | ||
![]() |
0466f7a18a | ||
![]() |
97cf3b2079 | ||
![]() |
6542d75a6a | ||
![]() |
c6900c5d51 | ||
![]() |
0e9642ea3e | ||
![]() |
3ab2892066 | ||
![]() |
69b69aca6a | ||
![]() |
a05dbd2e5a | ||
![]() |
c1641f6fb8 | ||
![]() |
452c79f9a1 | ||
![]() |
37959fe31c | ||
![]() |
30f73f39a0 | ||
![]() |
fa613cd5fb | ||
![]() |
b8afb22902 | ||
![]() |
07e07fc7e8 | ||
![]() |
58f95c1891 | ||
![]() |
7ad8e3b3da | ||
![]() |
b9a548758a | ||
![]() |
a436caf2ad | ||
![]() |
5461f8a225 | ||
![]() |
78b747571c | ||
![]() |
0c6a9a189b | ||
![]() |
45d666ff2d | ||
![]() |
4143925322 | ||
![]() |
9be3d2ccaf | ||
![]() |
8be8a310d7 | ||
![]() |
b81c339922 | ||
![]() |
d3d103f141 | ||
![]() |
dd575ccb88 | ||
![]() |
a83c7c64b5 | ||
![]() |
c04ded6fd8 | ||
![]() |
f24c779737 | ||
![]() |
3cdd358fc8 | ||
![]() |
e11939b149 | ||
![]() |
a4ef0702c9 | ||
![]() |
548b6352ca | ||
![]() |
2658c16073 | ||
![]() |
49d0b6aa00 | ||
![]() |
e65f584197 | ||
![]() |
ce1bbda188 | ||
![]() |
846897fb4c | ||
![]() |
457e134261 |
4
.github/stale.yml
vendored
4
.github/stale.yml
vendored
@@ -4,8 +4,8 @@ daysUntilStale: 30
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: [cant-reproduce]
|
||||
# Only issues or pull requests with any of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
any-of-labels: ['cant-reproduce','not a bug','unconfirmed']
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
|
68
.github/workflows/ci.yml
vendored
68
.github/workflows/ci.yml
vendored
@@ -16,7 +16,7 @@ on:
|
||||
env:
|
||||
# This is the version of pipenv all the steps will use
|
||||
# If changing this, change Dockerfile
|
||||
DEFAULT_PIP_ENV_VERSION: "2023.4.20"
|
||||
DEFAULT_PIP_ENV_VERSION: "2023.6.12"
|
||||
# This is the default version of Python to use in most steps
|
||||
# If changing this, change Dockerfile
|
||||
DEFAULT_PYTHON_VERSION: "3.9"
|
||||
@@ -77,6 +77,7 @@ jobs:
|
||||
with:
|
||||
name: documentation
|
||||
path: site/
|
||||
retention-days: 7
|
||||
|
||||
documentation-deploy:
|
||||
name: "Deploy Documentation"
|
||||
@@ -106,15 +107,6 @@ jobs:
|
||||
matrix:
|
||||
python-version: ['3.8', '3.9', '3.10']
|
||||
fail-fast: false
|
||||
env:
|
||||
# Enable Tika end to end testing
|
||||
TIKA_LIVE: 1
|
||||
# Enable paperless_mail testing against real server
|
||||
PAPERLESS_MAIL_TEST_HOST: ${{ secrets.TEST_MAIL_HOST }}
|
||||
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
|
||||
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
|
||||
# Enable Gotenberg end to end testing
|
||||
GOTENBERG_LIVE: 1
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
@@ -156,12 +148,18 @@ jobs:
|
||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
|
||||
-
|
||||
name: Tests
|
||||
env:
|
||||
PAPERLESS_CI_TEST: 1
|
||||
# Enable paperless_mail testing against real server
|
||||
PAPERLESS_MAIL_TEST_HOST: ${{ secrets.TEST_MAIL_HOST }}
|
||||
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
|
||||
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
|
||||
run: |
|
||||
cd src/
|
||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pytest -ra
|
||||
-
|
||||
name: Upload coverage to Codecov
|
||||
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION && github.event_name == 'pull_request'}}
|
||||
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
# not required for public repos, but intermittently fails otherwise
|
||||
@@ -192,10 +190,46 @@ jobs:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'src-ui/package-lock.json'
|
||||
- run: cd src-ui && npm ci
|
||||
- run: cd src-ui && npm run lint
|
||||
- run: cd src-ui && npm run test
|
||||
- run: cd src-ui && npm run e2e:ci
|
||||
-
|
||||
name: Install dependencies
|
||||
run: cd src-ui && npm ci
|
||||
-
|
||||
name: Install Playwright
|
||||
run: npx playwright install --with-deps
|
||||
-
|
||||
name: Linting checks
|
||||
run: cd src-ui && npm run lint
|
||||
-
|
||||
name: Run Jest unit tests
|
||||
run: cd src-ui && npm run test
|
||||
-
|
||||
name: Upload Jest coverage
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: jest-coverage-report
|
||||
path: src-ui/coverage
|
||||
retention-days: 7
|
||||
-
|
||||
name: Upload frontend coverage to Codecov
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
# not required for public repos, but intermittently fails otherwise
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
# future expansion
|
||||
flags: frontend
|
||||
-
|
||||
name: Run Playwright e2e tests
|
||||
run: cd src-ui && npx playwright test
|
||||
-
|
||||
name: Upload Playwright test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: playwright-report
|
||||
path: src-ui/playwright-report
|
||||
retention-days: 7
|
||||
|
||||
build-docker-image:
|
||||
name: Build Docker image for ${{ github.ref_name }}
|
||||
@@ -294,7 +328,7 @@ jobs:
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.docker-meta.outputs.tags }}
|
||||
labels: ${{ steps.docker-meta.outputs.labels }}
|
||||
# Get cache layers from this branch, then dev, then main
|
||||
# Get cache layers from this branch, then dev
|
||||
# This allows new branches to get at least some cache benefits, generally from dev
|
||||
cache-from: |
|
||||
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
||||
@@ -316,6 +350,7 @@ jobs:
|
||||
with:
|
||||
name: frontend-compiled
|
||||
path: src/documents/static/frontend/
|
||||
retention-days: 7
|
||||
|
||||
build-release:
|
||||
needs:
|
||||
@@ -424,6 +459,7 @@ jobs:
|
||||
with:
|
||||
name: release
|
||||
path: dist/paperless-ngx.tar.xz
|
||||
retention-days: 7
|
||||
|
||||
publish-release:
|
||||
runs-on: ubuntu-22.04
|
||||
|
10
.github/workflows/cleanup-tags.yml
vendored
10
.github/workflows/cleanup-tags.yml
vendored
@@ -38,6 +38,7 @@ jobs:
|
||||
scheme: "branch"
|
||||
repo_name: "paperless-ngx"
|
||||
match_regex: "feature-"
|
||||
do_delete: "true"
|
||||
|
||||
cleanup-untagged-images:
|
||||
name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
|
||||
@@ -51,14 +52,6 @@ jobs:
|
||||
include:
|
||||
- primary-name: "paperless-ngx"
|
||||
- primary-name: "paperless-ngx/builder/cache/app"
|
||||
- primary-name: "paperless-ngx/builder/qpdf"
|
||||
- primary-name: "paperless-ngx/builder/cache/qpdf"
|
||||
- primary-name: "paperless-ngx/builder/pikepdf"
|
||||
- primary-name: "paperless-ngx/builder/cache/pikepdf"
|
||||
- primary-name: "paperless-ngx/builder/jbig2enc"
|
||||
- primary-name: "paperless-ngx/builder/cache/jbig2enc"
|
||||
- primary-name: "paperless-ngx/builder/psycopg2"
|
||||
- primary-name: "paperless-ngx/builder/cache/psycopg2"
|
||||
# TODO: Remove the above and replace with the below
|
||||
# - primary-name: "builder/qpdf"
|
||||
# - primary-name: "builder/cache/qpdf"
|
||||
@@ -81,3 +74,4 @@ jobs:
|
||||
owner: "${{ github.repository_owner }}"
|
||||
is_org: "true"
|
||||
package_name: "${{ matrix.primary-name }}"
|
||||
do_delete: "true"
|
||||
|
@@ -37,7 +37,7 @@ repos:
|
||||
exclude: "(^Pipfile\\.lock$)"
|
||||
# Python hooks
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: 'v0.0.265'
|
||||
rev: 'v0.0.272'
|
||||
hooks:
|
||||
- id: ruff
|
||||
- repo: https://github.com/psf/black
|
||||
@@ -57,6 +57,6 @@ repos:
|
||||
args:
|
||||
- "--tab"
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: "v0.9.0.2"
|
||||
rev: "v0.9.0.5"
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
|
31
Dockerfile
31
Dockerfile
@@ -1,11 +1,11 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# syntax=docker/dockerfile:1
|
||||
# https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md
|
||||
|
||||
# Stage: compile-frontend
|
||||
# Purpose: Compiles the frontend
|
||||
# Notes:
|
||||
# - Does NPM stuff with Typescript and such
|
||||
FROM --platform=$BUILDPLATFORM node:16-bullseye-slim AS compile-frontend
|
||||
FROM --platform=$BUILDPLATFORM docker.io/node:16-bookworm-slim AS compile-frontend
|
||||
|
||||
COPY ./src-ui /src/src-ui
|
||||
|
||||
@@ -21,7 +21,7 @@ RUN set -eux \
|
||||
# Comments:
|
||||
# - pipenv dependencies are not left in the final image
|
||||
# - pipenv can't touch the final image somehow
|
||||
FROM --platform=$BUILDPLATFORM python:3.9-alpine as pipenv-base
|
||||
FROM --platform=$BUILDPLATFORM docker.io/python:3.9-alpine as pipenv-base
|
||||
|
||||
WORKDIR /usr/src/pipenv
|
||||
|
||||
@@ -29,7 +29,7 @@ COPY Pipfile* ./
|
||||
|
||||
RUN set -eux \
|
||||
&& echo "Installing pipenv" \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2023.4.20 \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2023.6.12 \
|
||||
&& echo "Generating requirement.txt" \
|
||||
&& pipenv requirements > requirements.txt
|
||||
|
||||
@@ -37,7 +37,7 @@ RUN set -eux \
|
||||
# Purpose: The final image
|
||||
# Comments:
|
||||
# - Don't leave anything extra in here
|
||||
FROM python:3.9-slim-bullseye as main-app
|
||||
FROM docker.io/python:3.9-slim-bookworm as main-app
|
||||
|
||||
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.paperless-ngx.com/"
|
||||
@@ -70,9 +70,9 @@ ARG RUNTIME_PACKAGES="\
|
||||
# Image processing
|
||||
liblept5 \
|
||||
liblcms2-2 \
|
||||
libtiff5 \
|
||||
libtiff6 \
|
||||
libfreetype6 \
|
||||
libwebp6 \
|
||||
libwebp7 \
|
||||
libopenjp2-7 \
|
||||
libimagequant0 \
|
||||
libraqm0 \
|
||||
@@ -98,6 +98,8 @@ ARG RUNTIME_PACKAGES="\
|
||||
libxml2 \
|
||||
libxslt1.1 \
|
||||
libgnutls30 \
|
||||
libqpdf29 \
|
||||
qpdf \
|
||||
# Mime type detection
|
||||
file \
|
||||
libmagic1 \
|
||||
@@ -181,7 +183,7 @@ ARG PSYCOPG2_VERSION=2.9.6
|
||||
RUN set -eux \
|
||||
&& echo "Getting binaries" \
|
||||
&& mkdir paperless-ngx \
|
||||
&& curl --fail --silent --show-error --output paperless-ngx.tar.gz --location https://github.com/paperless-ngx/builder/archive/3d6574e2dbaa8b8cdced864a256b0de59015f605.tar.gz \
|
||||
&& curl --fail --silent --show-error --output paperless-ngx.tar.gz --location https://github.com/paperless-ngx/builder/archive/58bb061b9b3b63009852d6d875f9a305d9ae6ac9.tar.gz \
|
||||
&& tar -xf paperless-ngx.tar.gz --directory paperless-ngx --strip-components=1 \
|
||||
&& cd paperless-ngx \
|
||||
# Setting a specific revision ensures we know what this installed
|
||||
@@ -189,9 +191,7 @@ RUN set -eux \
|
||||
&& echo "Installing jbig2enc" \
|
||||
&& cp ./jbig2enc/${JBIG2ENC_VERSION}/${TARGETARCH}${TARGETVARIANT}/jbig2 /usr/local/bin/ \
|
||||
&& cp ./jbig2enc/${JBIG2ENC_VERSION}/${TARGETARCH}${TARGETVARIANT}/libjbig2enc* /usr/local/lib/ \
|
||||
&& echo "Installing qpdf" \
|
||||
&& apt-get install --yes --no-install-recommends ./qpdf/${QPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/libqpdf29_*.deb \
|
||||
&& apt-get install --yes --no-install-recommends ./qpdf/${QPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/qpdf_*.deb \
|
||||
&& chmod a+x /usr/local/bin/jbig2 \
|
||||
&& echo "Installing pikepdf and dependencies" \
|
||||
&& python3 -m pip install --no-cache-dir ./pikepdf/${PIKEPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/*.whl \
|
||||
&& python3 -m pip list \
|
||||
@@ -214,16 +214,17 @@ COPY --from=pipenv-base /usr/src/pipenv/requirements.txt ./
|
||||
ARG BUILD_PACKAGES="\
|
||||
build-essential \
|
||||
git \
|
||||
default-libmysqlclient-dev \
|
||||
python3-dev"
|
||||
default-libmysqlclient-dev"
|
||||
|
||||
RUN set -eux \
|
||||
# hadolint ignore=DL3042
|
||||
RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
|
||||
set -eux \
|
||||
&& echo "Installing build system packages" \
|
||||
&& apt-get update \
|
||||
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade wheel \
|
||||
&& echo "Installing Python requirements" \
|
||||
&& python3 -m pip install --default-timeout=1000 --no-cache-dir --requirement requirements.txt \
|
||||
&& python3 -m pip install --default-timeout=1000 --requirement requirements.txt \
|
||||
&& echo "Installing NLTK data" \
|
||||
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
|
||||
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \
|
||||
|
6
Pipfile
6
Pipfile
@@ -37,14 +37,13 @@ psycopg2 = "*"
|
||||
rapidfuzz = "*"
|
||||
redis = {extras = ["hiredis"], version = "*"}
|
||||
scikit-learn = "~=1.2"
|
||||
numpy = "*"
|
||||
whitenoise = "~=6.3"
|
||||
watchdog = "~=2.2"
|
||||
whoosh="~=2.7"
|
||||
inotifyrecursive = "~=0.3"
|
||||
ocrmypdf = "~=14.0"
|
||||
tqdm = "*"
|
||||
tika = "*"
|
||||
tika-client = "*"
|
||||
channels = "~=4.0"
|
||||
channels-redis = "*"
|
||||
uvicorn = {extras = ["standard"], version = "*"}
|
||||
@@ -65,6 +64,8 @@ zxing-cpp = {version = "*", platform_machine = "== 'x86_64'"}
|
||||
scipy = "==1.8.1"
|
||||
# v4 brings in extra dependencies for features not used here
|
||||
reportlab = "==3.6.12"
|
||||
# Pin this until piwheels is building a newer version (see https://www.piwheels.org/project/cryptography/)
|
||||
cryptography = "==40.0.1"
|
||||
|
||||
[dev-packages]
|
||||
# Linting
|
||||
@@ -76,6 +77,7 @@ factory-boy = "*"
|
||||
pytest = "*"
|
||||
pytest-cov = "*"
|
||||
pytest-django = "*"
|
||||
pytest-httpx = "*"
|
||||
pytest-env = "*"
|
||||
pytest-sugar = "*"
|
||||
pytest-xdist = "*"
|
||||
|
1621
Pipfile.lock
generated
1621
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -101,14 +101,7 @@ For bugs please [open an issue](https://github.com/paperless-ngx/paperless-ngx/i
|
||||
|
||||
# Affiliated Projects
|
||||
|
||||
Paperless has been around for a while now, and people have built tools that interact with it. If you're one of them, please reach out and we can add your project to the list. Current projects include:
|
||||
|
||||
- **Mobile**
|
||||
- [Paperless App](https://github.com/bauerj/paperless_app): An Android/iOS application for Paperless-ngx.
|
||||
- [Paperless Mobile](https://github.com/astubenbord/paperless-mobile): A modern, feature rich Android app for Paperless-ngx.
|
||||
- [Paperless Share](https://github.com/qcasey/paperless_share): Share any files from your Android application with Paperless-ngx. Very simple, but works with all mobile scanning apps that allow you to share scanned documents.
|
||||
- **Desktop**
|
||||
- [Scan to Paperless](https://github.com/sbrunner/scan-to-paperless): Scan and prepare (crop, deskew, OCR, ...) your documents for use in Paperless-ngx.
|
||||
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Affiliated-Projects) for a user-maintained list of affiliated projects and software that is compatible with Paperless-ngx.
|
||||
|
||||
# Important Note
|
||||
|
||||
|
@@ -1,4 +1,8 @@
|
||||
commit_message: '[ci skip]'
|
||||
pull_request_labels: [
|
||||
"skip-changelog",
|
||||
"translation"
|
||||
]
|
||||
files:
|
||||
- source: /src/locale/en_US/LC_MESSAGES/django.po
|
||||
translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po
|
||||
|
@@ -80,7 +80,7 @@ django_checks() {
|
||||
|
||||
search_index() {
|
||||
|
||||
local -r index_version=5
|
||||
local -r index_version=6
|
||||
local -r index_version_file=${DATA_DIR}/.index_version
|
||||
|
||||
if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then
|
||||
|
@@ -28,7 +28,7 @@ if __name__ == "__main__":
|
||||
except Exception as e:
|
||||
print(
|
||||
f"Redis ping #{attempt} failed.\n"
|
||||
f"Error: {str(e)}.\n"
|
||||
f"Error: {e!s}.\n"
|
||||
f"Waiting {RETRY_SLEEP_SECONDS}s",
|
||||
flush=True,
|
||||
)
|
||||
|
@@ -148,6 +148,13 @@ following:
|
||||
$ pip install -r requirements.txt
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
At times, some dependencies will be removed from requirements.txt.
|
||||
Comparing the versions and removing no longer needed dependencies
|
||||
will keep your system or virtual environment clean and prevent
|
||||
possible conflicts.
|
||||
|
||||
3. Migrate the database.
|
||||
|
||||
```shell-session
|
||||
@@ -296,7 +303,7 @@ will be placed in individual json files, instead of a single JSON file. The main
|
||||
manifest.json will still contain application wide information (e.g. tags, correspondent,
|
||||
documenttype, etc)
|
||||
|
||||
If `-z` or `--zip` is provided, the export will be a zipfile
|
||||
If `-z` or `--zip` is provided, the export will be a zip file
|
||||
in the target directory, named according to the current date.
|
||||
|
||||
!!! warning
|
||||
|
@@ -488,7 +488,7 @@ database to be case sensitive. This would prevent a user from creating a
|
||||
tag `Name` and `NAME` as they are considered the same.
|
||||
|
||||
Per Django documentation, to enable this requires manual intervention.
|
||||
To enable case sensetive tables, you can execute the following command
|
||||
To enable case sensitive tables, you can execute the following command
|
||||
against each table:
|
||||
|
||||
`ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`
|
||||
@@ -508,7 +508,7 @@ existing tables) with:
|
||||
|
||||
Paperless is able to utilize barcodes for automatically preforming some tasks.
|
||||
|
||||
At this time, the library utilized for detection of bacodes supports the following types:
|
||||
At this time, the library utilized for detection of barcodes supports the following types:
|
||||
|
||||
- AN-13/UPC-A
|
||||
- UPC-E
|
||||
|
25
docs/api.md
25
docs/api.md
@@ -47,6 +47,8 @@ fields:
|
||||
Read-only.
|
||||
- `archived_file_name`: Verbose filename of the archived document.
|
||||
Read-only. Null if no archived document is available.
|
||||
- `set_permissions`: Allows setting document permissions. Optional,
|
||||
write-only. See [below](#permissions).
|
||||
|
||||
## Downloading documents
|
||||
|
||||
@@ -267,6 +269,29 @@ However, querying the tasks endpoint with the returned UUID e.g.
|
||||
`/api/tasks/?task_id={uuid}` will provide information on the state of the
|
||||
consumption including the ID of a created document if consumption succeeded.
|
||||
|
||||
## Permissions
|
||||
|
||||
All objects (documents, tags, etc.) allow setting object-level permissions
|
||||
with an optional `set_permissions` parameter which is of the form:
|
||||
|
||||
```
|
||||
{
|
||||
"owner": user_id,
|
||||
"view": {
|
||||
"users": [...],
|
||||
"groups": [...],
|
||||
},
|
||||
"change": {
|
||||
"users": [...],
|
||||
"groups": [...],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If this parameter is supplied the object's permissions will be overwritten,
|
||||
assuming the authenticated user has permission to do so (the user must be
|
||||
the object owner or a superuser).
|
||||
|
||||
## API Versioning
|
||||
|
||||
The REST API is versioned since Paperless-ngx 1.3.0.
|
||||
|
@@ -1,5 +1,185 @@
|
||||
# Changelog
|
||||
|
||||
## paperless-ngx 1.16.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: PIL ImportError on ARM devices with Docker [@stumpylog](https://github.com/stumpylog) ([#3605](https://github.com/paperless-ngx/paperless-ngx/pull/3605))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Chore: Enable the image cleanup action [@stumpylog](https://github.com/stumpylog) ([#3606](https://github.com/paperless-ngx/paperless-ngx/pull/3606))
|
||||
|
||||
## paperless-ngx 1.16.0
|
||||
|
||||
### Notable Changes
|
||||
|
||||
- Chore: Update base image to Debian bookworm [@stumpylog](https://github.com/stumpylog) ([#3469](https://github.com/paperless-ngx/paperless-ngx/pull/3469))
|
||||
|
||||
### Features
|
||||
|
||||
- Feature: Update to a simpler Tika library [@stumpylog](https://github.com/stumpylog) ([#3517](https://github.com/paperless-ngx/paperless-ngx/pull/3517))
|
||||
- Feature: Allow to filter documents by original filename and checksum [@jayme-github](https://github.com/jayme-github) ([#3485](https://github.com/paperless-ngx/paperless-ngx/pull/3485))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: return user first / last name from backend [@shamoon](https://github.com/shamoon) ([#3579](https://github.com/paperless-ngx/paperless-ngx/pull/3579))
|
||||
- Fix use of `PAPERLESS_DB_TIMEOUT` for all db types [@shamoon](https://github.com/shamoon) ([#3576](https://github.com/paperless-ngx/paperless-ngx/pull/3576))
|
||||
- Fix: handle mail rules with no filters on some imap servers [@shamoon](https://github.com/shamoon) ([#3554](https://github.com/paperless-ngx/paperless-ngx/pull/3554))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Chore: Python dependency updates (celery 5.3.0 in particular) [@stumpylog](https://github.com/stumpylog) ([#3584](https://github.com/paperless-ngx/paperless-ngx/pull/3584))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>8 changes</summary>
|
||||
|
||||
- Chore: Python dependency updates (celery 5.3.0 in particular) [@stumpylog](https://github.com/stumpylog) ([#3584](https://github.com/paperless-ngx/paperless-ngx/pull/3584))
|
||||
- Fix: return user first / last name from backend [@shamoon](https://github.com/shamoon) ([#3579](https://github.com/paperless-ngx/paperless-ngx/pull/3579))
|
||||
- Fix use of `PAPERLESS_DB_TIMEOUT` for all db types [@shamoon](https://github.com/shamoon) ([#3576](https://github.com/paperless-ngx/paperless-ngx/pull/3576))
|
||||
- Fix: handle mail rules with no filters on some imap servers [@shamoon](https://github.com/shamoon) ([#3554](https://github.com/paperless-ngx/paperless-ngx/pull/3554))
|
||||
- Chore: Copy file stats from original file [@stumpylog](https://github.com/stumpylog) ([#3551](https://github.com/paperless-ngx/paperless-ngx/pull/3551))
|
||||
- Chore: Adds test for barcode ASN when it already exists [@stumpylog](https://github.com/stumpylog) ([#3550](https://github.com/paperless-ngx/paperless-ngx/pull/3550))
|
||||
- Feature: Update to a simpler Tika library [@stumpylog](https://github.com/stumpylog) ([#3517](https://github.com/paperless-ngx/paperless-ngx/pull/3517))
|
||||
- Feature: Allow to filter documents by original filename and checksum [@jayme-github](https://github.com/jayme-github) ([#3485](https://github.com/paperless-ngx/paperless-ngx/pull/3485))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 1.15.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix incorrect colors in v1.15.0 [@shamoon](https://github.com/shamoon) ([#3523](https://github.com/paperless-ngx/paperless-ngx/pull/3523))
|
||||
|
||||
### All App Changes
|
||||
|
||||
- Fix incorrect colors in v1.15.0 [@shamoon](https://github.com/shamoon) ([#3523](https://github.com/paperless-ngx/paperless-ngx/pull/3523))
|
||||
|
||||
## paperless-ngx 1.15.0
|
||||
|
||||
### Features
|
||||
|
||||
- Feature: quick filters from document detail [@shamoon](https://github.com/shamoon) ([#3476](https://github.com/paperless-ngx/paperless-ngx/pull/3476))
|
||||
- Feature: Add explanations to relative dates [@shamoon](https://github.com/shamoon) ([#3471](https://github.com/paperless-ngx/paperless-ngx/pull/3471))
|
||||
- Enhancement: paginate frontend tasks [@shamoon](https://github.com/shamoon) ([#3445](https://github.com/paperless-ngx/paperless-ngx/pull/3445))
|
||||
- Feature: Better encapsulation of barcode logic [@stumpylog](https://github.com/stumpylog) ([#3425](https://github.com/paperless-ngx/paperless-ngx/pull/3425))
|
||||
- Enhancement: Improve frontend error handling [@shamoon](https://github.com/shamoon) ([#3413](https://github.com/paperless-ngx/paperless-ngx/pull/3413))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: KeyError error on unauthenticated API calls \& persist authentication when enabled [@ajgon](https://github.com/ajgon) ([#3516](https://github.com/paperless-ngx/paperless-ngx/pull/3516))
|
||||
- Fix: exclude consumer \& AnonymousUser users from export manifest [@shamoon](https://github.com/shamoon) ([#3487](https://github.com/paperless-ngx/paperless-ngx/pull/3487))
|
||||
- Fix: prevent date suggestion search if disabled [@shamoon](https://github.com/shamoon) ([#3472](https://github.com/paperless-ngx/paperless-ngx/pull/3472))
|
||||
- Sync Pipfile.lock based on latest Pipfile [@adamantike](https://github.com/adamantike) ([#3475](https://github.com/paperless-ngx/paperless-ngx/pull/3475))
|
||||
- Fix: DocumentSerializer should return correct original filename [@jayme-github](https://github.com/jayme-github) ([#3473](https://github.com/paperless-ngx/paperless-ngx/pull/3473))
|
||||
- consumer.py: read from original file (instead of temp copy) [@chrisblech](https://github.com/chrisblech) ([#3466](https://github.com/paperless-ngx/paperless-ngx/pull/3466))
|
||||
- Bugfix: Catch an nltk AttributeError and handle it [@stumpylog](https://github.com/stumpylog) ([#3453](https://github.com/paperless-ngx/paperless-ngx/pull/3453))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Adding doc on how to setup Fail2ban [@GuillaumeHullin](https://github.com/GuillaumeHullin) ([#3414](https://github.com/paperless-ngx/paperless-ngx/pull/3414))
|
||||
- Docs: Fix typo [@MarcelBochtler](https://github.com/MarcelBochtler) ([#3437](https://github.com/paperless-ngx/paperless-ngx/pull/3437))
|
||||
- [Documentation] Move nginx [@shamoon](https://github.com/shamoon) ([#3420](https://github.com/paperless-ngx/paperless-ngx/pull/3420))
|
||||
- Documentation: Note possible dependency removal for bare metal [@stumpylog](https://github.com/stumpylog) ([#3408](https://github.com/paperless-ngx/paperless-ngx/pull/3408))
|
||||
|
||||
### Development
|
||||
|
||||
- Development: migrate frontend tests to playwright [@shamoon](https://github.com/shamoon) ([#3401](https://github.com/paperless-ngx/paperless-ngx/pull/3401))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>10 changes</summary>
|
||||
|
||||
- Bump eslint from 8.39.0 to 8.41.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3513](https://github.com/paperless-ngx/paperless-ngx/pull/3513))
|
||||
- Bump concurrently from 8.0.1 to 8.1.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3510](https://github.com/paperless-ngx/paperless-ngx/pull/3510))
|
||||
- Bump [@<!---->ng-bootstrap/ng-bootstrap from 14.1.0 to 14.2.0 in /src-ui @dependabot](https://github.com/<!---->ng-bootstrap/ng-bootstrap from 14.1.0 to 14.2.0 in /src-ui @dependabot) ([#3507](https://github.com/paperless-ngx/paperless-ngx/pull/3507))
|
||||
- Bump [@<!---->popperjs/core from 2.11.7 to 2.11.8 in /src-ui @dependabot](https://github.com/<!---->popperjs/core from 2.11.7 to 2.11.8 in /src-ui @dependabot) ([#3508](https://github.com/paperless-ngx/paperless-ngx/pull/3508))
|
||||
- Bump [@<!---->typescript-eslint/parser from 5.59.2 to 5.59.8 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/parser from 5.59.2 to 5.59.8 in /src-ui @dependabot) ([#3505](https://github.com/paperless-ngx/paperless-ngx/pull/3505))
|
||||
- Bump bootstrap from 5.2.3 to 5.3.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3497](https://github.com/paperless-ngx/paperless-ngx/pull/3497))
|
||||
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.59.2 to 5.59.8 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.59.2 to 5.59.8 in /src-ui @dependabot) ([#3500](https://github.com/paperless-ngx/paperless-ngx/pull/3500))
|
||||
- Bump tslib from 2.5.0 to 2.5.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#3501](https://github.com/paperless-ngx/paperless-ngx/pull/3501))
|
||||
- Bump [@<!---->types/node from 18.16.3 to 20.2.5 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.16.3 to 20.2.5 in /src-ui @dependabot) ([#3498](https://github.com/paperless-ngx/paperless-ngx/pull/3498))
|
||||
- Bump [@<!---->playwright/test from 1.33.0 to 1.34.3 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.33.0 to 1.34.3 in /src-ui @dependabot) ([#3499](https://github.com/paperless-ngx/paperless-ngx/pull/3499))
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>22 changes</summary>
|
||||
|
||||
- Fix: KeyError error on unauthenticated API calls \& persist authentication when enabled [@ajgon](https://github.com/ajgon) ([#3516](https://github.com/paperless-ngx/paperless-ngx/pull/3516))
|
||||
- Bump eslint from 8.39.0 to 8.41.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3513](https://github.com/paperless-ngx/paperless-ngx/pull/3513))
|
||||
- Bump concurrently from 8.0.1 to 8.1.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3510](https://github.com/paperless-ngx/paperless-ngx/pull/3510))
|
||||
- Bump [@<!---->ng-bootstrap/ng-bootstrap from 14.1.0 to 14.2.0 in /src-ui @dependabot](https://github.com/<!---->ng-bootstrap/ng-bootstrap from 14.1.0 to 14.2.0 in /src-ui @dependabot) ([#3507](https://github.com/paperless-ngx/paperless-ngx/pull/3507))
|
||||
- Bump [@<!---->popperjs/core from 2.11.7 to 2.11.8 in /src-ui @dependabot](https://github.com/<!---->popperjs/core from 2.11.7 to 2.11.8 in /src-ui @dependabot) ([#3508](https://github.com/paperless-ngx/paperless-ngx/pull/3508))
|
||||
- Bump [@<!---->typescript-eslint/parser from 5.59.2 to 5.59.8 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/parser from 5.59.2 to 5.59.8 in /src-ui @dependabot) ([#3505](https://github.com/paperless-ngx/paperless-ngx/pull/3505))
|
||||
- Bump bootstrap from 5.2.3 to 5.3.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3497](https://github.com/paperless-ngx/paperless-ngx/pull/3497))
|
||||
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.59.2 to 5.59.8 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.59.2 to 5.59.8 in /src-ui @dependabot) ([#3500](https://github.com/paperless-ngx/paperless-ngx/pull/3500))
|
||||
- Bump tslib from 2.5.0 to 2.5.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#3501](https://github.com/paperless-ngx/paperless-ngx/pull/3501))
|
||||
- Bump [@<!---->types/node from 18.16.3 to 20.2.5 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.16.3 to 20.2.5 in /src-ui @dependabot) ([#3498](https://github.com/paperless-ngx/paperless-ngx/pull/3498))
|
||||
- Bump [@<!---->playwright/test from 1.33.0 to 1.34.3 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.33.0 to 1.34.3 in /src-ui @dependabot) ([#3499](https://github.com/paperless-ngx/paperless-ngx/pull/3499))
|
||||
- Feature: quick filters from document detail [@shamoon](https://github.com/shamoon) ([#3476](https://github.com/paperless-ngx/paperless-ngx/pull/3476))
|
||||
- Fix: exclude consumer \& AnonymousUser users from export manifest [@shamoon](https://github.com/shamoon) ([#3487](https://github.com/paperless-ngx/paperless-ngx/pull/3487))
|
||||
- Fix: prevent date suggestion search if disabled [@shamoon](https://github.com/shamoon) ([#3472](https://github.com/paperless-ngx/paperless-ngx/pull/3472))
|
||||
- Feature: Add explanations to relative dates [@shamoon](https://github.com/shamoon) ([#3471](https://github.com/paperless-ngx/paperless-ngx/pull/3471))
|
||||
- Fix: DocumentSerializer should return correct original filename [@jayme-github](https://github.com/jayme-github) ([#3473](https://github.com/paperless-ngx/paperless-ngx/pull/3473))
|
||||
- consumer.py: read from original file (instead of temp copy) [@chrisblech](https://github.com/chrisblech) ([#3466](https://github.com/paperless-ngx/paperless-ngx/pull/3466))
|
||||
- Bugfix: Catch an nltk AttributeError and handle it [@stumpylog](https://github.com/stumpylog) ([#3453](https://github.com/paperless-ngx/paperless-ngx/pull/3453))
|
||||
- Chore: Improves the logging mixin and allows it to be typed better [@stumpylog](https://github.com/stumpylog) ([#3451](https://github.com/paperless-ngx/paperless-ngx/pull/3451))
|
||||
- Enhancement: paginate frontend tasks [@shamoon](https://github.com/shamoon) ([#3445](https://github.com/paperless-ngx/paperless-ngx/pull/3445))
|
||||
- Add SSL Support for MariaDB [@kimdre](https://github.com/kimdre) ([#3444](https://github.com/paperless-ngx/paperless-ngx/pull/3444))
|
||||
- Enhancement: Improve frontend error handling [@shamoon](https://github.com/shamoon) ([#3413](https://github.com/paperless-ngx/paperless-ngx/pull/3413))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 1.14.5
|
||||
|
||||
### Features
|
||||
|
||||
- Feature: owner filtering [@shamoon](https://github.com/shamoon) ([#3309](https://github.com/paperless-ngx/paperless-ngx/pull/3309))
|
||||
- Enhancement: dynamic counts include all pages, hide for Any [@shamoon](https://github.com/shamoon) ([#3329](https://github.com/paperless-ngx/paperless-ngx/pull/3329))
|
||||
- Enhancement: save tour completion, hide welcome widget [@shamoon](https://github.com/shamoon) ([#3321](https://github.com/paperless-ngx/paperless-ngx/pull/3321))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: Adds better handling for files with invalid utf8 content [@stumpylog](https://github.com/stumpylog) ([#3387](https://github.com/paperless-ngx/paperless-ngx/pull/3387))
|
||||
- Fix: respect permissions for autocomplete suggestions [@shamoon](https://github.com/shamoon) ([#3359](https://github.com/paperless-ngx/paperless-ngx/pull/3359))
|
||||
- Fix: Transition to new library for finding IPs for failed logins [@stumpylog](https://github.com/stumpylog) ([#3382](https://github.com/paperless-ngx/paperless-ngx/pull/3382))
|
||||
- [Security] Render frontend text as plain text [@shamoon](https://github.com/shamoon) ([#3366](https://github.com/paperless-ngx/paperless-ngx/pull/3366))
|
||||
- Fix: default frontend to current owner, allow setting no owner on create [@shamoon](https://github.com/shamoon) ([#3347](https://github.com/paperless-ngx/paperless-ngx/pull/3347))
|
||||
- Fix: dont perform mail actions when rule filename filter not met [@shamoon](https://github.com/shamoon) ([#3336](https://github.com/paperless-ngx/paperless-ngx/pull/3336))
|
||||
- Fix: permission-aware bulk editing in 1.14.1+ [@shamoon](https://github.com/shamoon) ([#3345](https://github.com/paperless-ngx/paperless-ngx/pull/3345))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Chore: Rework workflows [@stumpylog](https://github.com/stumpylog) ([#3242](https://github.com/paperless-ngx/paperless-ngx/pull/3242))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Chore: Upgrade channels to v4 [@stumpylog](https://github.com/stumpylog) ([#3383](https://github.com/paperless-ngx/paperless-ngx/pull/3383))
|
||||
- Chore: Upgrades Python dependencies to their latest allowed versions [@stumpylog](https://github.com/stumpylog) ([#3365](https://github.com/paperless-ngx/paperless-ngx/pull/3365))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>13 changes</summary>
|
||||
|
||||
- Fix: Adds better handling for files with invalid utf8 content [@stumpylog](https://github.com/stumpylog) ([#3387](https://github.com/paperless-ngx/paperless-ngx/pull/3387))
|
||||
- Fix: respect permissions for autocomplete suggestions [@shamoon](https://github.com/shamoon) ([#3359](https://github.com/paperless-ngx/paperless-ngx/pull/3359))
|
||||
- Chore: Upgrade channels to v4 [@stumpylog](https://github.com/stumpylog) ([#3383](https://github.com/paperless-ngx/paperless-ngx/pull/3383))
|
||||
- Fix: Transition to new library for finding IPs for failed logins [@stumpylog](https://github.com/stumpylog) ([#3382](https://github.com/paperless-ngx/paperless-ngx/pull/3382))
|
||||
- Feature: owner filtering [@shamoon](https://github.com/shamoon) ([#3309](https://github.com/paperless-ngx/paperless-ngx/pull/3309))
|
||||
- [Security] Render frontend text as plain text [@shamoon](https://github.com/shamoon) ([#3366](https://github.com/paperless-ngx/paperless-ngx/pull/3366))
|
||||
- Enhancement: dynamic counts include all pages, hide for Any [@shamoon](https://github.com/shamoon) ([#3329](https://github.com/paperless-ngx/paperless-ngx/pull/3329))
|
||||
- Fix: default frontend to current owner, allow setting no owner on create [@shamoon](https://github.com/shamoon) ([#3347](https://github.com/paperless-ngx/paperless-ngx/pull/3347))
|
||||
- [Fix] Position:fixed for .global-dropzone-overlay [@denilsonsa](https://github.com/denilsonsa) ([#3367](https://github.com/paperless-ngx/paperless-ngx/pull/3367))
|
||||
- Fix: dont perform mail actions when rule filename filter not met [@shamoon](https://github.com/shamoon) ([#3336](https://github.com/paperless-ngx/paperless-ngx/pull/3336))
|
||||
- Enhancement: save tour completion, hide welcome widget [@shamoon](https://github.com/shamoon) ([#3321](https://github.com/paperless-ngx/paperless-ngx/pull/3321))
|
||||
- Fix: permission-aware bulk editing in 1.14.1+ [@shamoon](https://github.com/shamoon) ([#3345](https://github.com/paperless-ngx/paperless-ngx/pull/3345))
|
||||
- Fix: Add proper testing for \*\_\_id\_\_in testing [@shamoon](https://github.com/shamoon) ([#3315](https://github.com/paperless-ngx/paperless-ngx/pull/3315))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 1.14.4
|
||||
|
||||
### Bug Fixes
|
||||
|
@@ -83,21 +83,29 @@ changed here.
|
||||
|
||||
`PAPERLESS_DBSSLMODE=<mode>`
|
||||
|
||||
: SSL mode to use when connecting to PostgreSQL.
|
||||
: SSL mode to use when connecting to PostgreSQL or MariaDB.
|
||||
|
||||
See [the official documentation about
|
||||
sslmode](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
|
||||
Default is `prefer`.
|
||||
See [the official documentation about
|
||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-mode).
|
||||
|
||||
*Note*: SSL mode values differ between PostgreSQL and MariaDB.
|
||||
|
||||
Default is `prefer` for PostgreSQL and `PREFERRED` for MariaDB.
|
||||
|
||||
`PAPERLESS_DBSSLROOTCERT=<ca-path>`
|
||||
|
||||
: SSL root certificate path
|
||||
|
||||
See [the official documentation about
|
||||
sslmode](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
Changes path of `root.crt`.
|
||||
|
||||
See [the official documentation about
|
||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-ca).
|
||||
|
||||
Defaults to unset, using the documented path in the home directory.
|
||||
|
||||
`PAPERLESS_DBSSLCERT=<client-cert-path>`
|
||||
@@ -105,7 +113,11 @@ changed here.
|
||||
: SSL client certificate path
|
||||
|
||||
See [the official documentation about
|
||||
sslmode](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
|
||||
See [the official documentation about
|
||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-cert).
|
||||
|
||||
Changes path of `postgresql.crt`.
|
||||
|
||||
Defaults to unset, using the documented path in the home directory.
|
||||
@@ -115,16 +127,20 @@ changed here.
|
||||
: SSL client key path
|
||||
|
||||
See [the official documentation about
|
||||
sslmode](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
|
||||
See [the official documentation about
|
||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-key).
|
||||
|
||||
Changes path of `postgresql.key`.
|
||||
|
||||
Defaults to unset, using the documented path in the home directory.
|
||||
|
||||
`PAPERLESS_DB_TIMEOUT=<float>`
|
||||
`PAPERLESS_DB_TIMEOUT=<int>`
|
||||
|
||||
: Amount of time for a database connection to wait for the database to
|
||||
unlock. Mostly applicable for an sqlite based installation, consider
|
||||
changing to postgresql if you need to increase this.
|
||||
unlock. Mostly applicable for sqlite based installation. Consider changing
|
||||
to postgresql if you are having concurrency problems with sqlite.
|
||||
|
||||
Defaults to unset, keeping the Django defaults.
|
||||
|
||||
|
@@ -186,7 +186,7 @@ The front end is built using AngularJS. In order to get started, you need Node.j
|
||||
|
||||
2. Make sure that it's on your path.
|
||||
|
||||
3. Install all neccessary modules:
|
||||
3. Install all necessary modules:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
@@ -216,19 +216,18 @@ The front end is built using AngularJS. In order to get started, you need Node.j
|
||||
$ git ls-files -- '*.ts' | xargs pre-commit run prettier --files
|
||||
```
|
||||
|
||||
- Front end testing uses jest and cypress. There is currently a need
|
||||
for significantly more front end tests. Unit tests and e2e tests,
|
||||
- Front end testing uses Jest and Playwright. Unit tests and e2e tests,
|
||||
respectively, can be run non-interactively with:
|
||||
|
||||
```bash
|
||||
$ ng test
|
||||
$ npm run e2e:ci
|
||||
$ npx playwright test
|
||||
```
|
||||
|
||||
- Cypress also includes a UI which can be run with:
|
||||
- Playwright also includes a UI which can be run with:
|
||||
|
||||
```bash
|
||||
$ ./node_modules/.bin/cypress open
|
||||
$ npx playwright test --ui
|
||||
```
|
||||
|
||||
- In order to build the front end and serve it as part of Django, execute:
|
||||
@@ -362,7 +361,7 @@ If you want to build the documentation locally, this is how you do it:
|
||||
|
||||
3. Serve the documentation. This will spin up a
|
||||
copy of the documentation at http://127.0.0.1:8000
|
||||
that will automatically refresh everytime you change
|
||||
that will automatically refresh every time you change
|
||||
something.
|
||||
|
||||
```bash
|
||||
@@ -395,7 +394,7 @@ responsible for:
|
||||
- Retrieving the content from the original
|
||||
- Creating a thumbnail
|
||||
- _optional:_ Retrieving a created date from the original
|
||||
- _optional:_ Creainge an archived document from the original
|
||||
- _optional:_ Creating an archived document from the original
|
||||
|
||||
Custom parsers can be added to Paperless-ngx to support more file types. In
|
||||
order to do that, you need to write the parser itself and announce its
|
||||
|
@@ -27,6 +27,12 @@ system. On Linux, chances are high that this location is
|
||||
files around manually. This folder is meant to be entirely managed by
|
||||
docker and paperless.
|
||||
|
||||
!!! note
|
||||
|
||||
Files consumed from the consumption directory are re-created inside
|
||||
this media directory and are removed from the consumption directory
|
||||
itself.
|
||||
|
||||
## Let's say I want to switch tools in a year. Can I easily move to other systems?
|
||||
|
||||
**A:** Your documents are stored as plain files inside the media folder.
|
||||
@@ -103,7 +109,7 @@ see if it works.
|
||||
|
||||
## _How do I proxy this with NGINX?_
|
||||
|
||||
**A:** See [here](/setup#nginx).
|
||||
**A:** See [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx#nginx).
|
||||
|
||||
## _How do I get WebSocket support with Apache mod_wsgi_?
|
||||
|
||||
|
@@ -483,7 +483,7 @@ supported.
|
||||
in front of gunicorn instead.
|
||||
|
||||
For instructions on how to use nginx for that,
|
||||
[see the instructions below](/setup#nginx).
|
||||
[see the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx#nginx).
|
||||
|
||||
!!! warning
|
||||
|
||||
@@ -559,7 +559,7 @@ Users who installed with the bare-metal route should also update their
|
||||
Git clone to point to `https://github.com/paperless-ngx/paperless-ngx`,
|
||||
e.g. using the command
|
||||
`git remote set-url origin https://github.com/paperless-ngx/paperless-ngx`
|
||||
and then pull the lastest version.
|
||||
and then pull the latest version.
|
||||
|
||||
## Migrating from Paperless
|
||||
|
||||
@@ -862,45 +862,8 @@ For details, refer to [configuration](/configuration).
|
||||
|
||||
# Using nginx as a reverse proxy {#nginx}
|
||||
|
||||
If you want to expose paperless to the internet, you should hide it
|
||||
behind a reverse proxy with SSL enabled.
|
||||
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx#nginx) for user-maintained documentation of using nginx with Paperless-ngx.
|
||||
|
||||
In addition to the usual configuration for SSL, the following
|
||||
configuration is required for paperless to operate:
|
||||
# Enhancing security {#security}
|
||||
|
||||
```nginx
|
||||
http {
|
||||
|
||||
# Adjust as required. This is the maximum size for file uploads.
|
||||
# The default value 1M might be a little too small.
|
||||
client_max_body_size 10M;
|
||||
|
||||
server {
|
||||
|
||||
location / {
|
||||
|
||||
# Adjust host and port as required.
|
||||
proxy_pass http://localhost:8000/;
|
||||
|
||||
# These configuration options are required for WebSockets to work.
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Host $server_name;
|
||||
add_header P3P 'CP=""'; # may not be required in all setups
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `PAPERLESS_URL` configuration variable is also required when using a
|
||||
reverse proxy. Please refer to the [hosting and security](/configuration#hosting-and-security) docs.
|
||||
|
||||
Also read
|
||||
[this](https://channels.readthedocs.io/en/stable/deploying.html#nginx-supervisor-ubuntu),
|
||||
towards the end of the section.
|
||||
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-Security-Tools-with-Paperless-ngx) for user-maintained documentation of how to configure security tools like Fail2ban with Paperless-ngx.
|
||||
|
@@ -69,7 +69,9 @@ following operations on your documents:
|
||||
No matter which options you choose, Paperless will always store the
|
||||
original document that it found in the consumption directory or in the
|
||||
mail and will never overwrite that document. Archived versions are
|
||||
stored alongside the original versions.
|
||||
stored alongside the original versions. Any files found in the
|
||||
consumption directory will stored inside the Paperless-ngx file
|
||||
structure and will not be retained in the consumption directory.
|
||||
|
||||
### The consumption directory
|
||||
|
||||
@@ -77,7 +79,9 @@ The primary method of getting documents into your database is by putting
|
||||
them in the consumption directory. The consumer waits patiently, looking
|
||||
for new additions to this directory. When it finds them,
|
||||
the consumer goes about the process of parsing them with the OCR,
|
||||
indexing what it finds, and storing it in the media directory.
|
||||
indexing what it finds, and storing it in the media directory. You should
|
||||
think of this folder as a temporary location, as files will be re-created
|
||||
inside Paperless-ngx and removed from the consumption folder.
|
||||
|
||||
Getting stuff into this directory is up to you. If you're running
|
||||
Paperless on your local computer, you might just want to drag and drop
|
||||
@@ -88,6 +92,15 @@ Typically, you're looking at an FTP server like
|
||||
[Proftpd](http://www.proftpd.org/) or a Windows folder share with
|
||||
[Samba](https://www.samba.org/).
|
||||
|
||||
!!! warning
|
||||
|
||||
Files found in the consumption directory that are consumed will be
|
||||
removed from the consumption directory and stored inside the
|
||||
Paperless-ngx file structure using any settings / storage paths
|
||||
you have specified. This action is performed as safely as possible
|
||||
but this means it is expected that files in the consumption
|
||||
directory will no longer exist (there) after being consumed.
|
||||
|
||||
### Web UI Upload
|
||||
|
||||
The dashboard has a file drop field to upload documents to paperless.
|
||||
@@ -205,8 +218,10 @@ for details.
|
||||
## Permissions
|
||||
|
||||
As of version 1.14.0 Paperless-ngx added core support for user / group permissions. Permissions is
|
||||
based around an object 'owner' and 'view' and 'edit' permissions can be granted to other users
|
||||
or groups.
|
||||
based around 'global' permissions as well as 'object-level' permissions. Global permissions designate
|
||||
which parts of the application a user can access (e.g. Documents, Tags, Settings) and object-level
|
||||
determine which objects are visible or editable. All objects have an 'owner' and 'view' and 'edit'
|
||||
permissions which can be granted to other users or groups.
|
||||
|
||||
Permissions uses the built-in user model of the backend framework, Django.
|
||||
|
||||
|
3
src-ui/.gitignore
vendored
3
src-ui/.gitignore
vendored
@@ -49,3 +49,6 @@ Thumbs.db
|
||||
# Cypress
|
||||
cypress/videos/**/*
|
||||
cypress/screenshots/**/*
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
|
@@ -147,37 +147,6 @@
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@cypress/schematic:cypress",
|
||||
"options": {
|
||||
"devServerTarget": "paperless-ui:serve",
|
||||
"watch": true,
|
||||
"headless": false
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "paperless-ui:serve:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cypress-run": {
|
||||
"builder": "@cypress/schematic:cypress",
|
||||
"options": {
|
||||
"devServerTarget": "paperless-ui:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "paperless-ui:serve:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cypress-open": {
|
||||
"builder": "@cypress/schematic:cypress",
|
||||
"options": {
|
||||
"watch": true,
|
||||
"headless": false
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
|
@@ -1,14 +0,0 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
videosFolder: 'cypress/videos',
|
||||
video: false,
|
||||
screenshotsFolder: 'cypress/screenshots',
|
||||
fixturesFolder: 'cypress/fixtures',
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
return require('./cypress/plugins/index.ts')(on, config)
|
||||
},
|
||||
baseUrl: 'http://localhost:4200',
|
||||
},
|
||||
})
|
@@ -1,68 +0,0 @@
|
||||
describe('settings', () => {
|
||||
beforeEach(() => {
|
||||
// also uses global fixtures from cypress/support/e2e.ts
|
||||
|
||||
// mock restricted permissions
|
||||
cy.intercept('http://localhost:8000/api/ui_settings/', {
|
||||
fixture: 'ui_settings/settings_restricted.json',
|
||||
})
|
||||
})
|
||||
|
||||
it('should not allow user to edit settings', () => {
|
||||
cy.visit('/dashboard')
|
||||
cy.contains('Settings').should('not.exist')
|
||||
cy.visit('/settings').wait(2000)
|
||||
cy.contains("You don't have permissions to do that").should('exist')
|
||||
})
|
||||
|
||||
it('should not allow user to view documents', () => {
|
||||
cy.visit('/dashboard')
|
||||
cy.contains('Documents').should('not.exist')
|
||||
cy.visit('/documents').wait(2000)
|
||||
cy.contains("You don't have permissions to do that").should('exist')
|
||||
cy.visit('/documents/1').wait(2000)
|
||||
cy.contains("You don't have permissions to do that").should('exist')
|
||||
})
|
||||
|
||||
it('should not allow user to view correspondents', () => {
|
||||
cy.visit('/dashboard')
|
||||
cy.contains('Correspondents').should('not.exist')
|
||||
cy.visit('/correspondents').wait(2000)
|
||||
cy.contains("You don't have permissions to do that").should('exist')
|
||||
})
|
||||
|
||||
it('should not allow user to view tags', () => {
|
||||
cy.visit('/dashboard')
|
||||
cy.contains('Tags').should('not.exist')
|
||||
cy.visit('/tags').wait(2000)
|
||||
cy.contains("You don't have permissions to do that").should('exist')
|
||||
})
|
||||
|
||||
it('should not allow user to view document types', () => {
|
||||
cy.visit('/dashboard')
|
||||
cy.contains('Document Types').should('not.exist')
|
||||
cy.visit('/documenttypes').wait(2000)
|
||||
cy.contains("You don't have permissions to do that").should('exist')
|
||||
})
|
||||
|
||||
it('should not allow user to view storage paths', () => {
|
||||
cy.visit('/dashboard')
|
||||
cy.contains('Storage Paths').should('not.exist')
|
||||
cy.visit('/storagepaths').wait(2000)
|
||||
cy.contains("You don't have permissions to do that").should('exist')
|
||||
})
|
||||
|
||||
it('should not allow user to view logs', () => {
|
||||
cy.visit('/dashboard')
|
||||
cy.contains('Logs').should('not.exist')
|
||||
cy.visit('/logs').wait(2000)
|
||||
cy.contains("You don't have permissions to do that").should('exist')
|
||||
})
|
||||
|
||||
it('should not allow user to view tasks', () => {
|
||||
cy.visit('/dashboard')
|
||||
cy.contains('Tasks').should('not.exist')
|
||||
cy.visit('/tasks').wait(2000)
|
||||
cy.contains("You don't have permissions to do that").should('exist')
|
||||
})
|
||||
})
|
@@ -1,118 +0,0 @@
|
||||
describe('document-detail', () => {
|
||||
beforeEach(() => {
|
||||
// also uses global fixtures from cypress/support/e2e.ts
|
||||
|
||||
this.modifiedDocuments = []
|
||||
|
||||
cy.fixture('documents/documents.json').then((documentsJson) => {
|
||||
cy.intercept(
|
||||
'GET',
|
||||
'http://localhost:8000/api/documents/1/?full_perms=true',
|
||||
(req) => {
|
||||
let response = { ...documentsJson }
|
||||
response = response.results.find((d) => d.id == 1)
|
||||
req.reply(response)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
cy.intercept('PUT', 'http://localhost:8000/api/documents/1/', (req) => {
|
||||
this.modifiedDocuments.push(req.body) // store this for later
|
||||
req.reply({ result: 'OK' })
|
||||
}).as('saveDoc')
|
||||
|
||||
cy.fixture('documents/1/notes.json').then((notesJson) => {
|
||||
cy.intercept(
|
||||
'GET',
|
||||
'http://localhost:8000/api/documents/1/notes/',
|
||||
(req) => {
|
||||
req.reply(notesJson.filter((c) => c.id != 10)) // 3
|
||||
}
|
||||
)
|
||||
|
||||
cy.intercept(
|
||||
'DELETE',
|
||||
'http://localhost:8000/api/documents/1/notes/?id=9',
|
||||
(req) => {
|
||||
req.reply(notesJson.filter((c) => c.id != 9 && c.id != 10)) // 2
|
||||
}
|
||||
)
|
||||
|
||||
cy.intercept(
|
||||
'POST',
|
||||
'http://localhost:8000/api/documents/1/notes/',
|
||||
(req) => {
|
||||
req.reply(notesJson) // 4
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
cy.viewport(1024, 1024)
|
||||
cy.visit('/documents/1/').wait('@ui-settings')
|
||||
})
|
||||
|
||||
it('should activate / deactivate save button when changes are saved', () => {
|
||||
cy.contains('button', 'Save').should('be.disabled')
|
||||
cy.get('app-input-text[formcontrolname="title"]')
|
||||
.type(' additional')
|
||||
.wait(1500) // this delay is for frontend debounce
|
||||
cy.contains('button', 'Save').should('not.be.disabled')
|
||||
})
|
||||
|
||||
it('should warn on unsaved changes', () => {
|
||||
cy.get('app-input-text[formcontrolname="title"]')
|
||||
.type(' additional')
|
||||
.wait(1500) // this delay is for frontend debounce
|
||||
cy.get('button[title="Close"]').click()
|
||||
cy.contains('You have unsaved changes')
|
||||
cy.contains('button', 'Cancel').click().wait(150)
|
||||
cy.contains('button', 'Save').click().wait('@saveDoc').wait(2000) // navigates away after saving
|
||||
cy.contains('You have unsaved changes').should('not.exist')
|
||||
})
|
||||
|
||||
it('should show a mobile preview', () => {
|
||||
cy.viewport(440, 1000)
|
||||
cy.get('a')
|
||||
.contains('Preview')
|
||||
.scrollIntoView({ offset: { top: 150, left: 0 } })
|
||||
.click()
|
||||
cy.get('pdf-viewer').should('be.visible')
|
||||
})
|
||||
|
||||
it('should show a list of notes', () => {
|
||||
cy.wait(1000).get('a').contains('Notes').click({ force: true }).wait(1000)
|
||||
cy.get('app-document-notes').find('.card').its('length').should('eq', 3)
|
||||
})
|
||||
|
||||
it('should support note deletion', () => {
|
||||
cy.wait(1000).get('a').contains('Notes').click().wait(1000)
|
||||
cy.get('app-document-notes')
|
||||
.find('.card')
|
||||
.first()
|
||||
.find('button')
|
||||
.click({ force: true })
|
||||
.wait(500)
|
||||
cy.get('app-document-notes').find('.card').its('length').should('eq', 2)
|
||||
})
|
||||
|
||||
it('should support note insertion', () => {
|
||||
cy.wait(1000).get('a').contains('Notes').click().wait(1000)
|
||||
cy.get('app-document-notes')
|
||||
.find('form textarea')
|
||||
.type('Testing new note')
|
||||
.wait(500)
|
||||
cy.get('app-document-notes').find('form button').click().wait(1500)
|
||||
cy.get('app-document-notes').find('.card').its('length').should('eq', 4)
|
||||
})
|
||||
|
||||
it('should support navigation to notes tab by url', () => {
|
||||
cy.visit('/documents/1/notes')
|
||||
cy.get('app-document-notes').should('exist')
|
||||
})
|
||||
|
||||
it('should dynamically update note counts', () => {
|
||||
cy.visit('/documents/1/notes')
|
||||
cy.get('app-document-notes').within(() => cy.contains('Delete').click())
|
||||
cy.get('ul.nav').find('li').contains('Notes').find('.badge').contains('2')
|
||||
})
|
||||
})
|
@@ -1,196 +0,0 @@
|
||||
describe('documents-list', () => {
|
||||
beforeEach(() => {
|
||||
// also uses global fixtures from cypress/support/e2e.ts
|
||||
|
||||
this.bulkEdits = {}
|
||||
|
||||
cy.fixture('documents/documents.json').then((documentsJson) => {
|
||||
// bulk edit
|
||||
cy.intercept(
|
||||
'POST',
|
||||
'http://localhost:8000/api/documents/bulk_edit/',
|
||||
(req) => {
|
||||
this.bulkEdits = req.body // store this for later
|
||||
req.reply({ result: 'OK' })
|
||||
}
|
||||
)
|
||||
|
||||
cy.intercept('GET', 'http://localhost:8000/api/documents/*', (req) => {
|
||||
let response = { ...documentsJson }
|
||||
|
||||
// bulkEdits was set earlier by bulk_edit intercept
|
||||
if (this.bulkEdits.hasOwnProperty('documents')) {
|
||||
response.results = response.results.map((d) => {
|
||||
if ((this.bulkEdits['documents'] as Array<number>).includes(d.id)) {
|
||||
switch (this.bulkEdits['method']) {
|
||||
case 'modify_tags':
|
||||
d.tags = (d.tags as Array<number>).concat([
|
||||
this.bulkEdits['parameters']['add_tags'],
|
||||
])
|
||||
break
|
||||
case 'set_correspondent':
|
||||
d.correspondent =
|
||||
this.bulkEdits['parameters']['correspondent']
|
||||
break
|
||||
case 'set_document_type':
|
||||
d.document_type =
|
||||
this.bulkEdits['parameters']['document_type']
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return d
|
||||
})
|
||||
} else if (req.query.hasOwnProperty('tags__id__all')) {
|
||||
// filtering e.g. http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&tags__id__all=2
|
||||
const tag_id = +req.query['tags__id__all']
|
||||
response.results = (documentsJson.results as Array<any>).filter((d) =>
|
||||
(d.tags as Array<number>).includes(tag_id)
|
||||
)
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('correspondent__id__in')) {
|
||||
// filtering e.g. http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&correspondent__id__in=9,14
|
||||
const correspondent_ids = req.query['correspondent__id__in']
|
||||
.toString()
|
||||
.split(',')
|
||||
.map((c) => +c)
|
||||
response.results = (documentsJson.results as Array<any>).filter((d) =>
|
||||
correspondent_ids.includes(d.correspondent)
|
||||
)
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('correspondent__id__none')) {
|
||||
// filtering e.g. http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&correspondent__id__none=9,14
|
||||
const correspondent_ids = req.query['correspondent__id__none']
|
||||
.toString()
|
||||
.split(',')
|
||||
.map((c) => +c)
|
||||
response.results = (documentsJson.results as Array<any>).filter(
|
||||
(d) => !correspondent_ids.includes(d.correspondent)
|
||||
)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
req.reply(response)
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/documents/selection_data/', {
|
||||
fixture: 'documents/selection_data.json',
|
||||
}).as('selection-data')
|
||||
})
|
||||
|
||||
cy.viewport(1280, 1024)
|
||||
cy.visit('/documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents rendered as cards with thumbnails', () => {
|
||||
cy.contains('3 documents')
|
||||
cy.contains('lorem ipsum')
|
||||
cy.get('app-document-card-small:first-of-type img')
|
||||
.invoke('attr', 'src')
|
||||
.should('eq', 'http://localhost:8000/api/documents/1/thumb/')
|
||||
})
|
||||
|
||||
it('should change to table "details" view', () => {
|
||||
cy.get('div.btn-group input[value="details"]').next().click()
|
||||
cy.get('table')
|
||||
})
|
||||
|
||||
it('should change to large cards view', () => {
|
||||
cy.get('div.btn-group input[value="largeCards"]').next().click()
|
||||
cy.get('app-document-card-large')
|
||||
})
|
||||
|
||||
it('should show partial tag selection', () => {
|
||||
cy.get('app-document-card-small:nth-child(1)').click()
|
||||
cy.get('app-document-card-small:nth-child(4)').click()
|
||||
cy.get('app-bulk-editor button')
|
||||
.contains('Tags')
|
||||
.click()
|
||||
.wait('@selection-data')
|
||||
cy.get('svg.bi-dash').should('be.visible')
|
||||
cy.get('svg.bi-check').should('be.visible')
|
||||
})
|
||||
|
||||
it('should allow bulk removal', () => {
|
||||
cy.get('app-document-card-small:nth-child(1)').click()
|
||||
cy.get('app-document-card-small:nth-child(4)').click()
|
||||
cy.get('app-bulk-editor').within(() => {
|
||||
cy.get('button').contains('Tags').click().wait('@selection-data')
|
||||
cy.get('button').contains('Another Sample Tag').click()
|
||||
cy.get('button').contains('Apply').click()
|
||||
})
|
||||
cy.contains('operation will remove the tag')
|
||||
})
|
||||
|
||||
it('should filter tags', () => {
|
||||
cy.get('app-filter-editor app-filterable-dropdown[title="Tags"]').within(
|
||||
() => {
|
||||
cy.contains('button', 'Tags').click()
|
||||
cy.contains('button', 'Tag 2').click()
|
||||
}
|
||||
)
|
||||
cy.contains('One document')
|
||||
})
|
||||
|
||||
it('should filter including multiple correspondents', () => {
|
||||
cy.get('app-filter-editor app-filterable-dropdown[title="Correspondent"]')
|
||||
.click()
|
||||
.within(() => {
|
||||
cy.contains('button', 'ABC Test Correspondent').click()
|
||||
cy.contains('button', 'Corresp 11').click()
|
||||
})
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
|
||||
it('should filter excluding multiple correspondents', () => {
|
||||
cy.get('app-filter-editor app-filterable-dropdown[title="Correspondent"]')
|
||||
.click()
|
||||
.within(() => {
|
||||
cy.contains('button', 'ABC Test Correspondent').click()
|
||||
cy.contains('button', 'Corresp 11').click()
|
||||
cy.contains('label', 'Exclude').click()
|
||||
})
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
|
||||
it('should apply tags', () => {
|
||||
cy.get('app-document-card-small:first-of-type').click()
|
||||
cy.get('app-bulk-editor app-filterable-dropdown[title="Tags"]').within(
|
||||
() => {
|
||||
cy.contains('button', 'Tags').click()
|
||||
cy.contains('button', 'Test Tag').click()
|
||||
cy.contains('button', 'Apply').click()
|
||||
}
|
||||
)
|
||||
cy.contains('button', 'Confirm').click()
|
||||
cy.get('app-document-card-small:first-of-type').contains('Test Tag')
|
||||
})
|
||||
|
||||
it('should apply correspondent', () => {
|
||||
cy.get('app-document-card-small:first-of-type').click()
|
||||
cy.get(
|
||||
'app-bulk-editor app-filterable-dropdown[title="Correspondent"]'
|
||||
).within(() => {
|
||||
cy.contains('button', 'Correspondent').click()
|
||||
cy.contains('button', 'ABC Test Correspondent').click()
|
||||
cy.contains('button', 'Apply').click()
|
||||
})
|
||||
cy.contains('button', 'Confirm').click()
|
||||
cy.get('app-document-card-small:first-of-type').contains(
|
||||
'ABC Test Correspondent'
|
||||
)
|
||||
})
|
||||
|
||||
it('should apply document type', () => {
|
||||
cy.get('app-document-card-small:first-of-type').click()
|
||||
cy.get(
|
||||
'app-bulk-editor app-filterable-dropdown[title="Document type"]'
|
||||
).within(() => {
|
||||
cy.contains('button', 'Document type').click()
|
||||
cy.contains('button', 'Test Doc Type').click()
|
||||
cy.contains('button', 'Apply').click()
|
||||
})
|
||||
cy.contains('button', 'Confirm').click()
|
||||
cy.get('app-document-card-small:first-of-type').contains('Test Doc Type')
|
||||
})
|
||||
})
|
@@ -1,391 +0,0 @@
|
||||
import { PaperlessDocument } from 'src/app/data/paperless-document'
|
||||
|
||||
describe('documents query params', () => {
|
||||
beforeEach(() => {
|
||||
// also uses global fixtures from cypress/support/e2e.ts
|
||||
|
||||
cy.fixture('documents/documents.json').then((documentsJson) => {
|
||||
// mock api filtering
|
||||
cy.intercept('GET', 'http://localhost:8000/api/documents/*', (req) => {
|
||||
let response = { ...documentsJson }
|
||||
|
||||
if (req.query.hasOwnProperty('ordering')) {
|
||||
const sort_field = req.query['ordering'].toString().replace('-', '')
|
||||
const reverse = req.query['ordering'].toString().indexOf('-') !== -1
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).sort((docA, docB) => {
|
||||
let result = 0
|
||||
switch (sort_field) {
|
||||
case 'created':
|
||||
case 'added':
|
||||
result =
|
||||
new Date(docA[sort_field]) < new Date(docB[sort_field])
|
||||
? -1
|
||||
: 1
|
||||
break
|
||||
case 'archive_serial_number':
|
||||
result = docA[sort_field] < docB[sort_field] ? -1 : 1
|
||||
break
|
||||
}
|
||||
if (reverse) result = -result
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
if (req.query.hasOwnProperty('tags__id__in')) {
|
||||
const tag_ids: Array<number> = req.query['tags__id__in']
|
||||
.toString()
|
||||
.split(',')
|
||||
.map((v) => +v)
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter(
|
||||
(d) =>
|
||||
d.tags.length > 0 &&
|
||||
d.tags.filter((t) => tag_ids.includes(t)).length > 0
|
||||
)
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('tags__id__none')) {
|
||||
const tag_ids: Array<number> = req.query['tags__id__none']
|
||||
.toString()
|
||||
.split(',')
|
||||
.map((v) => +v)
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.tags.filter((t) => tag_ids.includes(t)).length == 0)
|
||||
response.count = response.results.length
|
||||
} else if (
|
||||
req.query.hasOwnProperty('is_tagged') &&
|
||||
req.query['is_tagged'] == '0'
|
||||
) {
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.tags.length == 0)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
if (req.query.hasOwnProperty('document_type__id')) {
|
||||
const doctype_id = +req.query['document_type__id']
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.document_type == doctype_id)
|
||||
response.count = response.results.length
|
||||
} else if (
|
||||
req.query.hasOwnProperty('document_type__isnull') &&
|
||||
req.query['document_type__isnull'] == '1'
|
||||
) {
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.document_type == undefined)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
if (req.query.hasOwnProperty('correspondent__id')) {
|
||||
const correspondent_id = +req.query['correspondent__id']
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.correspondent == correspondent_id)
|
||||
response.count = response.results.length
|
||||
} else if (
|
||||
req.query.hasOwnProperty('correspondent__isnull') &&
|
||||
req.query['correspondent__isnull'] == '1'
|
||||
) {
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.correspondent == undefined)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
if (req.query.hasOwnProperty('storage_path__id')) {
|
||||
const storage_path_id = +req.query['storage_path__id']
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.storage_path == storage_path_id)
|
||||
response.count = response.results.length
|
||||
} else if (
|
||||
req.query.hasOwnProperty('storage_path__isnull') &&
|
||||
req.query['storage_path__isnull'] == '1'
|
||||
) {
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.storage_path == undefined)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
if (req.query.hasOwnProperty('created__date__gt')) {
|
||||
const date = new Date(req.query['created__date__gt'])
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => new Date(d.created) > date)
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('created__date__lt')) {
|
||||
const date = new Date(req.query['created__date__lt'])
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => new Date(d.created) < date)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
if (req.query.hasOwnProperty('added__date__gt')) {
|
||||
const date = new Date(req.query['added__date__gt'])
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => new Date(d.added) > date)
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('added__date__lt')) {
|
||||
const date = new Date(req.query['added__date__lt'])
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => new Date(d.added) < date)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
if (req.query.hasOwnProperty('title_content')) {
|
||||
const title_content_regexp = new RegExp(
|
||||
req.query['title_content'].toString(),
|
||||
'i'
|
||||
)
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter(
|
||||
(d) =>
|
||||
title_content_regexp.test(d.title) ||
|
||||
title_content_regexp.test(d.content)
|
||||
)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
if (req.query.hasOwnProperty('archive_serial_number')) {
|
||||
const asn = +req.query['archive_serial_number']
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.archive_serial_number == asn)
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('archive_serial_number__isnull')) {
|
||||
const isnull = req.query['storage_path__isnull'] == '1'
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) =>
|
||||
isnull
|
||||
? d.archive_serial_number == undefined
|
||||
: d.archive_serial_number != undefined
|
||||
)
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('archive_serial_number__gt')) {
|
||||
const asn = +req.query['archive_serial_number__gt']
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter(
|
||||
(d) => d.archive_serial_number > 0 && d.archive_serial_number > asn
|
||||
)
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('archive_serial_number__lt')) {
|
||||
const asn = +req.query['archive_serial_number__lt']
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter(
|
||||
(d) => d.archive_serial_number > 0 && d.archive_serial_number < asn
|
||||
)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
if (req.query.hasOwnProperty('owner__id')) {
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.owner == req.query['owner__id'])
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('owner__id__in')) {
|
||||
const owners = req.query['owner__id__in']
|
||||
.toString()
|
||||
.split(',')
|
||||
.map((o) => parseInt(o))
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => owners.includes(d.owner))
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('owner__id__none')) {
|
||||
const owners = req.query['owner__id__none']
|
||||
.toString()
|
||||
.split(',')
|
||||
.map((o) => parseInt(o))
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => !owners.includes(d.owner))
|
||||
response.count = response.results.length
|
||||
} else if (req.query.hasOwnProperty('owner__isnull')) {
|
||||
response.results = (
|
||||
documentsJson.results as Array<PaperlessDocument>
|
||||
).filter((d) => d.owner === null)
|
||||
response.count = response.results.length
|
||||
}
|
||||
|
||||
req.reply(response)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should show a list of documents sorted by created', () => {
|
||||
cy.visit('/documents?sort=created')
|
||||
cy.get('app-document-card-small').first().contains('No latin title')
|
||||
})
|
||||
|
||||
it('should show a list of documents reverse sorted by created', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true')
|
||||
cy.get('app-document-card-small').first().contains('Doc 6')
|
||||
})
|
||||
|
||||
it('should show a list of documents sorted by added', () => {
|
||||
cy.visit('/documents?sort=added')
|
||||
cy.get('app-document-card-small').first().contains('No latin title')
|
||||
})
|
||||
|
||||
it('should show a list of documents reverse sorted by added', () => {
|
||||
cy.visit('/documents?sort=added&reverse=true')
|
||||
cy.get('app-document-card-small').first().contains('Doc 6')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by any tags', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&tags__id__in=2,4,5')
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by excluded tags', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&tags__id__none=2,4')
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by no tags', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&is_tagged=0')
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by document type', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&document_type__id=1')
|
||||
cy.contains('2 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by multiple correspondents', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&document_type__id__in=1,2')
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by no document type', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&document_type__isnull=1')
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by correspondent', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&correspondent__id=9')
|
||||
cy.contains('2 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by multiple correspondents', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&correspondent__id__in=9,14')
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by no correspondent', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&correspondent__isnull=1')
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by storage path', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&storage_path__id=2')
|
||||
cy.contains('One document')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by no storage path', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&storage_path__isnull=1')
|
||||
cy.contains('5 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by title or content', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&title_content=lorem')
|
||||
cy.contains('2 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by asn', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&archive_serial_number=12345')
|
||||
cy.contains('One document')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by empty asn', () => {
|
||||
cy.visit(
|
||||
'/documents?sort=created&reverse=true&archive_serial_number__isnull=1'
|
||||
)
|
||||
cy.contains('2 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by non-empty asn', () => {
|
||||
cy.visit(
|
||||
'/documents?sort=created&reverse=true&archive_serial_number__isnull=0'
|
||||
)
|
||||
cy.contains('2 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by asn greater than', () => {
|
||||
cy.visit(
|
||||
'/documents?sort=created&reverse=true&archive_serial_number__gt=12346'
|
||||
)
|
||||
cy.contains('One document')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by asn less than', () => {
|
||||
cy.visit(
|
||||
'/documents?sort=created&reverse=true&archive_serial_number__lt=12346'
|
||||
)
|
||||
cy.contains('One document')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by created date greater than', () => {
|
||||
cy.visit(
|
||||
'/documents?sort=created&reverse=true&created__date__gt=2022-03-23'
|
||||
)
|
||||
cy.contains('5 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by created date less than', () => {
|
||||
cy.visit(
|
||||
'/documents?sort=created&reverse=true&created__date__lt=2022-03-23'
|
||||
)
|
||||
cy.contains('One document')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by added date greater than', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&added__date__gt=2022-03-24')
|
||||
cy.contains('4 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by added date less than', () => {
|
||||
cy.visit('/documents?sort=created&reverse=true&added__date__lt=2022-03-24')
|
||||
cy.contains('2 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by multiple filters', () => {
|
||||
cy.visit(
|
||||
'/documents?sort=created&reverse=true&document_type__id=1&correspondent__id=9&tags__id__in=4,5'
|
||||
)
|
||||
cy.contains('2 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by owner', () => {
|
||||
cy.visit('/documents?owner__id=15')
|
||||
cy.contains('One document')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by multiple owners', () => {
|
||||
cy.visit('/documents?owner__id__in=6,15')
|
||||
cy.contains('2 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by excluded owners', () => {
|
||||
cy.visit('/documents?owner__id__none=6')
|
||||
cy.contains('5 documents')
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by null owner', () => {
|
||||
cy.visit('/documents?owner__isnull=true')
|
||||
cy.contains('4 documents')
|
||||
})
|
||||
})
|
@@ -1,25 +0,0 @@
|
||||
describe('manage', () => {
|
||||
// also uses global fixtures from cypress/support/e2e.ts
|
||||
|
||||
it('should show a list of correspondents with bottom pagination as well', () => {
|
||||
cy.visit('/correspondents')
|
||||
cy.get('tbody').find('tr').its('length').should('eq', 25)
|
||||
cy.get('ngb-pagination').its('length').should('eq', 2)
|
||||
})
|
||||
|
||||
it('should show a list of tags without bottom pagination', () => {
|
||||
cy.visit('/tags')
|
||||
cy.get('tbody').find('tr').its('length').should('eq', 8)
|
||||
cy.get('ngb-pagination').its('length').should('eq', 1)
|
||||
})
|
||||
|
||||
it('should show a list of documents filtered by tag', () => {
|
||||
cy.intercept('http://localhost:8000/api/documents/*', (req) => {
|
||||
if (req.url.indexOf('tags__id__all=4'))
|
||||
req.reply({ count: 3, next: null, previous: null, results: [] })
|
||||
})
|
||||
cy.visit('/tags')
|
||||
cy.get('tbody').find('button:visible').contains('Documents').first().click() // id = 4
|
||||
cy.contains('3 documents')
|
||||
})
|
||||
})
|
@@ -1,187 +0,0 @@
|
||||
describe('settings', () => {
|
||||
beforeEach(() => {
|
||||
// also uses global fixtures from cypress/support/e2e.ts
|
||||
|
||||
this.modifiedViews = []
|
||||
|
||||
// mock API methods
|
||||
cy.intercept('http://localhost:8000/api/ui_settings/', {
|
||||
fixture: 'ui_settings/settings.json',
|
||||
}).then(() => {
|
||||
cy.fixture('saved_views/savedviews.json').then((savedViewsJson) => {
|
||||
// saved views PATCH
|
||||
cy.intercept(
|
||||
'PATCH',
|
||||
'http://localhost:8000/api/saved_views/*',
|
||||
(req) => {
|
||||
this.modifiedViews.push(req.body) // store this for later
|
||||
req.reply({ result: 'OK' })
|
||||
}
|
||||
)
|
||||
|
||||
cy.intercept(
|
||||
'GET',
|
||||
'http://localhost:8000/api/saved_views/*',
|
||||
(req) => {
|
||||
let response = { ...savedViewsJson }
|
||||
if (this.modifiedViews.length) {
|
||||
response.results = response.results.map((v) => {
|
||||
if (this.modifiedViews.find((mv) => mv.id == v.id))
|
||||
v = this.modifiedViews.find((mv) => mv.id == v.id)
|
||||
return v
|
||||
})
|
||||
}
|
||||
|
||||
req.reply(response)
|
||||
}
|
||||
).as('savedViews')
|
||||
})
|
||||
|
||||
this.newMailAccounts = []
|
||||
|
||||
cy.intercept(
|
||||
'POST',
|
||||
'http://localhost:8000/api/mail_accounts/',
|
||||
(req) => {
|
||||
const newRule = req.body
|
||||
newRule.id = 3
|
||||
this.newMailAccounts.push(newRule) // store this for later
|
||||
req.reply({ result: 'OK' })
|
||||
}
|
||||
).as('saveAccount')
|
||||
|
||||
cy.fixture('mail_accounts/mail_accounts.json').then(
|
||||
(mailAccountsJson) => {
|
||||
cy.intercept(
|
||||
'GET',
|
||||
'http://localhost:8000/api/mail_accounts/*',
|
||||
(req) => {
|
||||
let response = { ...mailAccountsJson }
|
||||
if (this.newMailAccounts.length) {
|
||||
response.results = response.results.concat(this.newMailAccounts)
|
||||
}
|
||||
|
||||
req.reply(response)
|
||||
}
|
||||
).as('getAccounts')
|
||||
}
|
||||
)
|
||||
|
||||
this.newMailRules = []
|
||||
|
||||
cy.intercept('POST', 'http://localhost:8000/api/mail_rules/', (req) => {
|
||||
const newRule = req.body
|
||||
newRule.id = 2
|
||||
this.newMailRules.push(newRule) // store this for later
|
||||
req.reply({ result: 'OK' })
|
||||
}).as('saveRule')
|
||||
|
||||
cy.fixture('mail_rules/mail_rules.json').then((mailRulesJson) => {
|
||||
cy.intercept('GET', 'http://localhost:8000/api/mail_rules/*', (req) => {
|
||||
let response = { ...mailRulesJson }
|
||||
if (this.newMailRules.length) {
|
||||
response.results = response.results.concat(this.newMailRules)
|
||||
}
|
||||
|
||||
req.reply(response)
|
||||
}).as('getRules')
|
||||
})
|
||||
|
||||
cy.fixture('documents/documents.json').then((documentsJson) => {
|
||||
cy.intercept('GET', 'http://localhost:8000/api/documents/1/', (req) => {
|
||||
let response = { ...documentsJson }
|
||||
response = response.results.find((d) => d.id == 1)
|
||||
req.reply(response)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
cy.viewport(1024, 1600)
|
||||
cy.visit('/settings')
|
||||
})
|
||||
|
||||
it('should activate / deactivate save button when settings change and are saved', () => {
|
||||
cy.contains('button', 'Save').should('be.disabled')
|
||||
cy.contains('Use system settings').click()
|
||||
cy.contains('button', 'Save').should('not.be.disabled')
|
||||
cy.contains('button', 'Save').click()
|
||||
cy.contains('button', 'Save').should('be.disabled')
|
||||
})
|
||||
|
||||
it('should warn on unsaved changes', () => {
|
||||
cy.contains('Use system settings').click()
|
||||
cy.contains('a', 'Dashboard').click()
|
||||
cy.contains('You have unsaved changes')
|
||||
cy.contains('button', 'Cancel').click()
|
||||
cy.contains('button', 'Save').click().wait(2000)
|
||||
cy.contains('a', 'Dashboard').click()
|
||||
cy.contains('You have unsaved changes').should('not.exist')
|
||||
})
|
||||
|
||||
it('should apply appearance changes when set', () => {
|
||||
cy.contains('Use system settings').click()
|
||||
cy.get('body').should('not.have.class', 'color-scheme-system')
|
||||
cy.contains('Enable dark mode').click()
|
||||
cy.get('body').should('have.class', 'color-scheme-dark')
|
||||
})
|
||||
|
||||
it('should remove saved view from sidebar when unset', () => {
|
||||
cy.contains('a', 'Saved views').click().wait(2000)
|
||||
cy.get('#show_in_sidebar_1').click()
|
||||
cy.contains('button', 'Save').click().wait('@savedViews').wait(2000)
|
||||
cy.contains('li', 'Inbox').should('not.exist')
|
||||
})
|
||||
|
||||
it('should remove saved view from dashboard when unset', () => {
|
||||
cy.contains('a', 'Saved views').click()
|
||||
cy.get('#show_on_dashboard_1').click()
|
||||
cy.contains('button', 'Save').click().wait('@savedViews').wait(2000)
|
||||
cy.visit('/dashboard')
|
||||
cy.get('app-saved-view-widget').contains('Inbox').should('not.exist')
|
||||
})
|
||||
|
||||
it('should show a list of mail accounts & support creation', () => {
|
||||
cy.contains('a', 'Mail').click()
|
||||
cy.get('app-settings .tab-content ul li').its('length').should('eq', 5) // 2 headers, 2 accounts, 1 rule
|
||||
cy.contains('button', 'Add Account').click()
|
||||
cy.contains('Create new mail account')
|
||||
cy.get('app-input-text[formcontrolname="name"]').type(
|
||||
'Example Mail Account'
|
||||
)
|
||||
cy.get('app-input-text[formcontrolname="imap_server"]').type(
|
||||
'mail.example.com'
|
||||
)
|
||||
cy.get('app-input-text[formcontrolname="imap_port"]').type('993')
|
||||
cy.get('app-input-text[formcontrolname="username"]').type('username')
|
||||
cy.get('app-input-password[formcontrolname="password"]').type('pass')
|
||||
cy.contains('app-mail-account-edit-dialog button', 'Save')
|
||||
.click()
|
||||
.wait('@saveAccount')
|
||||
.wait('@getAccounts')
|
||||
cy.contains('Saved account')
|
||||
|
||||
cy.get('app-settings .tab-content ul li').its('length').should('eq', 6)
|
||||
})
|
||||
|
||||
it('should show a list of mail rules & support creation', () => {
|
||||
cy.contains('a', 'Mail').click()
|
||||
cy.get('app-settings .tab-content ul li').its('length').should('eq', 5) // 2 headers, 2 accounts, 1 rule
|
||||
|
||||
cy.wait(1000)
|
||||
cy.contains('button', 'Add Rule').click()
|
||||
cy.contains('Create new mail rule')
|
||||
cy.get('app-input-text[formcontrolname="name"]').type('Example Rule')
|
||||
cy.get('app-input-select[formcontrolname="account"]').type('Example{enter}')
|
||||
cy.get('app-input-number[formcontrolname="maximum_age"]').type('30')
|
||||
cy.get('app-input-text[formcontrolname="filter_subject"]').type(
|
||||
'[paperless]'
|
||||
)
|
||||
cy.contains('app-mail-rule-edit-dialog button', 'Save')
|
||||
.click()
|
||||
.wait('@saveRule')
|
||||
.wait('@getRules')
|
||||
cy.contains('Saved rule').wait(1000)
|
||||
|
||||
cy.get('app-settings .tab-content ul li').its('length').should('eq', 6)
|
||||
})
|
||||
})
|
@@ -1,93 +0,0 @@
|
||||
describe('tasks', () => {
|
||||
beforeEach(() => {
|
||||
this.dismissedTasks = new Set<number>()
|
||||
|
||||
cy.fixture('tasks/tasks.json').then((tasksViewsJson) => {
|
||||
// acknowledge tasks POST
|
||||
cy.intercept(
|
||||
'POST',
|
||||
'http://localhost:8000/api/acknowledge_tasks/',
|
||||
(req) => {
|
||||
req.body['tasks'].forEach((t) => this.dismissedTasks.add(t)) // store this for later
|
||||
req.reply({ result: 'OK' })
|
||||
}
|
||||
)
|
||||
|
||||
cy.intercept('GET', 'http://localhost:8000/api/tasks/', (req) => {
|
||||
let response = [...tasksViewsJson]
|
||||
if (this.dismissedTasks.size) {
|
||||
response = response.filter((t) => {
|
||||
return !this.dismissedTasks.has(t.id)
|
||||
})
|
||||
}
|
||||
|
||||
req.reply(response)
|
||||
}).as('tasks')
|
||||
})
|
||||
|
||||
cy.visit('/tasks')
|
||||
cy.wait('@tasks')
|
||||
})
|
||||
|
||||
it('should show a list of dismissable tasks in tabs', () => {
|
||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 10) // double because collapsible result tr
|
||||
cy.wait(500) // stabilizes the test, for some reason...
|
||||
cy.get('tbody')
|
||||
.find('button:visible')
|
||||
.contains('Dismiss')
|
||||
.first()
|
||||
.click()
|
||||
.wait('@tasks')
|
||||
.wait(2000)
|
||||
.then(() => {
|
||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 8) // double because collapsible result tr
|
||||
})
|
||||
})
|
||||
|
||||
it('should correctly switch between task tabs', () => {
|
||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 10) // double because collapsible result tr
|
||||
cy.wait(500) // stabilizes the test, for some reason...
|
||||
cy.get('app-tasks')
|
||||
.find('a:visible')
|
||||
.contains('Queued')
|
||||
.first()
|
||||
.click()
|
||||
.wait(2000)
|
||||
.then(() => {
|
||||
cy.get('tbody').find('tr:visible').should('not.exist')
|
||||
})
|
||||
cy.get('app-tasks')
|
||||
.find('a:visible')
|
||||
.contains('Started')
|
||||
.first()
|
||||
.click()
|
||||
.wait(2000)
|
||||
.then(() => {
|
||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 2) // double because collapsible result tr
|
||||
})
|
||||
cy.get('app-tasks')
|
||||
.find('a:visible')
|
||||
.contains('Complete')
|
||||
.first()
|
||||
.click()
|
||||
.wait('@tasks')
|
||||
.wait(2000)
|
||||
.then(() => {
|
||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 12) // double because collapsible result tr
|
||||
})
|
||||
})
|
||||
|
||||
it('should allow toggling all tasks in list and warn on dismiss', () => {
|
||||
cy.get('thead').find('input[type="checkbox"]').first().click()
|
||||
cy.get('body').find('button').contains('Dismiss selected').first().click()
|
||||
cy.contains('Confirm')
|
||||
cy.get('.modal')
|
||||
.contains('button', 'Dismiss')
|
||||
.click()
|
||||
.wait('@tasks')
|
||||
.wait(2000)
|
||||
.then(() => {
|
||||
cy.get('tbody').find('tr:visible').should('not.exist')
|
||||
})
|
||||
})
|
||||
})
|
@@ -1,257 +0,0 @@
|
||||
{
|
||||
"count": 27,
|
||||
"next": "http://localhost:8000/api/correspondents/?page=2",
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 9,
|
||||
"slug": "abc-test-correspondent",
|
||||
"name": "ABC Test Correspondent",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"slug": "corresp-10",
|
||||
"name": "Corresp 10",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"slug": "corresp-11",
|
||||
"name": "Corresp 11",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"slug": "corresp-12",
|
||||
"name": "Corresp 12",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"slug": "corresp-13",
|
||||
"name": "Corresp 13",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"slug": "corresp-15",
|
||||
"name": "Corresp 15",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"slug": "corresp-16",
|
||||
"name": "Corresp 16",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"slug": "corresp-17",
|
||||
"name": "Corresp 17",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"slug": "corresp-18",
|
||||
"name": "Corresp 18",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"slug": "corresp-19",
|
||||
"name": "Corresp 19",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"slug": "corresp-20",
|
||||
"name": "Corresp 20",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"slug": "corresp-21",
|
||||
"name": "Corresp 21",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"slug": "corresp-22",
|
||||
"name": "Corresp 22",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"slug": "corresp-23",
|
||||
"name": "Corresp 23",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"slug": "corresp-3",
|
||||
"name": "Corresp 3",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"slug": "corresp-4",
|
||||
"name": "Corresp 4",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"slug": "corresp-5",
|
||||
"name": "Corresp 5",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"slug": "corresp-6",
|
||||
"name": "Corresp 6",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"slug": "corresp-7",
|
||||
"name": "Corresp 7",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"slug": "corresp-8",
|
||||
"name": "Corresp 8",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"slug": "corresp-9",
|
||||
"name": "Corresp 9",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"slug": "correspondent-14",
|
||||
"name": "Correspondent 14",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 0,
|
||||
"last_correspondence": null
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"slug": "correspondent-2",
|
||||
"name": "Correspondent 2",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 7,
|
||||
"last_correspondence": "2021-01-20T23:37:58.204614Z"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"slug": "correspondent-slug",
|
||||
"name": "Correspondent Slug",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 1,
|
||||
"last_correspondence": "2022-03-16T03:48:50.089624Z"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"slug": "newest-correspondent",
|
||||
"name": "Newest Correspondent",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 1,
|
||||
"last_correspondence": "2021-02-07T08:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"count": 2,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"slug": "test",
|
||||
"name": "Test Doc Type",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"slug": "test2",
|
||||
"name": "Test Doc Type 2",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"document_count": 1
|
||||
}
|
||||
]
|
||||
}
|
@@ -1 +0,0 @@
|
||||
{"original_checksum":"e959bc7d593245d92685213264e962ba","original_size":963754,"original_mime_type":"application/pdf","media_filename":"2022/lorem-ipsum.pdf","has_archive_version":true,"original_metadata":[],"archive_checksum":"5a1f46a9150bcade978c764b039ce4d0","archive_media_filename":"2022/lorem-ipsum.pdf","archive_size":351160,"archive_metadata":[{"namespace":"http://ns.adobe.com/pdf/1.3/","prefix":"pdf","key":"Producer","value":"pikepdf5.0.1"},{"namespace":"http://ns.adobe.com/xap/1.0/","prefix":"xmp","key":"ModifyDate","value":"2022-03-22T04:53:18+00:00"},{"namespace":"http://ns.adobe.com/xap/1.0/","prefix":"xmp","key":"CreateDate","value":"2022-03-22T18:05:43+00:00"},{"namespace":"http://ns.adobe.com/xap/1.0/","prefix":"xmp","key":"CreatorTool","value":"ocrmypdf13.4.0/TesseractOCR-PDF4.1.1"},{"namespace":"http://ns.adobe.com/xap/1.0/mm/","prefix":"xmpMM","key":"DocumentID","value":"uuid:df27edcf-e34a-11f7-0000-8fa6067a3c04"},{"namespace":"http://purl.org/dc/elements/1.1/","prefix":"dc","key":"format","value":"application/pdf"},{"namespace":"http://purl.org/dc/elements/1.1/","prefix":"dc","key":"title","value":"ScannedDocument"},{"namespace":"http://www.aiim.org/pdfa/ns/id/","prefix":"pdfaid","key":"part","value":"2"},{"namespace":"http://www.aiim.org/pdfa/ns/id/","prefix":"pdfaid","key":"conformance","value":"B"},{"namespace":"http://purl.org/dc/elements/1.1/","prefix":"dc","key":"creator","value":"None"},{"namespace":"http://ns.adobe.com/xap/1.0/","prefix":"xmp","key":"MetadataDate","value":"2022-03-22T21:53:18.882551-07:00"}]}
|
@@ -1,26 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 10,
|
||||
"note": "Testing new note",
|
||||
"created": "2022-08-08T04:24:55.176008Z",
|
||||
"user": 3
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"note": "Testing one more time",
|
||||
"created": "2022-02-18T04:24:55.176008Z",
|
||||
"user": 15
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"note": "Another note",
|
||||
"created": "2021-11-08T04:24:47.925042Z",
|
||||
"user": 3
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"note": "Cupcake ipsum dolor sit amet cheesecake candy cookie tiramisu. Donut chocolate chupa chups macaroon brownie halvah pie cheesecake gummies. Sweet chocolate bar candy donut gummi bears bear claw liquorice bonbon shortbread.\n\nDonut chocolate bar candy wafer wafer tiramisu. Gummies chocolate cake muffin toffee carrot cake macaroon. Toffee toffee jelly beans danish lollipop cake.",
|
||||
"created": "2021-02-08T02:37:49.724132Z",
|
||||
"user": 3
|
||||
}
|
||||
]
|
@@ -1 +0,0 @@
|
||||
{"correspondents":[],"tags":[3],"document_types":[1]}
|
@@ -1,206 +0,0 @@
|
||||
{
|
||||
"count": 3,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"correspondent": 9,
|
||||
"document_type": 1,
|
||||
"storage_path": null,
|
||||
"title": "No latin title",
|
||||
"content": "Test document PDF \n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla est purus, ultrices in porttitor \nin, accumsan non quam. Nam consectetur porttitor rhoncus. Curabitur eu est et leo feugiat \nauctor vel quis lorem. Ut et ligula dolor, sit amet consequat lorem. Aliquam porta eros sed \nvelit imperdiet egestas. Maecenas tempus eros ut diam ullamcorper id dictum libero \ntempor. Donec quis augue quis magna condimentum lobortis. Quisque imperdiet ipsum vel \nmagna viverra rutrum. Cras viverra molestie urna, vitae vestibulum turpis varius id. \nVestibulum mollis, arcu iaculis bibendum varius, velit sapien blandit metus, ac posuere lorem \nnulla ac dolor. Maecenas urna elit, tincidunt in dapibus nec, vehicula eu dui. Duis lacinia \nfringilla massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur \nridiculus mus. Ut consequat ultricies est, non rhoncus mauris congue porta. Vivamus viverra \nsuscipit felis eget condimentum. Cum sociis natoque penatibus et magnis dis parturient \nmontes, nascetur ridiculus mus. Integer bibendum sagittis ligula, non faucibus nulla volutpat \nvitae. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. \nIn aliquet quam et velit bibendum accumsan. Cum sociis natoque penatibus et magnis dis \nparturient montes, nascetur ridiculus mus. Vestibulum vitae ipsum nec arcu semper \nadipiscing at ac lacus. Praesent id pellentesque orci. Morbi congue viverra nisl nec rhoncus. \nInteger mattis, ipsum a tincidunt commodo, lacus arcu elementum elit, at mollis eros ante ac \nrisus. In volutpat, ante at pretium ultricies, velit magna suscipit enim, aliquet blandit massa \norci nec lorem. Nulla facilisi. Duis eu vehicula arcu. Nulla facilisi. Maecenas pellentesque \nvolutpat felis, quis tristique ligula luctus vel. Sed nec mi eros. Integer augue enim, sollicitudin \nullamcorper mattis eget, aliquam in est. Morbi sollicitudin libero nec augue dignissim ut \nconsectetur dui volutpat. Nulla facilisi. Mauris egestas vestibulum neque cursus tincidunt. \nDonec sit amet pulvinar orci. \nQuisque volutpat pharetra tincidunt. Fusce sapien arcu, molestie eget varius egestas, \nfaucibus ac urna. Sed at nisi in velit egestas aliquam ut a felis. Aenean malesuada iaculis nisl, \nut tempor lacus egestas consequat. Nam nibh lectus, gravida sed egestas ut, feugiat quis \ndolor. Donec eu leo enim, non laoreet ante. Morbi dictum tempor vulputate. Phasellus \nultricies risus vel augue sagittis euismod. Vivamus tincidunt placerat nisi in aliquam. Cras \nquis mi ac nunc pretium aliquam. Aenean elementum erat ac metus commodo rhoncus. \nAliquam nulla augue, porta non sagittis quis, accumsan vitae sem. Phasellus id lectus tortor, \neget pulvinar augue. Etiam eget velit ac purus fringilla blandit. Donec odio odio, sagittis sed \niaculis sed, consectetur eget sem. Lorem ipsum dolor sit amet, consectetur adipiscing elit. \nMaecenas accumsan velit vel turpis rutrum in sodales diam placerat. \nQuisque luctus ullamcorper velit sit amet lobortis. Etiam ligula felis, vulputate quis rhoncus \nnec, fermentum eget odio. Vivamus vel ipsum ac augue sodales mollis euismod nec tellus. \nFusce et augue rutrum nunc semper vehicula vel semper nisl. Nam laoreet euismod quam at \nvarius. Sed aliquet auctor nibh. Curabitur malesuada fermentum lacus vel accumsan. Duis \nornare scelerisque nulla, ac pulvinar ligula tempus sit amet. In placerat nulla ac ante \nscelerisque posuere. Phasellus at ante felis. Sed hendrerit risus a metus posuere rutrum. \nPhasellus eu augue dui. Proin in vestibulum ipsum. Aenean accumsan mollis sapien, ut \neleifend sem blandit at. Vivamus luctus mi eget lorem lobortis pharetra. Phasellus at tortor \nquam, a volutpat purus. Etiam sollicitudin arcu vel elit bibendum et imperdiet risus tincidunt. \nEtiam elit velit, posuere ut pulvinar ac, condimentum eget justo. Fusce a erat velit. Vivamus \nimperdiet ultrices orci in hendrerit.",
|
||||
"tags": [
|
||||
4
|
||||
],
|
||||
"created": "2022-03-22T07:24:18Z",
|
||||
"created_date": "2022-03-22",
|
||||
"modified": "2022-03-22T07:24:23.264859Z",
|
||||
"added": "2022-03-22T07:24:22.922631Z",
|
||||
"archive_serial_number": null,
|
||||
"original_file_name": "2022-03-22 no latin title.pdf",
|
||||
"archived_file_name": "2022-03-22 no latin title.pdf",
|
||||
"owner": null,
|
||||
"user_can_change": true,
|
||||
"permissions": {
|
||||
"view": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
},
|
||||
"change": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
}
|
||||
},
|
||||
"notes": [
|
||||
{
|
||||
"id": 9,
|
||||
"note": "Testing one more time",
|
||||
"created": "2022-02-18T04:24:55.176008Z",
|
||||
"user": 15
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"note": "Another note",
|
||||
"created": "2021-11-08T04:24:47.925042Z",
|
||||
"user": 3
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"note": "Cupcake ipsum dolor sit amet cheesecake candy cookie tiramisu. Donut chocolate chupa chups macaroon brownie halvah pie cheesecake gummies. Sweet chocolate bar candy donut gummi bears bear claw liquorice bonbon shortbread.\n\nDonut chocolate bar candy wafer wafer tiramisu. Gummies chocolate cake muffin toffee carrot cake macaroon. Toffee toffee jelly beans danish lollipop cake.",
|
||||
"created": "2021-02-08T02:37:49.724132Z",
|
||||
"user": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"correspondent": null,
|
||||
"document_type": null,
|
||||
"storage_path": 2,
|
||||
"title": "lorem ipsum dolor sit amet",
|
||||
"content": "Test document PDF",
|
||||
"tags": [],
|
||||
"created": "2022-03-23T07:24:18Z",
|
||||
"created_date": "2022-03-23",
|
||||
"modified": "2022-03-23T07:24:23.264859Z",
|
||||
"added": "2022-03-23T07:24:22.922631Z",
|
||||
"archive_serial_number": 12345,
|
||||
"original_file_name": "2022-03-23 lorem ipsum dolor sit amet.pdf",
|
||||
"archived_file_name": "2022-03-23 llorem ipsum dolor sit amet.pdf",
|
||||
"owner": null,
|
||||
"user_can_change": true,
|
||||
"permissions": {
|
||||
"view": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
},
|
||||
"change": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
}
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"correspondent": 14,
|
||||
"document_type": 1,
|
||||
"storage_path": null,
|
||||
"title": "dolor",
|
||||
"content": "Test document PDF",
|
||||
"tags": [
|
||||
2
|
||||
],
|
||||
"created": "2022-03-24T07:24:18Z",
|
||||
"created_date": "2022-03-24",
|
||||
"modified": "2022-03-24T07:24:23.264859Z",
|
||||
"added": "2022-03-24T07:24:22.922631Z",
|
||||
"archive_serial_number": null,
|
||||
"original_file_name": "2022-03-24 dolor.pdf",
|
||||
"archived_file_name": "2022-03-24 dolor.pdf",
|
||||
"owner": null,
|
||||
"user_can_change": true,
|
||||
"permissions": {
|
||||
"view": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
},
|
||||
"change": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
}
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"correspondent": 9,
|
||||
"document_type": 2,
|
||||
"storage_path": null,
|
||||
"title": "sit amet",
|
||||
"content": "Test document PDF",
|
||||
"tags": [
|
||||
4, 5
|
||||
],
|
||||
"created": "2022-06-01T07:24:18Z",
|
||||
"created_date": "2022-06-01",
|
||||
"modified": "2022-06-01T07:24:23.264859Z",
|
||||
"added": "2022-06-01T07:24:22.922631Z",
|
||||
"archive_serial_number": 12347,
|
||||
"original_file_name": "2022-06-01 sit amet.pdf",
|
||||
"archived_file_name": "2022-06-01 sit amet.pdf",
|
||||
"owner": null,
|
||||
"user_can_change": true,
|
||||
"permissions": {
|
||||
"view": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
},
|
||||
"change": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
}
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"correspondent": null,
|
||||
"document_type": null,
|
||||
"storage_path": null,
|
||||
"title": "Doc 5",
|
||||
"content": "Test document 5",
|
||||
"tags": [],
|
||||
"created": "2023-05-01T07:24:18Z",
|
||||
"created_date": "2023-05-02",
|
||||
"modified": "2023-05-02T07:24:23.264859Z",
|
||||
"added": "2023-05-02T07:24:22.922631Z",
|
||||
"archive_serial_number": null,
|
||||
"original_file_name": "doc5.pdf",
|
||||
"archived_file_name": "doc5.pdf",
|
||||
"owner": 15,
|
||||
"user_can_change": true,
|
||||
"permissions": {
|
||||
"view": {
|
||||
"users": [1],
|
||||
"groups": []
|
||||
},
|
||||
"change": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
}
|
||||
},
|
||||
"notes": []
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"correspondent": null,
|
||||
"document_type": null,
|
||||
"storage_path": null,
|
||||
"title": "Doc 6",
|
||||
"content": "Test document 6",
|
||||
"tags": [],
|
||||
"created": "2023-05-01T10:24:18Z",
|
||||
"created_date": "2023-05-02",
|
||||
"modified": "2023-05-02T10:24:23.264859Z",
|
||||
"added": "2023-05-02T10:24:22.922631Z",
|
||||
"archive_serial_number": null,
|
||||
"original_file_name": "doc6.pdf",
|
||||
"archived_file_name": "doc6.pdf",
|
||||
"owner": 6,
|
||||
"user_can_change": true,
|
||||
"permissions": {
|
||||
"view": {
|
||||
"users": [1],
|
||||
"groups": []
|
||||
},
|
||||
"change": {
|
||||
"users": [],
|
||||
"groups": []
|
||||
}
|
||||
},
|
||||
"notes": []
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 156 KiB |
@@ -1,293 +0,0 @@
|
||||
{
|
||||
"selected_correspondents": [
|
||||
{
|
||||
"id": 62,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 55,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 56,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 58,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 74,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 54,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 71,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 68,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 82,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 51,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 43,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 80,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 70,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 52,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 67,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 53,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 63,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 79,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 72,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 78,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 57,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 61,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 81,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 77,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 69,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"document_count": 3
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 50,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 49,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 60,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 47,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 66,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 59,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 76,
|
||||
"document_count": 0
|
||||
}
|
||||
],
|
||||
"selected_tags": [
|
||||
{
|
||||
"id": 4,
|
||||
"document_count": 2
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"document_count": 1
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"document_count": 1
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"document_count": 0
|
||||
}
|
||||
],
|
||||
"selected_document_types": [
|
||||
{
|
||||
"id": 4,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"document_count": 2
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"document_count": 1
|
||||
}
|
||||
],
|
||||
"selected_storage_paths": []
|
||||
}
|
@@ -1,119 +0,0 @@
|
||||
{
|
||||
"count": 2,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Another Group",
|
||||
"permissions": [
|
||||
"add_user",
|
||||
"change_user",
|
||||
"delete_user",
|
||||
"view_user",
|
||||
"add_note",
|
||||
"change_note",
|
||||
"delete_note",
|
||||
"view_note"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "First Group",
|
||||
"permissions": [
|
||||
"add_group",
|
||||
"change_group",
|
||||
"delete_group",
|
||||
"view_group",
|
||||
"add_permission",
|
||||
"change_permission",
|
||||
"delete_permission",
|
||||
"view_permission",
|
||||
"add_token",
|
||||
"change_token",
|
||||
"delete_token",
|
||||
"view_token",
|
||||
"add_tokenproxy",
|
||||
"change_tokenproxy",
|
||||
"delete_tokenproxy",
|
||||
"view_tokenproxy",
|
||||
"add_contenttype",
|
||||
"change_contenttype",
|
||||
"delete_contenttype",
|
||||
"view_contenttype",
|
||||
"add_chordcounter",
|
||||
"change_chordcounter",
|
||||
"delete_chordcounter",
|
||||
"view_chordcounter",
|
||||
"add_groupresult",
|
||||
"change_groupresult",
|
||||
"delete_groupresult",
|
||||
"view_groupresult",
|
||||
"add_taskresult",
|
||||
"change_taskresult",
|
||||
"delete_taskresult",
|
||||
"view_taskresult",
|
||||
"add_failure",
|
||||
"change_failure",
|
||||
"delete_failure",
|
||||
"view_failure",
|
||||
"add_ormq",
|
||||
"change_ormq",
|
||||
"delete_ormq",
|
||||
"view_ormq",
|
||||
"add_schedule",
|
||||
"change_schedule",
|
||||
"delete_schedule",
|
||||
"view_schedule",
|
||||
"add_success",
|
||||
"change_success",
|
||||
"delete_success",
|
||||
"view_success",
|
||||
"add_task",
|
||||
"change_task",
|
||||
"delete_task",
|
||||
"view_task",
|
||||
"add_note",
|
||||
"change_note",
|
||||
"delete_note",
|
||||
"view_note",
|
||||
"add_correspondent",
|
||||
"change_correspondent",
|
||||
"delete_correspondent",
|
||||
"view_correspondent",
|
||||
"add_document",
|
||||
"change_document",
|
||||
"delete_document",
|
||||
"view_document",
|
||||
"add_documenttype",
|
||||
"change_documenttype",
|
||||
"delete_documenttype",
|
||||
"view_documenttype",
|
||||
"add_frontendsettings",
|
||||
"change_frontendsettings",
|
||||
"delete_frontendsettings",
|
||||
"view_frontendsettings",
|
||||
"add_log",
|
||||
"change_log",
|
||||
"delete_log",
|
||||
"view_log",
|
||||
"add_savedview",
|
||||
"change_savedview",
|
||||
"delete_savedview",
|
||||
"view_savedview",
|
||||
"add_savedviewfilterrule",
|
||||
"change_savedviewfilterrule",
|
||||
"delete_savedviewfilterrule",
|
||||
"view_savedviewfilterrule",
|
||||
"add_taskattributes",
|
||||
"change_taskattributes",
|
||||
"delete_taskattributes",
|
||||
"view_taskattributes",
|
||||
"add_session",
|
||||
"change_session",
|
||||
"delete_session",
|
||||
"view_session"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"count": 2,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "IMAP Server",
|
||||
"imap_server": "imap.example.com",
|
||||
"imap_port": 993,
|
||||
"imap_security": 2,
|
||||
"username": "inbox@example.com",
|
||||
"password": "pass",
|
||||
"character_set": "UTF-8"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Gmail",
|
||||
"imap_server": "imap.gmail.com",
|
||||
"imap_port": 993,
|
||||
"imap_security": 2,
|
||||
"username": "user@gmail.com",
|
||||
"password": "pass",
|
||||
"character_set": "UTF-8"
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"count": 1,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Gmail",
|
||||
"account": 2,
|
||||
"folder": "INBOX",
|
||||
"filter_from": null,
|
||||
"filter_to": null,
|
||||
"filter_subject": "[paperless]",
|
||||
"filter_body": null,
|
||||
"filter_attachment_filename": null,
|
||||
"maximum_age": 30,
|
||||
"action": 3,
|
||||
"action_parameter": null,
|
||||
"assign_title_from": 1,
|
||||
"assign_tags": [
|
||||
9
|
||||
],
|
||||
"assign_correspondent_from": 1,
|
||||
"assign_correspondent": 2,
|
||||
"assign_document_type": null,
|
||||
"order": 0,
|
||||
"attachment_type": 2,
|
||||
"consumption_scope": 1
|
||||
}
|
||||
]
|
||||
}
|
@@ -1 +0,0 @@
|
||||
{"version":"v1.7.1","update_available":false,"feature_is_set":true}
|
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"count": 3,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Inbox",
|
||||
"show_on_dashboard": true,
|
||||
"show_in_sidebar": true,
|
||||
"sort_field": "created",
|
||||
"sort_reverse": true,
|
||||
"filter_rules": [
|
||||
{
|
||||
"rule_type": 6,
|
||||
"value": "18"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Recently Added",
|
||||
"show_on_dashboard": true,
|
||||
"show_in_sidebar": false,
|
||||
"sort_field": "created",
|
||||
"sort_reverse": true,
|
||||
"filter_rules": []
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Taxes",
|
||||
"show_on_dashboard": false,
|
||||
"show_in_sidebar": true,
|
||||
"sort_field": "created",
|
||||
"sort_reverse": true,
|
||||
"filter_rules": [
|
||||
{
|
||||
"rule_type": 6,
|
||||
"value": "39"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"count": 1,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 2,
|
||||
"slug": "year-title",
|
||||
"name": "Year - Title",
|
||||
"path": "{created_year}/{title}",
|
||||
"match": "",
|
||||
"matching_algorithm": 6,
|
||||
"is_insensitive": true,
|
||||
"document_count": 1
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,103 +0,0 @@
|
||||
{
|
||||
"count": 8,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 4,
|
||||
"slug": "another-sample-tag",
|
||||
"name": "Another Sample Tag",
|
||||
"color": "#a6cee3",
|
||||
"text_color": "#000000",
|
||||
"match": "",
|
||||
"matching_algorithm": 6,
|
||||
"is_insensitive": true,
|
||||
"is_inbox_tag": false,
|
||||
"document_count": 3
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"slug": "newone",
|
||||
"name": "NewOne",
|
||||
"color": "#9e4ad1",
|
||||
"text_color": "#ffffff",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"is_inbox_tag": false,
|
||||
"document_count": 2
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"slug": "partial-tag",
|
||||
"name": "Partial Tag",
|
||||
"color": "#72dba7",
|
||||
"text_color": "#000000",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"is_inbox_tag": false,
|
||||
"document_count": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"slug": "tag-2",
|
||||
"name": "Tag 2",
|
||||
"color": "#612db7",
|
||||
"text_color": "#ffffff",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"is_inbox_tag": false,
|
||||
"document_count": 3
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"slug": "tag-3",
|
||||
"name": "Tag 3",
|
||||
"color": "#b2df8a",
|
||||
"text_color": "#000000",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"is_inbox_tag": false,
|
||||
"document_count": 4
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"slug": "tagwithpartial",
|
||||
"name": "TagWithPartial",
|
||||
"color": "#3b2db4",
|
||||
"text_color": "#ffffff",
|
||||
"match": "",
|
||||
"matching_algorithm": 6,
|
||||
"is_insensitive": true,
|
||||
"is_inbox_tag": false,
|
||||
"document_count": 2
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"slug": "test-another",
|
||||
"name": "Test Another",
|
||||
"color": "#3ccea5",
|
||||
"text_color": "#000000",
|
||||
"match": "",
|
||||
"matching_algorithm": 4,
|
||||
"is_insensitive": true,
|
||||
"is_inbox_tag": false,
|
||||
"document_count": 0
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"slug": "test-tag",
|
||||
"name": "Test Tag",
|
||||
"color": "#fb9a99",
|
||||
"text_color": "#000000",
|
||||
"match": "",
|
||||
"matching_algorithm": 1,
|
||||
"is_insensitive": true,
|
||||
"is_inbox_tag": false,
|
||||
"document_count": 4
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,142 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 141,
|
||||
"type": "file",
|
||||
"result": "sample 2.pdf: Not consuming sample 2.pdf: It is a duplicate. : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ngx.nosync-udqDZzaE/lib/python3.8/site-packages/django_q/cluster.py\", line 432, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/paperless-ngx/src/documents/tasks.py\", line 316, in consume_file\n document = Consumer().try_consume_file(\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 218, in try_consume_file\n self.pre_check_duplicate()\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 113, in pre_check_duplicate\n self._fail(\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 84, in _fail\n raise ConsumerError(f\"{self.filename}: {log_message or message}\")\ndocuments.consumer.ConsumerError: sample 2.pdf: Not consuming sample 2.pdf: It is a duplicate.\n",
|
||||
"status": "FAILURE",
|
||||
"task_id": "d8ddbe298a42427d82553206ddf0bc94",
|
||||
"task_file_name": "sample 2.pdf",
|
||||
"date_created": "2022-05-26T23:17:38.333474-07:00",
|
||||
"date_done": null,
|
||||
"acknowledged": false,
|
||||
"related_document": null
|
||||
},
|
||||
{
|
||||
"id": 132,
|
||||
"type": "file",
|
||||
"result": " : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 131, in get_version\n env=env,\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 68, in run\n proc = subprocess_run(args, env=env, **kwargs)\n File \"/Users/admin/opt/anaconda3/envs/paperless-ng/lib/python3.6/subprocess.py\", line 423, in run\n with Popen(*popenargs, **kwargs) as process:\n File \"/Users/admin/opt/anaconda3/envs/paperless-ng/lib/python3.6/subprocess.py\", line 729, in __init__\n restore_signals, start_new_session)\n File \"/Users/admin/opt/anaconda3/envs/paperless-ng/lib/python3.6/subprocess.py\", line 1364, in _execute_child\n raise child_exception_type(errno_num, err_msg, err_filename)\nFileNotFoundError: [Errno 2] No such file or directory: 'unpaper': 'unpaper'\n\nThe above exception was the direct cause of the following exception:\n\nTraceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 287, in check_external_program\n found_version = version_checker()\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/_exec/unpaper.py\", line 34, in version\n return get_version('unpaper')\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 137, in get_version\n ) from e\nocrmypdf.exceptions.MissingDependencyError: Could not find program 'unpaper' on the PATH\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/paperless_tesseract/parsers.py\", line 176, in parse\n ocrmypdf.ocr(**ocr_args)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/api.py\", line 315, in ocr\n check_options(options, plugin_manager)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/_validation.py\", line 260, in check_options\n _check_options(options, plugin_manager, ocr_engine_languages)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/_validation.py\", line 250, in _check_options\n check_options_preprocessing(options)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/_validation.py\", line 128, in check_options_preprocessing\n required_for=['--clean, --clean-final'],\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 293, in check_external_program\n raise MissingDependencyError()\nocrmypdf.exceptions.MissingDependencyError\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 179, in try_consume_file\n document_parser.parse(self.path, mime_type, self.filename)\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/paperless_tesseract/parsers.py\", line 197, in parse\n raise ParseError(e)\ndocuments.parsers.ParseError\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/django_q/cluster.py\", line 436, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/tasks.py\", line 73, in consume_file\n override_tag_ids=override_tag_ids)\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 196, in try_consume_file\n raise ConsumerError(e)\ndocuments.consumer.ConsumerError\n",
|
||||
"status": "FAILURE",
|
||||
"task_id": "4c554075552c4cc985abd76e6f274c90",
|
||||
"task_file_name": "pdf-sample 10.24.48 PM.pdf",
|
||||
"date_created": "2022-05-26T14:26:07.846365-07:00",
|
||||
"date_done": null,
|
||||
"acknowledged": null
|
||||
},
|
||||
{
|
||||
"id": 115,
|
||||
"type": "file",
|
||||
"result": "2021-01-24 2021-01-20 sample_wide_orange.pdf: Document is a duplicate : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/django_q/cluster.py\", line 436, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/tasks.py\", line 75, in consume_file\n task_id=task_id\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 168, in try_consume_file\n self.pre_check_duplicate()\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 85, in pre_check_duplicate\n self._fail(\"Document is a duplicate\")\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 53, in _fail\n raise ConsumerError(f\"{self.filename}: {message}\")\ndocuments.consumer.ConsumerError: 2021-01-24 2021-01-20 sample_wide_orange.pdf: Document is a duplicate\n",
|
||||
"status": "FAILURE",
|
||||
"task_id": "86494713646a4364b01da17aadca071d",
|
||||
"task_file_name": "2021-01-24 2021-01-20 sample_wide_orange.pdf",
|
||||
"date_created": "2022-05-26T14:26:07.817608-07:00",
|
||||
"date_done": null,
|
||||
"acknowledged": null
|
||||
},
|
||||
{
|
||||
"id": 85,
|
||||
"type": "file",
|
||||
"result": "cannot open resource : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/django_q/cluster.py\", line 436, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/tasks.py\", line 81, in consume_file\n task_id=task_id\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 244, in try_consume_file\n self.path, mime_type, self.filename)\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/parsers.py\", line 302, in get_optimised_thumbnail\n thumbnail = self.get_thumbnail(document_path, mime_type, file_name)\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/paperless_text/parsers.py\", line 29, in get_thumbnail\n layout_engine=ImageFont.LAYOUT_BASIC)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/PIL/ImageFont.py\", line 852, in truetype\n return freetype(font)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/PIL/ImageFont.py\", line 849, in freetype\n return FreeTypeFont(font, size, index, encoding, layout_engine)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/PIL/ImageFont.py\", line 210, in __init__\n font, size, index, encoding, layout_engine=layout_engine\nOSError: cannot open resource\n",
|
||||
"status": "FAILURE",
|
||||
"task_id": "abca803fa46342e1ac81f3d3f2080e79",
|
||||
"task_file_name": "simple.txt",
|
||||
"date_created": "2022-05-26T14:26:07.771541-07:00",
|
||||
"date_done": null,
|
||||
"acknowledged": null
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"type": "file",
|
||||
"result": "commands.txt: Not consuming commands.txt: It is a duplicate. : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ngx.nosync-udqDZzaE/lib/python3.8/site-packages/django_q/cluster.py\", line 432, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/paperless-ngx/src/documents/tasks.py\", line 70, in consume_file\n document = Consumer().try_consume_file(\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 199, in try_consume_file\n self.pre_check_duplicate()\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 97, in pre_check_duplicate\n self._fail(\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 69, in _fail\n raise ConsumerError(f\"{self.filename}: {log_message or message}\")\ndocuments.consumer.ConsumerError: commands.txt: Not consuming commands.txt: It is a duplicate.\n",
|
||||
"status": "FAILURE",
|
||||
"task_id": "0af67672e8e14404b060d4cf8f69313d",
|
||||
"task_file_name": "commands.txt",
|
||||
"date_created": "2022-05-26T14:26:07.704247-07:00",
|
||||
"date_done": null,
|
||||
"acknowledged": null
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "file",
|
||||
"result": "Success. New document id 260 created",
|
||||
"status": "SUCCESS",
|
||||
"task_id": "b7629a0f41bd40c7a3ea4680341321b5",
|
||||
"task_file_name": "2022-03-24+Sonstige+ScanPC2022-03-24_081058.pdf",
|
||||
"date_created": "2022-05-26T14:26:07.670577-07:00",
|
||||
"date_done": "2022-05-26T14:26:07.670577-07:00",
|
||||
"acknowledged": false,
|
||||
"related_document": 260
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"type": "file",
|
||||
"result": "Success. New document id 261 created",
|
||||
"status": "SUCCESS",
|
||||
"task_id": "02e276a86a424ccfb83309df5d8594be",
|
||||
"task_file_name": "2sample-pdf-with-images.pdf",
|
||||
"date_created": "2022-05-26T14:26:07.668987-07:00",
|
||||
"date_done": "2022-05-26T14:26:07.668987-07:00",
|
||||
"acknowledged": false,
|
||||
"related_document": 261
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "file",
|
||||
"result": "Success. New document id 262 created",
|
||||
"status": "SUCCESS",
|
||||
"task_id": "41229b8be9b445c0a523697d0f58f13e",
|
||||
"task_file_name": "2sample-pdf-with-images_pw.pdf",
|
||||
"date_created": "2022-05-26T14:26:07.667993-07:00",
|
||||
"date_done": "2022-05-26T14:26:07.667993-07:00",
|
||||
"acknowledged": false,
|
||||
"related_document": 262
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "file",
|
||||
"result": "Success. New document id 264 created",
|
||||
"status": "SUCCESS",
|
||||
"task_id": "bbbca32d408c4619bd0b512a8327c773",
|
||||
"task_file_name": "homebridge.log",
|
||||
"date_created": "2022-05-26T14:26:07.665560-07:00",
|
||||
"date_done": "2022-05-26T14:26:07.665560-07:00",
|
||||
"acknowledged": false,
|
||||
"related_document": 264
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "file",
|
||||
"result": "Success. New document id 265 created",
|
||||
"status": "SUCCESS",
|
||||
"task_id": "00ab285ab4bf482ab30c7d580b252ecb",
|
||||
"task_file_name": "IMG_7459.PNG",
|
||||
"date_created": "2022-05-26T14:26:07.664506-07:00",
|
||||
"date_done": "2022-05-26T14:26:07.664506-07:00",
|
||||
"acknowledged": false,
|
||||
"related_document": 265
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "file",
|
||||
"result": "Success. New document id 267 created",
|
||||
"status": "SUCCESS",
|
||||
"task_id": "289c5163cfec410db42948a0cacbeb9c",
|
||||
"task_file_name": "IMG_7459.PNG",
|
||||
"date_created": "2022-05-26T14:26:07.659661-07:00",
|
||||
"date_done": "2022-05-26T14:26:07.659661-07:00",
|
||||
"acknowledged": false,
|
||||
"related_document": 267
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"type": "file",
|
||||
"result": null,
|
||||
"status": "STARTED",
|
||||
"task_id": "7a4ebdb2bde04311935284027ef8ca65",
|
||||
"task_file_name": "2019-08-04 DSA Questionnaire - 5-8-19.pdf",
|
||||
"date_created": "2022-05-26T14:26:07.655276-07:00",
|
||||
"date_done": null,
|
||||
"acknowledged": false,
|
||||
"related_document": null
|
||||
}
|
||||
]
|
@@ -1,163 +0,0 @@
|
||||
{
|
||||
"user": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"is_superuser": true,
|
||||
"groups": []
|
||||
},
|
||||
"settings": {
|
||||
"language": "",
|
||||
"bulk_edit": {
|
||||
"confirmation_dialogs": true,
|
||||
"apply_on_close": false
|
||||
},
|
||||
"documentListSize": 50,
|
||||
"dark_mode": {
|
||||
"use_system": true,
|
||||
"enabled": "false",
|
||||
"thumb_inverted": "true"
|
||||
},
|
||||
"theme": {
|
||||
"color": "#b198e5"
|
||||
},
|
||||
"document_details": {
|
||||
"native_pdf_viewer": false
|
||||
},
|
||||
"date_display": {
|
||||
"date_locale": "",
|
||||
"date_format": "mediumDate"
|
||||
},
|
||||
"notifications": {
|
||||
"consumer_new_documents": true,
|
||||
"consumer_success": true,
|
||||
"consumer_failed": true,
|
||||
"consumer_suppress_on_dashboard": true
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"add_logentry",
|
||||
"change_logentry",
|
||||
"delete_logentry",
|
||||
"view_logentry",
|
||||
"add_group",
|
||||
"change_group",
|
||||
"delete_group",
|
||||
"view_group",
|
||||
"add_permission",
|
||||
"change_permission",
|
||||
"delete_permission",
|
||||
"view_permission",
|
||||
"add_user",
|
||||
"change_user",
|
||||
"delete_user",
|
||||
"view_user",
|
||||
"add_token",
|
||||
"change_token",
|
||||
"delete_token",
|
||||
"view_token",
|
||||
"add_tokenproxy",
|
||||
"change_tokenproxy",
|
||||
"delete_tokenproxy",
|
||||
"view_tokenproxy",
|
||||
"add_contenttype",
|
||||
"change_contenttype",
|
||||
"delete_contenttype",
|
||||
"view_contenttype",
|
||||
"add_chordcounter",
|
||||
"change_chordcounter",
|
||||
"delete_chordcounter",
|
||||
"view_chordcounter",
|
||||
"add_groupresult",
|
||||
"change_groupresult",
|
||||
"delete_groupresult",
|
||||
"view_groupresult",
|
||||
"add_taskresult",
|
||||
"change_taskresult",
|
||||
"delete_taskresult",
|
||||
"view_taskresult",
|
||||
"add_failure",
|
||||
"change_failure",
|
||||
"delete_failure",
|
||||
"view_failure",
|
||||
"add_ormq",
|
||||
"change_ormq",
|
||||
"delete_ormq",
|
||||
"view_ormq",
|
||||
"add_schedule",
|
||||
"change_schedule",
|
||||
"delete_schedule",
|
||||
"view_schedule",
|
||||
"add_success",
|
||||
"change_success",
|
||||
"delete_success",
|
||||
"view_success",
|
||||
"add_task",
|
||||
"change_task",
|
||||
"delete_task",
|
||||
"view_task",
|
||||
"add_note",
|
||||
"change_note",
|
||||
"delete_note",
|
||||
"view_note",
|
||||
"add_correspondent",
|
||||
"change_correspondent",
|
||||
"delete_correspondent",
|
||||
"view_correspondent",
|
||||
"add_document",
|
||||
"change_document",
|
||||
"delete_document",
|
||||
"view_document",
|
||||
"add_documenttype",
|
||||
"change_documenttype",
|
||||
"delete_documenttype",
|
||||
"view_documenttype",
|
||||
"add_frontendsettings",
|
||||
"change_frontendsettings",
|
||||
"delete_frontendsettings",
|
||||
"view_frontendsettings",
|
||||
"add_log",
|
||||
"change_log",
|
||||
"delete_log",
|
||||
"view_log",
|
||||
"add_paperlesstask",
|
||||
"change_paperlesstask",
|
||||
"delete_paperlesstask",
|
||||
"view_paperlesstask",
|
||||
"add_savedview",
|
||||
"change_savedview",
|
||||
"delete_savedview",
|
||||
"view_savedview",
|
||||
"add_savedviewfilterrule",
|
||||
"change_savedviewfilterrule",
|
||||
"delete_savedviewfilterrule",
|
||||
"view_savedviewfilterrule",
|
||||
"add_storagepath",
|
||||
"change_storagepath",
|
||||
"delete_storagepath",
|
||||
"view_storagepath",
|
||||
"add_tag",
|
||||
"change_tag",
|
||||
"delete_tag",
|
||||
"view_tag",
|
||||
"add_taskattributes",
|
||||
"change_taskattributes",
|
||||
"delete_taskattributes",
|
||||
"view_taskattributes",
|
||||
"add_uisettings",
|
||||
"change_uisettings",
|
||||
"delete_uisettings",
|
||||
"view_uisettings",
|
||||
"add_mailaccount",
|
||||
"change_mailaccount",
|
||||
"delete_mailaccount",
|
||||
"view_mailaccount",
|
||||
"add_mailrule",
|
||||
"change_mailrule",
|
||||
"delete_mailrule",
|
||||
"view_mailrule",
|
||||
"add_session",
|
||||
"change_session",
|
||||
"delete_session",
|
||||
"view_session"
|
||||
]
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"user": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"is_superuser": false,
|
||||
"groups": []
|
||||
},
|
||||
"settings": {
|
||||
"language": "",
|
||||
"bulk_edit": {
|
||||
"confirmation_dialogs": true,
|
||||
"apply_on_close": false
|
||||
},
|
||||
"documentListSize": 50,
|
||||
"dark_mode": {
|
||||
"use_system": true,
|
||||
"enabled": "false",
|
||||
"thumb_inverted": "true"
|
||||
},
|
||||
"theme": {
|
||||
"color": "#b198e5"
|
||||
},
|
||||
"document_details": {
|
||||
"native_pdf_viewer": false
|
||||
},
|
||||
"date_display": {
|
||||
"date_locale": "",
|
||||
"date_format": "mediumDate"
|
||||
},
|
||||
"notifications": {
|
||||
"consumer_new_documents": true,
|
||||
"consumer_success": true,
|
||||
"consumer_failed": true,
|
||||
"consumer_suppress_on_dashboard": true
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"add_token",
|
||||
"change_token",
|
||||
"delete_token",
|
||||
"view_token",
|
||||
"add_tokenproxy",
|
||||
"change_tokenproxy",
|
||||
"delete_tokenproxy",
|
||||
"view_tokenproxy",
|
||||
"add_contenttype",
|
||||
"change_contenttype",
|
||||
"delete_contenttype",
|
||||
"view_contenttype",
|
||||
"add_chordcounter",
|
||||
"change_chordcounter",
|
||||
"delete_chordcounter",
|
||||
"view_chordcounter",
|
||||
"add_groupresult",
|
||||
"change_groupresult",
|
||||
"delete_groupresult",
|
||||
"view_groupresult",
|
||||
"add_failure",
|
||||
"change_failure",
|
||||
"delete_failure",
|
||||
"view_failure",
|
||||
"add_ormq",
|
||||
"change_ormq",
|
||||
"delete_ormq",
|
||||
"view_ormq",
|
||||
"add_schedule",
|
||||
"change_schedule",
|
||||
"delete_schedule",
|
||||
"view_schedule",
|
||||
"add_success",
|
||||
"change_success",
|
||||
"delete_success",
|
||||
"view_success",
|
||||
"add_task",
|
||||
"change_task",
|
||||
"delete_task",
|
||||
"view_task",
|
||||
"add_note",
|
||||
"add_frontendsettings",
|
||||
"change_frontendsettings",
|
||||
"delete_frontendsettings",
|
||||
"view_frontendsettings",
|
||||
"add_session",
|
||||
"change_session",
|
||||
"delete_session",
|
||||
"view_session"
|
||||
]
|
||||
}
|
@@ -1,459 +0,0 @@
|
||||
{
|
||||
"count": 4,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 3,
|
||||
"username": "admin",
|
||||
"password": "**********",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"date_joined": "2022-02-14T23:11:09.103293Z",
|
||||
"is_staff": true,
|
||||
"is_active": true,
|
||||
"is_superuser": true,
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"inherited_permissions": [
|
||||
"auth.delete_permission",
|
||||
"paperless_mail.change_mailrule",
|
||||
"django_celery_results.add_taskresult",
|
||||
"documents.view_taskattributes",
|
||||
"documents.view_paperlesstask",
|
||||
"django_q.add_success",
|
||||
"documents.view_uisettings",
|
||||
"auth.change_user",
|
||||
"admin.delete_logentry",
|
||||
"django_celery_results.change_taskresult",
|
||||
"django_q.change_schedule",
|
||||
"django_celery_results.delete_taskresult",
|
||||
"paperless_mail.add_mailaccount",
|
||||
"auth.change_group",
|
||||
"documents.add_note",
|
||||
"paperless_mail.delete_mailaccount",
|
||||
"authtoken.delete_tokenproxy",
|
||||
"guardian.delete_groupobjectpermission",
|
||||
"contenttypes.delete_contenttype",
|
||||
"documents.change_correspondent",
|
||||
"authtoken.delete_token",
|
||||
"documents.delete_documenttype",
|
||||
"django_q.change_ormq",
|
||||
"documents.change_savedviewfilterrule",
|
||||
"auth.delete_group",
|
||||
"documents.add_documenttype",
|
||||
"django_q.change_success",
|
||||
"documents.delete_tag",
|
||||
"documents.change_note",
|
||||
"django_q.delete_task",
|
||||
"documents.add_savedviewfilterrule",
|
||||
"django_q.view_task",
|
||||
"paperless_mail.add_mailrule",
|
||||
"paperless_mail.view_mailaccount",
|
||||
"documents.add_frontendsettings",
|
||||
"sessions.change_session",
|
||||
"documents.view_savedview",
|
||||
"authtoken.add_tokenproxy",
|
||||
"documents.change_tag",
|
||||
"documents.view_document",
|
||||
"documents.add_savedview",
|
||||
"auth.delete_user",
|
||||
"documents.view_log",
|
||||
"documents.view_note",
|
||||
"guardian.change_groupobjectpermission",
|
||||
"sessions.delete_session",
|
||||
"django_q.change_failure",
|
||||
"guardian.change_userobjectpermission",
|
||||
"documents.change_storagepath",
|
||||
"documents.delete_document",
|
||||
"documents.delete_taskattributes",
|
||||
"django_celery_results.change_groupresult",
|
||||
"django_q.add_ormq",
|
||||
"guardian.view_groupobjectpermission",
|
||||
"admin.change_logentry",
|
||||
"django_q.delete_schedule",
|
||||
"documents.delete_paperlesstask",
|
||||
"django_q.view_ormq",
|
||||
"documents.change_paperlesstask",
|
||||
"guardian.delete_userobjectpermission",
|
||||
"auth.view_permission",
|
||||
"auth.view_user",
|
||||
"django_q.add_schedule",
|
||||
"authtoken.change_token",
|
||||
"guardian.add_groupobjectpermission",
|
||||
"documents.view_documenttype",
|
||||
"documents.change_log",
|
||||
"paperless_mail.delete_mailrule",
|
||||
"auth.view_group",
|
||||
"authtoken.view_token",
|
||||
"admin.view_logentry",
|
||||
"django_celery_results.view_chordcounter",
|
||||
"django_celery_results.view_groupresult",
|
||||
"documents.view_storagepath",
|
||||
"documents.add_storagepath",
|
||||
"django_celery_results.add_groupresult",
|
||||
"documents.view_tag",
|
||||
"guardian.view_userobjectpermission",
|
||||
"documents.delete_correspondent",
|
||||
"documents.add_tag",
|
||||
"documents.delete_savedviewfilterrule",
|
||||
"documents.add_correspondent",
|
||||
"authtoken.view_tokenproxy",
|
||||
"documents.delete_frontendsettings",
|
||||
"django_celery_results.delete_chordcounter",
|
||||
"django_q.change_task",
|
||||
"documents.add_taskattributes",
|
||||
"documents.delete_storagepath",
|
||||
"sessions.add_session",
|
||||
"documents.add_uisettings",
|
||||
"documents.change_taskattributes",
|
||||
"documents.delete_uisettings",
|
||||
"django_q.delete_ormq",
|
||||
"auth.change_permission",
|
||||
"documents.view_savedviewfilterrule",
|
||||
"documents.change_frontendsettings",
|
||||
"documents.change_documenttype",
|
||||
"documents.view_correspondent",
|
||||
"auth.add_user",
|
||||
"paperless_mail.change_mailaccount",
|
||||
"documents.add_paperlesstask",
|
||||
"django_q.view_success",
|
||||
"django_celery_results.delete_groupresult",
|
||||
"documents.delete_savedview",
|
||||
"authtoken.change_tokenproxy",
|
||||
"documents.view_frontendsettings",
|
||||
"authtoken.add_token",
|
||||
"django_celery_results.add_chordcounter",
|
||||
"contenttypes.change_contenttype",
|
||||
"admin.add_logentry",
|
||||
"django_q.delete_failure",
|
||||
"documents.change_uisettings",
|
||||
"django_q.view_failure",
|
||||
"documents.add_log",
|
||||
"documents.change_savedview",
|
||||
"paperless_mail.view_mailrule",
|
||||
"django_q.view_schedule",
|
||||
"documents.change_document",
|
||||
"django_celery_results.change_chordcounter",
|
||||
"documents.add_document",
|
||||
"django_celery_results.view_taskresult",
|
||||
"contenttypes.add_contenttype",
|
||||
"django_q.delete_success",
|
||||
"documents.delete_note",
|
||||
"django_q.add_failure",
|
||||
"guardian.add_userobjectpermission",
|
||||
"sessions.view_session",
|
||||
"contenttypes.view_contenttype",
|
||||
"auth.add_permission",
|
||||
"documents.delete_log",
|
||||
"django_q.add_task",
|
||||
"auth.add_group"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"username": "test",
|
||||
"password": "**********",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"date_joined": "2022-11-23T08:30:54Z",
|
||||
"is_staff": true,
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"groups": [
|
||||
1
|
||||
],
|
||||
"user_permissions": [
|
||||
"add_group",
|
||||
"change_group",
|
||||
"delete_group",
|
||||
"view_group",
|
||||
"add_permission",
|
||||
"change_permission",
|
||||
"delete_permission",
|
||||
"view_permission",
|
||||
"add_token",
|
||||
"change_token",
|
||||
"delete_token",
|
||||
"view_token",
|
||||
"add_tokenproxy",
|
||||
"change_tokenproxy",
|
||||
"delete_tokenproxy",
|
||||
"view_tokenproxy",
|
||||
"add_contenttype",
|
||||
"change_contenttype",
|
||||
"delete_contenttype",
|
||||
"view_contenttype",
|
||||
"add_chordcounter",
|
||||
"change_chordcounter",
|
||||
"delete_chordcounter",
|
||||
"view_chordcounter",
|
||||
"add_groupresult",
|
||||
"change_groupresult",
|
||||
"delete_groupresult",
|
||||
"view_groupresult",
|
||||
"add_taskresult",
|
||||
"change_taskresult",
|
||||
"delete_taskresult",
|
||||
"view_taskresult",
|
||||
"add_failure",
|
||||
"change_failure",
|
||||
"delete_failure",
|
||||
"view_failure",
|
||||
"add_ormq",
|
||||
"change_ormq",
|
||||
"delete_ormq",
|
||||
"view_ormq",
|
||||
"add_schedule",
|
||||
"change_schedule",
|
||||
"delete_schedule",
|
||||
"view_schedule",
|
||||
"add_success",
|
||||
"change_success",
|
||||
"delete_success",
|
||||
"view_success",
|
||||
"add_task",
|
||||
"change_task",
|
||||
"delete_task",
|
||||
"view_task",
|
||||
"add_note",
|
||||
"change_note",
|
||||
"delete_note",
|
||||
"view_note",
|
||||
"add_frontendsettings",
|
||||
"change_frontendsettings",
|
||||
"delete_frontendsettings",
|
||||
"view_frontendsettings",
|
||||
"add_log",
|
||||
"change_log",
|
||||
"delete_log",
|
||||
"view_log",
|
||||
"add_savedviewfilterrule",
|
||||
"change_savedviewfilterrule",
|
||||
"delete_savedviewfilterrule",
|
||||
"view_savedviewfilterrule",
|
||||
"add_taskattributes",
|
||||
"change_taskattributes",
|
||||
"delete_taskattributes",
|
||||
"view_taskattributes",
|
||||
"add_session",
|
||||
"change_session",
|
||||
"delete_session",
|
||||
"view_session"
|
||||
],
|
||||
"inherited_permissions": [
|
||||
"auth.delete_permission",
|
||||
"django_celery_results.add_taskresult",
|
||||
"documents.view_taskattributes",
|
||||
"django_q.add_ormq",
|
||||
"django_q.add_success",
|
||||
"django_q.delete_schedule",
|
||||
"django_q.view_ormq",
|
||||
"auth.view_permission",
|
||||
"django_q.add_schedule",
|
||||
"django_celery_results.change_taskresult",
|
||||
"django_q.change_schedule",
|
||||
"django_celery_results.delete_taskresult",
|
||||
"authtoken.change_token",
|
||||
"auth.change_group",
|
||||
"documents.add_note",
|
||||
"authtoken.delete_tokenproxy",
|
||||
"documents.view_documenttype",
|
||||
"contenttypes.delete_contenttype",
|
||||
"documents.change_correspondent",
|
||||
"authtoken.delete_token",
|
||||
"documents.change_log",
|
||||
"auth.view_group",
|
||||
"authtoken.view_token",
|
||||
"django_celery_results.view_chordcounter",
|
||||
"django_celery_results.view_groupresult",
|
||||
"documents.delete_documenttype",
|
||||
"django_q.change_ormq",
|
||||
"documents.change_savedviewfilterrule",
|
||||
"django_celery_results.add_groupresult",
|
||||
"auth.delete_group",
|
||||
"documents.add_documenttype",
|
||||
"django_q.change_success",
|
||||
"auth.add_permission",
|
||||
"documents.delete_correspondent",
|
||||
"documents.delete_savedviewfilterrule",
|
||||
"documents.add_correspondent",
|
||||
"authtoken.view_tokenproxy",
|
||||
"documents.delete_frontendsettings",
|
||||
"django_celery_results.delete_chordcounter",
|
||||
"documents.add_taskattributes",
|
||||
"django_q.change_task",
|
||||
"sessions.add_session",
|
||||
"documents.change_taskattributes",
|
||||
"documents.change_note",
|
||||
"django_q.delete_task",
|
||||
"django_q.delete_ormq",
|
||||
"auth.change_permission",
|
||||
"documents.add_savedviewfilterrule",
|
||||
"django_q.view_task",
|
||||
"documents.view_savedviewfilterrule",
|
||||
"documents.change_frontendsettings",
|
||||
"documents.change_documenttype",
|
||||
"documents.view_correspondent",
|
||||
"django_q.view_success",
|
||||
"documents.add_frontendsettings",
|
||||
"django_celery_results.delete_groupresult",
|
||||
"documents.delete_savedview",
|
||||
"authtoken.change_tokenproxy",
|
||||
"documents.view_frontendsettings",
|
||||
"authtoken.add_token",
|
||||
"sessions.change_session",
|
||||
"django_celery_results.add_chordcounter",
|
||||
"documents.view_savedview",
|
||||
"contenttypes.change_contenttype",
|
||||
"django_q.delete_failure",
|
||||
"authtoken.add_tokenproxy",
|
||||
"documents.view_document",
|
||||
"documents.add_savedview",
|
||||
"django_q.view_failure",
|
||||
"documents.view_note",
|
||||
"documents.view_log",
|
||||
"documents.add_log",
|
||||
"documents.change_savedview",
|
||||
"django_q.view_schedule",
|
||||
"documents.change_document",
|
||||
"django_celery_results.change_chordcounter",
|
||||
"documents.add_document",
|
||||
"sessions.delete_session",
|
||||
"django_q.change_failure",
|
||||
"django_celery_results.view_taskresult",
|
||||
"contenttypes.add_contenttype",
|
||||
"django_q.delete_success",
|
||||
"documents.delete_note",
|
||||
"django_q.add_failure",
|
||||
"sessions.view_session",
|
||||
"contenttypes.view_contenttype",
|
||||
"documents.delete_taskattributes",
|
||||
"documents.delete_document",
|
||||
"documents.delete_log",
|
||||
"django_q.add_task",
|
||||
"django_celery_results.change_groupresult",
|
||||
"auth.add_group"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"username": "testuser",
|
||||
"password": "**********",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"date_joined": "2022-11-16T04:14:20.484914Z",
|
||||
"is_staff": false,
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"groups": [
|
||||
1,
|
||||
6
|
||||
],
|
||||
"user_permissions": [
|
||||
"add_logentry",
|
||||
"change_logentry",
|
||||
"delete_logentry",
|
||||
"view_logentry"
|
||||
],
|
||||
"inherited_permissions": [
|
||||
"auth.delete_permission",
|
||||
"django_celery_results.add_taskresult",
|
||||
"documents.view_taskattributes",
|
||||
"django_q.add_ormq",
|
||||
"django_q.add_success",
|
||||
"django_q.delete_schedule",
|
||||
"django_q.view_ormq",
|
||||
"auth.change_user",
|
||||
"auth.view_permission",
|
||||
"auth.view_user",
|
||||
"django_q.add_schedule",
|
||||
"django_celery_results.change_taskresult",
|
||||
"django_q.change_schedule",
|
||||
"django_celery_results.delete_taskresult",
|
||||
"authtoken.change_token",
|
||||
"auth.change_group",
|
||||
"documents.add_note",
|
||||
"authtoken.delete_tokenproxy",
|
||||
"documents.view_documenttype",
|
||||
"contenttypes.delete_contenttype",
|
||||
"documents.change_correspondent",
|
||||
"authtoken.delete_token",
|
||||
"documents.change_log",
|
||||
"auth.view_group",
|
||||
"authtoken.view_token",
|
||||
"django_celery_results.view_chordcounter",
|
||||
"django_celery_results.view_groupresult",
|
||||
"documents.delete_documenttype",
|
||||
"django_q.change_ormq",
|
||||
"documents.change_savedviewfilterrule",
|
||||
"django_celery_results.add_groupresult",
|
||||
"auth.delete_group",
|
||||
"documents.add_documenttype",
|
||||
"django_q.change_success",
|
||||
"auth.add_permission",
|
||||
"documents.delete_correspondent",
|
||||
"documents.delete_savedviewfilterrule",
|
||||
"documents.add_correspondent",
|
||||
"authtoken.view_tokenproxy",
|
||||
"documents.delete_frontendsettings",
|
||||
"django_celery_results.delete_chordcounter",
|
||||
"documents.add_taskattributes",
|
||||
"django_q.change_task",
|
||||
"sessions.add_session",
|
||||
"documents.change_taskattributes",
|
||||
"documents.change_note",
|
||||
"django_q.delete_task",
|
||||
"django_q.delete_ormq",
|
||||
"auth.change_permission",
|
||||
"documents.add_savedviewfilterrule",
|
||||
"django_q.view_task",
|
||||
"documents.view_savedviewfilterrule",
|
||||
"documents.change_frontendsettings",
|
||||
"documents.change_documenttype",
|
||||
"documents.view_correspondent",
|
||||
"auth.add_user",
|
||||
"django_q.view_success",
|
||||
"documents.add_frontendsettings",
|
||||
"django_celery_results.delete_groupresult",
|
||||
"documents.delete_savedview",
|
||||
"authtoken.change_tokenproxy",
|
||||
"documents.view_frontendsettings",
|
||||
"authtoken.add_token",
|
||||
"sessions.change_session",
|
||||
"django_celery_results.add_chordcounter",
|
||||
"documents.view_savedview",
|
||||
"contenttypes.change_contenttype",
|
||||
"django_q.delete_failure",
|
||||
"authtoken.add_tokenproxy",
|
||||
"documents.view_document",
|
||||
"documents.add_savedview",
|
||||
"django_q.view_failure",
|
||||
"documents.view_note",
|
||||
"documents.view_log",
|
||||
"auth.delete_user",
|
||||
"documents.add_log",
|
||||
"documents.change_savedview",
|
||||
"django_q.view_schedule",
|
||||
"documents.change_document",
|
||||
"django_celery_results.change_chordcounter",
|
||||
"documents.add_document",
|
||||
"sessions.delete_session",
|
||||
"django_q.change_failure",
|
||||
"django_celery_results.view_taskresult",
|
||||
"contenttypes.add_contenttype",
|
||||
"django_q.delete_success",
|
||||
"documents.delete_note",
|
||||
"django_q.add_failure",
|
||||
"sessions.view_session",
|
||||
"contenttypes.view_contenttype",
|
||||
"documents.delete_taskattributes",
|
||||
"documents.delete_document",
|
||||
"documents.delete_log",
|
||||
"django_q.add_task",
|
||||
"django_celery_results.change_groupresult",
|
||||
"auth.add_group"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
// Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
|
||||
// For more info, visit https://on.cypress.io/plugins-api
|
||||
module.exports = (on, config) => {}
|
@@ -1,43 +0,0 @@
|
||||
// ***********************************************
|
||||
// This example namespace declaration will help
|
||||
// with Intellisense and code completion in your
|
||||
// IDE or Text Editor.
|
||||
// ***********************************************
|
||||
// declare namespace Cypress {
|
||||
// interface Chainable<Subject = any> {
|
||||
// customCommand(param: any): typeof customCommand;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// function customCommand(param: any): void {
|
||||
// console.warn(param);
|
||||
// }
|
||||
//
|
||||
// NOTE: You can use it like so:
|
||||
// Cypress.Commands.add('customCommand', customCommand);
|
||||
//
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
@@ -1,55 +0,0 @@
|
||||
// mock API methods
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('http://localhost:8000/api/ui_settings/', {
|
||||
fixture: 'ui_settings/settings.json',
|
||||
}).as('ui-settings')
|
||||
|
||||
cy.intercept('http://localhost:8000/api/users/*', {
|
||||
fixture: 'users/users.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/groups/*', {
|
||||
fixture: 'groups/groups.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/remote_version/', {
|
||||
fixture: 'remote_version/remote_version.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/saved_views/*', {
|
||||
fixture: 'saved_views/savedviews.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/tags/*', {
|
||||
fixture: 'tags/tags.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/correspondents/*', {
|
||||
fixture: 'correspondents/correspondents.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/document_types/*', {
|
||||
fixture: 'document_types/doctypes.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/storage_paths/*', {
|
||||
fixture: 'storage_paths/storage_paths.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/tasks/', {
|
||||
fixture: 'tasks/tasks.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/documents/1/metadata/', {
|
||||
fixture: 'documents/1/metadata.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/documents/1/suggestions/', {
|
||||
fixture: 'documents/1/suggestions.json',
|
||||
})
|
||||
|
||||
cy.intercept('http://localhost:8000/api/documents/1/thumb/', {
|
||||
fixture: 'documents/lorem-ipsum.png',
|
||||
})
|
||||
})
|
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"types": ["cypress"]
|
||||
}
|
||||
}
|
61
src-ui/e2e/dashboard/dashboard.spec.ts
Normal file
61
src-ui/e2e/dashboard/dashboard.spec.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
const REQUESTS_HAR1 = 'e2e/dashboard/requests/api-dashboard1.har'
|
||||
const REQUESTS_HAR2 = 'e2e/dashboard/requests/api-dashboard2.har'
|
||||
const REQUESTS_HAR3 = 'e2e/dashboard/requests/api-dashboard3.har'
|
||||
const REQUESTS_HAR4 = 'e2e/dashboard/requests/api-dashboard4.har'
|
||||
|
||||
test('dashboard inbox link', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await page.getByRole('link', { name: 'Documents in inbox' }).click()
|
||||
await expect(page).toHaveURL(/tags__id__all=9/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/8 documents/)
|
||||
})
|
||||
|
||||
test('dashboard total documents link', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await page.getByRole('link').filter({ hasText: 'Total documents' }).click()
|
||||
await expect(page).toHaveURL(/documents/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/61 documents/)
|
||||
await page.getByRole('button', { name: 'Reset filters' })
|
||||
})
|
||||
|
||||
test('dashboard saved view show all', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR3, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await page
|
||||
.locator('app-widget-frame')
|
||||
.filter({ hasText: 'Inbox' })
|
||||
.getByRole('link', { name: 'Show all' })
|
||||
.click()
|
||||
await expect(page).toHaveURL(/view\/7/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/8 documents/)
|
||||
})
|
||||
|
||||
test('dashboard saved view document links', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR4, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await page
|
||||
.locator('app-widget-frame')
|
||||
.filter({ hasText: 'Inbox' })
|
||||
.locator('table')
|
||||
.getByRole('link', { name: /test/ })
|
||||
.first()
|
||||
.click({ position: { x: 0, y: 0 } })
|
||||
await expect(page).toHaveURL(/documents\/310\/details/)
|
||||
})
|
||||
|
||||
test('test slim sidebar', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await page.locator('#sidebarMenu').getByRole('button').click()
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Dashboard' }).getByText('Dashboard')
|
||||
).toBeHidden()
|
||||
await page.locator('#sidebarMenu').getByRole('button').click()
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Dashboard' }).getByText('Dashboard')
|
||||
).toBeVisible()
|
||||
})
|
1323
src-ui/e2e/dashboard/requests/api-dashboard1.har
Normal file
1323
src-ui/e2e/dashboard/requests/api-dashboard1.har
Normal file
File diff suppressed because one or more lines are too long
3333
src-ui/e2e/dashboard/requests/api-dashboard2.har
Normal file
3333
src-ui/e2e/dashboard/requests/api-dashboard2.har
Normal file
File diff suppressed because one or more lines are too long
1389
src-ui/e2e/dashboard/requests/api-dashboard3.har
Normal file
1389
src-ui/e2e/dashboard/requests/api-dashboard3.har
Normal file
File diff suppressed because one or more lines are too long
790
src-ui/e2e/dashboard/requests/api-dashboard4.har
Normal file
790
src-ui/e2e/dashboard/requests/api-dashboard4.har
Normal file
File diff suppressed because one or more lines are too long
149
src-ui/e2e/document-detail/document-detail.spec.ts
Normal file
149
src-ui/e2e/document-detail/document-detail.spec.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
const REQUESTS_HAR = 'e2e/document-detail/requests/api-document-detail.har'
|
||||
const REQUESTS_HAR2 = 'e2e/document-detail/requests/api-document-detail2.har'
|
||||
|
||||
test('should activate / deactivate save button when changes are saved', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/documents/175/')
|
||||
await page.waitForSelector('app-document-detail app-input-text:first-child')
|
||||
await expect(page.getByTitle('Storage path', { exact: true })).toHaveText(
|
||||
/\w+/
|
||||
)
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Save', exact: true })
|
||||
).toBeDisabled()
|
||||
await page.getByTitle('Storage path').getByTitle('Clear all').click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Save', exact: true })
|
||||
).toBeEnabled()
|
||||
})
|
||||
|
||||
test('should warn on unsaved changes', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/documents/175/')
|
||||
await expect(page.getByTitle('Correspondent', { exact: true })).toHaveText(
|
||||
/\w+/
|
||||
)
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Save', exact: true })
|
||||
).toBeDisabled()
|
||||
await page
|
||||
.getByTitle('Storage path', { exact: true })
|
||||
.getByTitle('Clear all')
|
||||
.click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Save', exact: true })
|
||||
).toBeEnabled()
|
||||
await page.getByRole('button', { name: 'Close', exact: true }).click()
|
||||
await expect(page.getByRole('dialog')).toHaveText(/unsaved changes/)
|
||||
await page.getByRole('button', { name: 'Cancel' }).click()
|
||||
await page.getByRole('link', { name: 'Close all' }).click()
|
||||
await expect(page.getByRole('dialog')).toHaveText(/unsaved changes/)
|
||||
})
|
||||
|
||||
test('should support tab direct navigation', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/documents/175/details')
|
||||
await expect(page.getByRole('tab', { name: 'Details' })).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
)
|
||||
await page.goto('/documents/175/content')
|
||||
await expect(page.getByRole('tab', { name: 'Content' })).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
)
|
||||
await page.goto('/documents/175/metadata')
|
||||
await expect(page.getByRole('tab', { name: 'Metadata' })).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
)
|
||||
await page.goto('/documents/175/notes')
|
||||
await expect(page.getByRole('tab', { name: 'Notes' })).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
)
|
||||
await page.goto('/documents/175/permissions')
|
||||
await expect(page.getByRole('tab', { name: 'Permissions' })).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
)
|
||||
})
|
||||
|
||||
test('should show a mobile preview', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/documents/175/')
|
||||
await page.setViewportSize({ width: 400, height: 1000 })
|
||||
await expect(page.getByRole('tab', { name: 'Preview' })).toBeVisible()
|
||||
await page.getByRole('tab', { name: 'Preview' }).click()
|
||||
await page.waitForSelector('pdf-viewer')
|
||||
})
|
||||
|
||||
test('should show a list of notes', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/documents/175/notes')
|
||||
await expect(page.locator('app-document-notes')).toBeVisible()
|
||||
await expect(
|
||||
await page.getByRole('button', {
|
||||
name: /delete note/i,
|
||||
includeHidden: true,
|
||||
})
|
||||
).toHaveCount(4)
|
||||
})
|
||||
|
||||
test('should support note deletion', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/documents/175/notes')
|
||||
await expect(page.locator('app-document-notes')).toBeVisible()
|
||||
const deletePromise = page.waitForRequest(
|
||||
(request) =>
|
||||
request.method() === 'DELETE' &&
|
||||
request.url().includes('/api/documents/175/notes/')
|
||||
)
|
||||
await page
|
||||
.getByRole('button', { name: /delete note/i, includeHidden: true })
|
||||
.first()
|
||||
.click()
|
||||
await deletePromise
|
||||
})
|
||||
|
||||
test('should support note insertion', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/documents/175/notes')
|
||||
await expect(page.locator('app-document-notes')).toBeVisible()
|
||||
await expect(
|
||||
await page.getByRole('button', {
|
||||
name: /delete note/i,
|
||||
includeHidden: true,
|
||||
})
|
||||
).toHaveCount(4)
|
||||
await page.getByPlaceholder('Enter note').fill('This is a new note')
|
||||
const addPromise = page.waitForRequest((request) => {
|
||||
if (!request.url().includes('/notes/')) {
|
||||
// ignore other requests
|
||||
return true
|
||||
} else {
|
||||
const data = request.postDataJSON()
|
||||
const isValid = data['note'] === 'This is a new note'
|
||||
return (
|
||||
isValid &&
|
||||
request.method() === 'POST' &&
|
||||
request.url().includes('/notes/')
|
||||
)
|
||||
}
|
||||
})
|
||||
await page.getByRole('button', { name: 'Add note' }).click()
|
||||
await addPromise
|
||||
})
|
||||
|
||||
test('should support quick filters', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
|
||||
await page.goto('/documents/175/details')
|
||||
await page
|
||||
.getByRole('button', { name: 'Filter documents with these Tags' })
|
||||
.click()
|
||||
await expect(page).toHaveURL(/tags__id__all=4&sort=created&reverse=1&page=1/)
|
||||
})
|
949
src-ui/e2e/document-detail/requests/api-document-detail.har
Normal file
949
src-ui/e2e/document-detail/requests/api-document-detail.har
Normal file
File diff suppressed because one or more lines are too long
2425
src-ui/e2e/document-detail/requests/api-document-detail2.har
Normal file
2425
src-ui/e2e/document-detail/requests/api-document-detail2.har
Normal file
File diff suppressed because one or more lines are too long
192
src-ui/e2e/document-list/document-list.spec.ts
Normal file
192
src-ui/e2e/document-list/document-list.spec.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
const REQUESTS_HAR1 = 'e2e/document-list/requests/api-document-list1.har'
|
||||
const REQUESTS_HAR2 = 'e2e/document-list/requests/api-document-list2.har'
|
||||
const REQUESTS_HAR3 = 'e2e/document-list/requests/api-document-list3.har'
|
||||
const REQUESTS_HAR4 = 'e2e/document-list/requests/api-document-list4.har'
|
||||
const REQUESTS_HAR5 = 'e2e/document-list/requests/api-document-list5.har'
|
||||
const REQUESTS_HAR6 = 'e2e/document-list/requests/api-document-list6.har'
|
||||
|
||||
test('basic filtering', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||
await page.goto('/documents')
|
||||
await page.getByRole('button', { name: 'Tags' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Inbox' }).click()
|
||||
await expect(page).toHaveURL(/tags__id__all=9/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/8 documents/)
|
||||
await page.getByRole('button', { name: 'Document type' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Invoice Test 3' }).click()
|
||||
await expect(page).toHaveURL(/document_type__id__in=1/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/3 documents/)
|
||||
await page.getByRole('button', { name: 'Reset filters' }).first().click()
|
||||
await page.getByRole('button', { name: 'Correspondent' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Test Correspondent 1' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Correspondent 9' }).click()
|
||||
await expect(page).toHaveURL(/correspondent__id__in=12,1/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/7 documents/)
|
||||
await page
|
||||
.locator('app-filter-editor')
|
||||
.getByTitle('Correspondent')
|
||||
.getByText('Exclude')
|
||||
.click()
|
||||
await expect(page).toHaveURL(/correspondent__id__none=12,1/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/54 documents/)
|
||||
// clear button
|
||||
await page.getByRole('button', { name: '2 selected', exact: true }).click()
|
||||
await expect(page.locator('app-document-list')).toHaveText(/61 documents/)
|
||||
await page.getByRole('button', { name: 'Storage path' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Testing 12' }).click()
|
||||
await expect(page).toHaveURL(/storage_path__id__in=5/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/8 documents/)
|
||||
await page.getByRole('button', { name: 'Reset filters' }).first().click()
|
||||
await expect(page.locator('app-document-list')).toHaveText(/61 documents/)
|
||||
})
|
||||
|
||||
test('text filtering', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
|
||||
await page.goto('/documents')
|
||||
await page.getByRole('textbox').click()
|
||||
await page.getByRole('textbox').fill('test')
|
||||
await expect(page.locator('app-document-list')).toHaveText(/32 documents/)
|
||||
await expect(page).toHaveURL(/title_content=test/)
|
||||
await page.getByRole('button', { name: 'Title & content' }).click()
|
||||
await page.getByRole('button', { name: 'Title', exact: true }).click()
|
||||
await expect(page.locator('app-document-list')).toHaveText(/9 documents/)
|
||||
await expect(page).toHaveURL(/title__icontains=test/)
|
||||
await page.getByRole('button', { name: 'Title', exact: true }).click()
|
||||
await page.getByRole('button', { name: 'Advanced search' }).click()
|
||||
await expect(page).toHaveURL(/query=test/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/26 documents/)
|
||||
await page.getByRole('button', { name: 'Advanced search' }).click()
|
||||
await page.getByRole('button', { name: 'ASN' }).click()
|
||||
await page.getByRole('textbox').fill('1123')
|
||||
await expect(page).toHaveURL(/archive_serial_number=1123/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/one document/i)
|
||||
await page.locator('select').selectOption('greater')
|
||||
await page.getByRole('textbox').click()
|
||||
await page.getByRole('textbox').fill('1123')
|
||||
await expect(page).toHaveURL(/archive_serial_number__gt=1123/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/5 documents/)
|
||||
await page.locator('select').selectOption('less')
|
||||
await expect(page).toHaveURL(/archive_serial_number__lt=1123/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/0 documents/)
|
||||
await page.locator('select').selectOption('is null')
|
||||
await expect(page).toHaveURL(/archive_serial_number__isnull=1/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/55 documents/)
|
||||
await page.locator('select').selectOption('not null')
|
||||
await expect(page).toHaveURL(/archive_serial_number__isnull=0/)
|
||||
await expect(page.locator('app-document-list')).toHaveText(/6 documents/)
|
||||
})
|
||||
|
||||
test('date filtering', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR3, { notFound: 'fallback' })
|
||||
await page.goto('/documents')
|
||||
await page.getByRole('button', { name: 'Created' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Last 3 months' }).click()
|
||||
await expect(page.locator('app-document-list')).toHaveText(/one document/i)
|
||||
await page.getByRole('button', { name: 'Created Clear selected' }).click()
|
||||
await page.getByRole('button', { name: 'Created' }).click()
|
||||
await page
|
||||
.getByRole('menuitem', { name: 'After mm/dd/yyyy' })
|
||||
.getByRole('button')
|
||||
.click()
|
||||
await page.getByRole('combobox', { name: 'Select month' }).selectOption('12')
|
||||
await page.getByRole('combobox', { name: 'Select year' }).selectOption('2022')
|
||||
await page.getByText('11', { exact: true }).click()
|
||||
await page.getByRole('button', { name: 'Title & content' }).click()
|
||||
await expect(page.locator('app-document-list')).toHaveText(/2 documents/)
|
||||
})
|
||||
|
||||
test('sorting', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR4, { notFound: 'fallback' })
|
||||
await page.goto('/documents')
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page.getByRole('button', { name: 'ASN' }).click()
|
||||
await expect(page).toHaveURL(/sort=archive_serial_number/)
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page
|
||||
.locator('app-page-header')
|
||||
.getByRole('button', { name: 'Correspondent' })
|
||||
.click()
|
||||
await expect(page).toHaveURL(/sort=correspondent__name/)
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page.getByRole('button', { name: 'Title', exact: true }).click()
|
||||
await expect(page).toHaveURL(/sort=title/)
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page
|
||||
.locator('app-page-header')
|
||||
.getByRole('button', { name: 'Document type' })
|
||||
.click()
|
||||
await expect(page).toHaveURL(/sort=document_type__name/)
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page.getByRole('button', { name: 'Created', exact: true }).click()
|
||||
await expect(page).toHaveURL(/sort=created/)
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page.getByRole('button', { name: 'Added', exact: true }).click()
|
||||
await expect(page).toHaveURL(/sort=added/)
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page.getByRole('button', { name: 'Modified' }).click()
|
||||
await expect(page).toHaveURL(/sort=modified/)
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page.getByRole('button', { name: 'Notes' }).click()
|
||||
await expect(page).toHaveURL(/sort=num_notes/)
|
||||
await page.getByRole('button', { name: 'Sort' }).click()
|
||||
await page.locator('.w-100 > label > .toolbaricon').first().click()
|
||||
await expect(page).not.toHaveURL(/reverse=1/)
|
||||
})
|
||||
|
||||
test('change views', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR5, { notFound: 'fallback' })
|
||||
await page.goto('/documents')
|
||||
await page.locator('app-page-header label').first().click()
|
||||
await expect(page.locator('app-document-list table')).toBeVisible()
|
||||
await page.locator('app-page-header label').nth(1).click()
|
||||
await expect(page.locator('app-document-card-small').first()).toBeAttached()
|
||||
await page.locator('app-page-header label').nth(2).click()
|
||||
await expect(page.locator('app-document-card-large').first()).toBeAttached()
|
||||
})
|
||||
|
||||
test('bulk edit', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR6, { notFound: 'fallback' })
|
||||
await page.goto('/documents')
|
||||
|
||||
await page.locator('app-document-card-small').nth(0).click()
|
||||
await page
|
||||
.locator('app-document-card-small')
|
||||
.nth(3)
|
||||
.click({
|
||||
modifiers: ['Shift'],
|
||||
})
|
||||
|
||||
await expect(page.locator('app-document-list')).toHaveText(
|
||||
/Selected 4 of 61 documents/i
|
||||
)
|
||||
|
||||
await page.getByRole('button', { name: 'Page' }).click()
|
||||
await expect(page.locator('app-document-list')).toHaveText(
|
||||
/Selected 50 of 61 documents/i
|
||||
)
|
||||
await page.getByRole('button', { name: 'All' }).click()
|
||||
await expect(page.locator('app-document-list')).toHaveText(
|
||||
/Selected 61 of 61 documents/i
|
||||
)
|
||||
await page.getByRole('button', { name: 'Cancel' }).click()
|
||||
|
||||
await page.locator('app-document-card-small').nth(1).click()
|
||||
await page.locator('app-document-card-small').nth(2).click()
|
||||
|
||||
await page.getByRole('button', { name: 'Tags' }).click()
|
||||
await page.getByRole('menuitem', { name: 'TagWithPartial' }).click()
|
||||
|
||||
await page.getByRole('button', { name: 'Apply' }).click()
|
||||
|
||||
const bulkEditPromise = page.waitForRequest((request) => {
|
||||
const postData = request.postDataJSON()
|
||||
let isValid = postData['method'] == 'modify_tags'
|
||||
isValid = isValid && postData['parameters']['add_tags'].includes(5)
|
||||
return request.url().toString().includes('bulk_edit') && isValid
|
||||
})
|
||||
|
||||
await page.getByRole('button', { name: 'Confirm' }).click()
|
||||
await bulkEditPromise
|
||||
})
|
7111
src-ui/e2e/document-list/requests/api-document-list1.har
Normal file
7111
src-ui/e2e/document-list/requests/api-document-list1.har
Normal file
File diff suppressed because one or more lines are too long
5373
src-ui/e2e/document-list/requests/api-document-list2.har
Normal file
5373
src-ui/e2e/document-list/requests/api-document-list2.har
Normal file
File diff suppressed because one or more lines are too long
3829
src-ui/e2e/document-list/requests/api-document-list3.har
Normal file
3829
src-ui/e2e/document-list/requests/api-document-list3.har
Normal file
File diff suppressed because one or more lines are too long
5403
src-ui/e2e/document-list/requests/api-document-list4.har
Normal file
5403
src-ui/e2e/document-list/requests/api-document-list4.har
Normal file
File diff suppressed because one or more lines are too long
3545
src-ui/e2e/document-list/requests/api-document-list5.har
Normal file
3545
src-ui/e2e/document-list/requests/api-document-list5.har
Normal file
File diff suppressed because one or more lines are too long
3679
src-ui/e2e/document-list/requests/api-document-list6.har
Normal file
3679
src-ui/e2e/document-list/requests/api-document-list6.har
Normal file
File diff suppressed because one or more lines are too long
58
src-ui/e2e/manage/manage.spec.ts
Normal file
58
src-ui/e2e/manage/manage.spec.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
const REQUESTS_HAR1 = 'e2e/manage/requests/api-manage1.har'
|
||||
const REQUESTS_HAR2 = 'e2e/manage/requests/api-manage2.har'
|
||||
|
||||
test('should show a list of tags with bottom pagination as well', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||
await page.goto('/tags')
|
||||
await expect(page.getByRole('main')).toHaveText(/26 total tags/i)
|
||||
await expect(await page.locator('ngb-pagination')).toHaveCount(2)
|
||||
})
|
||||
|
||||
test('should show a list of correspondents without bottom pagination', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
|
||||
await page.goto('/correspondents')
|
||||
await expect(page.getByRole('main')).toHaveText(/4 total correspondents/i)
|
||||
await expect(await page.locator('ngb-pagination')).toHaveCount(1)
|
||||
})
|
||||
|
||||
test('should support quick filter Documents button', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||
await page.goto('/tags')
|
||||
await page
|
||||
.getByRole('row', { name: 'Inbox' })
|
||||
.getByRole('button', { name: 'Documents' })
|
||||
.click()
|
||||
await expect(page).toHaveURL(/tags__id__all=9/)
|
||||
})
|
||||
|
||||
test('should support item editing', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||
await page.goto('/tags')
|
||||
await page
|
||||
.getByRole('row', { name: 'Inbox' })
|
||||
.getByRole('button', { name: 'Edit' })
|
||||
.click()
|
||||
await expect(page.getByRole('dialog')).toBeVisible()
|
||||
await expect(page.getByLabel('Name')).toHaveValue('Inbox')
|
||||
await page.getByTitle('Color').getByRole('button').click()
|
||||
const color = await page.getByLabel('Color').inputValue()
|
||||
|
||||
const updatePromise = page.waitForRequest((request) => {
|
||||
const data = request.postDataJSON()
|
||||
const isValid = data['color'] === color
|
||||
return (
|
||||
isValid &&
|
||||
request.method() === 'PUT' &&
|
||||
request.url().includes('/api/tags/9/')
|
||||
)
|
||||
})
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click()
|
||||
await updatePromise
|
||||
})
|
1090
src-ui/e2e/manage/requests/api-manage1.har
Normal file
1090
src-ui/e2e/manage/requests/api-manage1.har
Normal file
File diff suppressed because one or more lines are too long
260
src-ui/e2e/manage/requests/api-manage2.har
Normal file
260
src-ui/e2e/manage/requests/api-manage2.har
Normal file
File diff suppressed because one or more lines are too long
95
src-ui/e2e/permissions/global-permissions.spec.ts
Normal file
95
src-ui/e2e/permissions/global-permissions.spec.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
const REQUESTS_HAR = 'e2e/permissions/requests/api-global-permissions.har'
|
||||
|
||||
test('should not allow user to edit settings', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await expect(page.getByRole('link', { name: 'Settings' })).not.toBeAttached()
|
||||
await page.goto('/settings')
|
||||
await expect(page.locator('body')).toHaveText(
|
||||
/You don't have permissions to do that/i
|
||||
)
|
||||
})
|
||||
|
||||
test('should not allow user to view documents', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await expect(
|
||||
page.locator('nav').getByRole('link', { name: 'Documents' })
|
||||
).not.toBeAttached()
|
||||
await page.goto('/documents')
|
||||
await expect(page.locator('body')).toHaveText(
|
||||
/You don't have permissions to do that/i
|
||||
)
|
||||
await page.goto('/documents/1')
|
||||
await expect(page.locator('body')).toHaveText(
|
||||
/You don't have permissions to do that/i
|
||||
)
|
||||
})
|
||||
|
||||
test('should not allow user to view correspondents', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Correspondents' })
|
||||
).not.toBeAttached()
|
||||
await page.goto('/correspondents')
|
||||
await expect(page.locator('body')).toHaveText(
|
||||
/You don't have permissions to do that/i
|
||||
)
|
||||
})
|
||||
|
||||
test('should not allow user to view tags', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await expect(page.getByRole('link', { name: 'Tags' })).not.toBeAttached()
|
||||
await page.goto('/tags')
|
||||
await expect(page.locator('body')).toHaveText(
|
||||
/You don't have permissions to do that/i
|
||||
)
|
||||
})
|
||||
|
||||
test('should not allow user to view document types', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Document Types' })
|
||||
).not.toBeAttached()
|
||||
await page.goto('/documenttypes')
|
||||
await expect(page.locator('body')).toHaveText(
|
||||
/You don't have permissions to do that/i
|
||||
)
|
||||
})
|
||||
|
||||
test('should not allow user to view storage paths', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Storage Paths' })
|
||||
).not.toBeAttached()
|
||||
await page.goto('/storagepaths')
|
||||
await expect(page.locator('body')).toHaveText(
|
||||
/You don't have permissions to do that/i
|
||||
)
|
||||
})
|
||||
|
||||
test('should not allow user to view logs', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await expect(page.getByRole('link', { name: 'Logs' })).not.toBeAttached()
|
||||
await page.goto('/logs')
|
||||
await expect(page.locator('body')).toHaveText(
|
||||
/You don't have permissions to do that/i
|
||||
)
|
||||
})
|
||||
|
||||
test('should not allow user to view tasks', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/dashboard')
|
||||
await expect(page.getByRole('link', { name: 'Tasks' })).not.toBeAttached()
|
||||
await page.goto('/tasks')
|
||||
await expect(page.locator('body')).toHaveText(
|
||||
/You don't have permissions to do that/i
|
||||
)
|
||||
})
|
353
src-ui/e2e/permissions/requests/api-global-permissions.har
Normal file
353
src-ui/e2e/permissions/requests/api-global-permissions.har
Normal file
@@ -0,0 +1,353 @@
|
||||
{
|
||||
"log": {
|
||||
"version": "1.2",
|
||||
"creator": {
|
||||
"name": "Playwright",
|
||||
"version": "1.33.0"
|
||||
},
|
||||
"browser": {
|
||||
"name": "chromium",
|
||||
"version": "113.0.5672.53"
|
||||
},
|
||||
"entries": [
|
||||
{
|
||||
"startedDateTime": "2023-05-14T07:16:51.455Z",
|
||||
"time": 5.787,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8000/api/ui_settings/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Accept", "value": "application/json; version=3" },
|
||||
{ "name": "Accept-Encoding", "value": "gzip, deflate, br" },
|
||||
{ "name": "Accept-Language", "value": "en-US" },
|
||||
{ "name": "Connection", "value": "keep-alive" },
|
||||
{ "name": "Host", "value": "localhost:8000" },
|
||||
{ "name": "Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Referer", "value": "http://localhost:4200/" },
|
||||
{ "name": "Sec-Fetch-Dest", "value": "empty" },
|
||||
{ "name": "Sec-Fetch-Mode", "value": "cors" },
|
||||
{ "name": "Sec-Fetch-Site", "value": "same-site" },
|
||||
{ "name": "User-Agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.53 Safari/537.36" }
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": -1,
|
||||
"bodySize": -1
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Access-Control-Allow-Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Allow", "value": "GET, POST, HEAD, OPTIONS" },
|
||||
{ "name": "Content-Encoding", "value": "br" },
|
||||
{ "name": "Content-Language", "value": "en-us" },
|
||||
{ "name": "Content-Length", "value": "385" },
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "Cross-Origin-Opener-Policy", "value": "same-origin" },
|
||||
{ "name": "Referrer-Policy", "value": "same-origin" },
|
||||
{ "name": "Vary", "value": "Accept, Accept-Language, Origin, Cookie, Accept-Encoding" },
|
||||
{ "name": "X-Api-Version", "value": "3" },
|
||||
{ "name": "X-Content-Type-Options", "value": "nosniff" },
|
||||
{ "name": "X-Frame-Options", "value": "ANY" },
|
||||
{ "name": "X-Version", "value": "1.14.4" }
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "application/json",
|
||||
"text": "{\"user\":{\"id\":2,\"username\":\"testuser\",\"is_superuser\":false,\"groups\":[]},\"settings\":{\"language\":\"\",\"bulk_edit\":{\"confirmation_dialogs\":true,\"apply_on_close\":false},\"documentListSize\":50,\"dark_mode\":{\"use_system\":false,\"enabled\":\"false\",\"thumb_inverted\":\"true\"},\"theme\":{\"color\":\"#9fbf2f\"},\"document_details\":{\"native_pdf_viewer\":false},\"date_display\":{\"date_locale\":\"\",\"date_format\":\"mediumDate\"},\"notifications\":{\"consumer_new_documents\":true,\"consumer_success\":true,\"consumer_failed\":true,\"consumer_suppress_on_dashboard\":true},\"comments_enabled\":true,\"slim_sidebar\":false,\"update_checking\":{\"enabled\":false,\"backend_setting\":\"default\"},\"saved_views\":{\"warn_on_unsaved_change\":true},\"notes_enabled\":true,\"tour_complete\":true},\"permissions\":[]}"
|
||||
},
|
||||
"headersSize": -1,
|
||||
"bodySize": -1,
|
||||
"redirectURL": ""
|
||||
},
|
||||
"cache": {},
|
||||
"timings": { "send": -1, "wait": -1, "receive": 5.787 }
|
||||
},
|
||||
{
|
||||
"startedDateTime": "2023-05-14T07:16:51.578Z",
|
||||
"time": 0.566,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8000/api/tasks/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Accept", "value": "application/json; version=3" },
|
||||
{ "name": "Accept-Encoding", "value": "gzip, deflate, br" },
|
||||
{ "name": "Accept-Language", "value": "en-US" },
|
||||
{ "name": "Connection", "value": "keep-alive" },
|
||||
{ "name": "Host", "value": "localhost:8000" },
|
||||
{ "name": "Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Referer", "value": "http://localhost:4200/" },
|
||||
{ "name": "Sec-Fetch-Dest", "value": "empty" },
|
||||
{ "name": "Sec-Fetch-Mode", "value": "cors" },
|
||||
{ "name": "Sec-Fetch-Site", "value": "same-site" },
|
||||
{ "name": "User-Agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.53 Safari/537.36" }
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": -1,
|
||||
"bodySize": -1
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Access-Control-Allow-Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Allow", "value": "GET, HEAD, OPTIONS" },
|
||||
{ "name": "Content-Language", "value": "en-us" },
|
||||
{ "name": "Content-Length", "value": "2" },
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "Cross-Origin-Opener-Policy", "value": "same-origin" },
|
||||
{ "name": "Referrer-Policy", "value": "same-origin" },
|
||||
{ "name": "Vary", "value": "Accept, Accept-Language, Origin, Cookie" },
|
||||
{ "name": "X-Api-Version", "value": "3" },
|
||||
{ "name": "X-Content-Type-Options", "value": "nosniff" },
|
||||
{ "name": "X-Frame-Options", "value": "ANY" },
|
||||
{ "name": "X-Version", "value": "1.14.4" }
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "application/json",
|
||||
"text": "[]"
|
||||
},
|
||||
"headersSize": -1,
|
||||
"bodySize": -1,
|
||||
"redirectURL": ""
|
||||
},
|
||||
"cache": {},
|
||||
"timings": { "send": -1, "wait": -1, "receive": 0.566 }
|
||||
},
|
||||
{
|
||||
"startedDateTime": "2023-05-14T07:16:51.578Z",
|
||||
"time": 0.452,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8000/api/statistics/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Accept", "value": "application/json; version=3" },
|
||||
{ "name": "Accept-Encoding", "value": "gzip, deflate, br" },
|
||||
{ "name": "Accept-Language", "value": "en-US" },
|
||||
{ "name": "Connection", "value": "keep-alive" },
|
||||
{ "name": "Host", "value": "localhost:8000" },
|
||||
{ "name": "Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Referer", "value": "http://localhost:4200/" },
|
||||
{ "name": "Sec-Fetch-Dest", "value": "empty" },
|
||||
{ "name": "Sec-Fetch-Mode", "value": "cors" },
|
||||
{ "name": "Sec-Fetch-Site", "value": "same-site" },
|
||||
{ "name": "User-Agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.53 Safari/537.36" }
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": -1,
|
||||
"bodySize": -1
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Access-Control-Allow-Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Allow", "value": "GET, HEAD, OPTIONS" },
|
||||
{ "name": "Content-Language", "value": "en-us" },
|
||||
{ "name": "Content-Length", "value": "257" },
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "Cross-Origin-Opener-Policy", "value": "same-origin" },
|
||||
{ "name": "Referrer-Policy", "value": "same-origin" },
|
||||
{ "name": "Vary", "value": "Accept, Accept-Language, Origin, Cookie" },
|
||||
{ "name": "X-Api-Version", "value": "3" },
|
||||
{ "name": "X-Content-Type-Options", "value": "nosniff" },
|
||||
{ "name": "X-Frame-Options", "value": "ANY" },
|
||||
{ "name": "X-Version", "value": "1.14.4" }
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "application/json",
|
||||
"text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tag\":9,\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}"
|
||||
},
|
||||
"headersSize": -1,
|
||||
"bodySize": -1,
|
||||
"redirectURL": ""
|
||||
},
|
||||
"cache": {},
|
||||
"timings": { "send": -1, "wait": -1, "receive": 0.452 }
|
||||
},
|
||||
{
|
||||
"startedDateTime": "2023-05-14T07:16:51.691Z",
|
||||
"time": 0.891,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8000/api/ui_settings/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Accept", "value": "application/json; version=3" },
|
||||
{ "name": "Accept-Encoding", "value": "gzip, deflate, br" },
|
||||
{ "name": "Accept-Language", "value": "en-US" },
|
||||
{ "name": "Connection", "value": "keep-alive" },
|
||||
{ "name": "Host", "value": "localhost:8000" },
|
||||
{ "name": "Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Referer", "value": "http://localhost:4200/" },
|
||||
{ "name": "Sec-Fetch-Dest", "value": "empty" },
|
||||
{ "name": "Sec-Fetch-Mode", "value": "cors" },
|
||||
{ "name": "Sec-Fetch-Site", "value": "same-site" },
|
||||
{ "name": "User-Agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.53 Safari/537.36" }
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": -1,
|
||||
"bodySize": -1
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Access-Control-Allow-Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Allow", "value": "GET, POST, HEAD, OPTIONS" },
|
||||
{ "name": "Content-Encoding", "value": "br" },
|
||||
{ "name": "Content-Language", "value": "en-us" },
|
||||
{ "name": "Content-Length", "value": "385" },
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "Cross-Origin-Opener-Policy", "value": "same-origin" },
|
||||
{ "name": "Referrer-Policy", "value": "same-origin" },
|
||||
{ "name": "Vary", "value": "Accept, Accept-Language, Origin, Cookie, Accept-Encoding" },
|
||||
{ "name": "X-Api-Version", "value": "3" },
|
||||
{ "name": "X-Content-Type-Options", "value": "nosniff" },
|
||||
{ "name": "X-Frame-Options", "value": "ANY" },
|
||||
{ "name": "X-Version", "value": "1.14.4" }
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "application/json",
|
||||
"text": "{\"user\":{\"id\":2,\"username\":\"testuser\",\"is_superuser\":false,\"groups\":[]},\"settings\":{\"language\":\"\",\"bulk_edit\":{\"confirmation_dialogs\":true,\"apply_on_close\":false},\"documentListSize\":50,\"dark_mode\":{\"use_system\":false,\"enabled\":\"false\",\"thumb_inverted\":\"true\"},\"theme\":{\"color\":\"#9fbf2f\"},\"document_details\":{\"native_pdf_viewer\":false},\"date_display\":{\"date_locale\":\"\",\"date_format\":\"mediumDate\"},\"notifications\":{\"consumer_new_documents\":true,\"consumer_success\":true,\"consumer_failed\":true,\"consumer_suppress_on_dashboard\":true},\"comments_enabled\":true,\"slim_sidebar\":false,\"update_checking\":{\"enabled\":false,\"backend_setting\":\"default\"},\"saved_views\":{\"warn_on_unsaved_change\":true},\"notes_enabled\":true,\"tour_complete\":true},\"permissions\":[]}"
|
||||
},
|
||||
"headersSize": -1,
|
||||
"bodySize": -1,
|
||||
"redirectURL": ""
|
||||
},
|
||||
"cache": {},
|
||||
"timings": { "send": -1, "wait": -1, "receive": 0.891 }
|
||||
},
|
||||
{
|
||||
"startedDateTime": "2023-05-14T07:16:51.739Z",
|
||||
"time": 0.405,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8000/api/tasks/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Accept", "value": "application/json; version=3" },
|
||||
{ "name": "Accept-Encoding", "value": "gzip, deflate, br" },
|
||||
{ "name": "Accept-Language", "value": "en-US" },
|
||||
{ "name": "Connection", "value": "keep-alive" },
|
||||
{ "name": "Host", "value": "localhost:8000" },
|
||||
{ "name": "Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Referer", "value": "http://localhost:4200/" },
|
||||
{ "name": "Sec-Fetch-Dest", "value": "empty" },
|
||||
{ "name": "Sec-Fetch-Mode", "value": "cors" },
|
||||
{ "name": "Sec-Fetch-Site", "value": "same-site" },
|
||||
{ "name": "User-Agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.53 Safari/537.36" }
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": -1,
|
||||
"bodySize": -1
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Access-Control-Allow-Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Allow", "value": "GET, HEAD, OPTIONS" },
|
||||
{ "name": "Content-Language", "value": "en-us" },
|
||||
{ "name": "Content-Length", "value": "2" },
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "Cross-Origin-Opener-Policy", "value": "same-origin" },
|
||||
{ "name": "Referrer-Policy", "value": "same-origin" },
|
||||
{ "name": "Vary", "value": "Accept, Accept-Language, Origin, Cookie" },
|
||||
{ "name": "X-Api-Version", "value": "3" },
|
||||
{ "name": "X-Content-Type-Options", "value": "nosniff" },
|
||||
{ "name": "X-Frame-Options", "value": "ANY" },
|
||||
{ "name": "X-Version", "value": "1.14.4" }
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "application/json",
|
||||
"text": "[]"
|
||||
},
|
||||
"headersSize": -1,
|
||||
"bodySize": -1,
|
||||
"redirectURL": ""
|
||||
},
|
||||
"cache": {},
|
||||
"timings": { "send": -1, "wait": -1, "receive": 0.405 }
|
||||
},
|
||||
{
|
||||
"startedDateTime": "2023-05-14T07:16:51.739Z",
|
||||
"time": 0.665,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8000/api/statistics/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Accept", "value": "application/json; version=3" },
|
||||
{ "name": "Accept-Encoding", "value": "gzip, deflate, br" },
|
||||
{ "name": "Accept-Language", "value": "en-US" },
|
||||
{ "name": "Connection", "value": "keep-alive" },
|
||||
{ "name": "Host", "value": "localhost:8000" },
|
||||
{ "name": "Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Referer", "value": "http://localhost:4200/" },
|
||||
{ "name": "Sec-Fetch-Dest", "value": "empty" },
|
||||
{ "name": "Sec-Fetch-Mode", "value": "cors" },
|
||||
{ "name": "Sec-Fetch-Site", "value": "same-site" },
|
||||
{ "name": "User-Agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.53 Safari/537.36" }
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": -1,
|
||||
"bodySize": -1
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{ "name": "Access-Control-Allow-Origin", "value": "http://localhost:4200" },
|
||||
{ "name": "Allow", "value": "GET, HEAD, OPTIONS" },
|
||||
{ "name": "Content-Language", "value": "en-us" },
|
||||
{ "name": "Content-Length", "value": "257" },
|
||||
{ "name": "Content-Type", "value": "application/json" },
|
||||
{ "name": "Cross-Origin-Opener-Policy", "value": "same-origin" },
|
||||
{ "name": "Referrer-Policy", "value": "same-origin" },
|
||||
{ "name": "Vary", "value": "Accept, Accept-Language, Origin, Cookie" },
|
||||
{ "name": "X-Api-Version", "value": "3" },
|
||||
{ "name": "X-Content-Type-Options", "value": "nosniff" },
|
||||
{ "name": "X-Frame-Options", "value": "ANY" },
|
||||
{ "name": "X-Version", "value": "1.14.4" }
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "application/json",
|
||||
"text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tag\":9,\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}"
|
||||
},
|
||||
"headersSize": -1,
|
||||
"bodySize": -1,
|
||||
"redirectURL": ""
|
||||
},
|
||||
"cache": {},
|
||||
"timings": { "send": -1, "wait": -1, "receive": 0.665 }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter')
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: ['./src/**/*.e2e-spec.ts'],
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function () {},
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json'),
|
||||
})
|
||||
jasmine.getEnv().addReporter(
|
||||
new SpecReporter({
|
||||
spec: {
|
||||
displayStacktrace: StacktraceOption.PRETTY,
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
}
|
194
src-ui/e2e/settings/requests/api-settings.har
Normal file
194
src-ui/e2e/settings/requests/api-settings.har
Normal file
File diff suppressed because one or more lines are too long
326
src-ui/e2e/settings/requests/api-settings2.har
Normal file
326
src-ui/e2e/settings/requests/api-settings2.har
Normal file
File diff suppressed because one or more lines are too long
589
src-ui/e2e/settings/requests/api-settings3.har
Normal file
589
src-ui/e2e/settings/requests/api-settings3.har
Normal file
File diff suppressed because one or more lines are too long
165
src-ui/e2e/settings/settings.spec.ts
Normal file
165
src-ui/e2e/settings/settings.spec.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
const REQUESTS_HAR = 'e2e/settings/requests/api-settings.har'
|
||||
const REQUESTS_HAR2 = 'e2e/settings/requests/api-settings2.har'
|
||||
const REQUESTS_HAR3 = 'e2e/settings/requests/api-settings3.har'
|
||||
|
||||
test('should post settings on save', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/settings')
|
||||
await page.getByLabel('Use system setting').click()
|
||||
await page.getByRole('button', { name: 'Save' }).scrollIntoViewIfNeeded()
|
||||
const updatePromise = page.waitForRequest((request) => {
|
||||
const data = request.postDataJSON()
|
||||
const isValid = data['settings'] != null
|
||||
return (
|
||||
isValid &&
|
||||
request.method() === 'POST' &&
|
||||
request.url().includes('/api/ui_settings/')
|
||||
)
|
||||
})
|
||||
await page.getByRole('button', { name: 'Save' }).click()
|
||||
await updatePromise
|
||||
})
|
||||
|
||||
test('should activate / deactivate save button when settings change', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/settings')
|
||||
await expect(page.getByRole('button', { name: 'Save' })).toBeDisabled()
|
||||
await page.getByLabel('Use system setting').click()
|
||||
await page.getByRole('button', { name: 'Save' }).scrollIntoViewIfNeeded()
|
||||
await expect(page.getByRole('button', { name: 'Save' })).toBeEnabled()
|
||||
})
|
||||
|
||||
test('should warn on unsaved changes', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/settings')
|
||||
await page.getByLabel('Use system setting').click()
|
||||
await page.getByRole('link', { name: 'Dashboard' }).click()
|
||||
await expect(page.getByRole('dialog')).toHaveText(/unsaved changes/)
|
||||
await page.getByRole('button', { name: 'Cancel' }).click()
|
||||
await page.getByLabel('Use system setting').click()
|
||||
await page.getByRole('link', { name: 'Dashboard' }).click()
|
||||
await expect(page.getByRole('dialog')).toHaveCount(0)
|
||||
})
|
||||
|
||||
test('should apply appearance changes when set', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/settings')
|
||||
await expect(page.locator('body')).toHaveClass(/color-scheme-system/)
|
||||
await page.getByLabel('Use system setting').click()
|
||||
await page.getByLabel('Enable dark mode').click()
|
||||
await expect(page.locator('body')).toHaveClass(/color-scheme-dark/)
|
||||
})
|
||||
|
||||
test('should toggle saved view options when set & saved', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/settings/savedviews')
|
||||
await page.getByLabel('Show on dashboard').first().click()
|
||||
await page.getByLabel('Show in sidebar').first().click()
|
||||
const updatePromise = page.waitForRequest((request) => {
|
||||
if (!request.url().includes('8')) return true // skip other saved views
|
||||
const data = request.postDataJSON()
|
||||
const isValid =
|
||||
data['show_on_dashboard'] === true && data['show_in_sidebar'] === true
|
||||
return (
|
||||
isValid &&
|
||||
request.method() === 'PATCH' &&
|
||||
request.url().includes('/api/saved_views/')
|
||||
)
|
||||
})
|
||||
await page.getByRole('button', { name: 'Save' }).scrollIntoViewIfNeeded()
|
||||
await page.getByRole('button', { name: 'Save' }).click()
|
||||
await updatePromise
|
||||
})
|
||||
|
||||
test('should support tab direct navigation', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/settings/general')
|
||||
await expect(page.getByRole('tab', { name: 'General' })).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
)
|
||||
await page.goto('/settings/notifications')
|
||||
await expect(
|
||||
page.getByRole('tab', { name: 'Notifications' })
|
||||
).toHaveAttribute('aria-selected', 'true')
|
||||
await page.goto('/settings/savedviews')
|
||||
await expect(page.getByRole('tab', { name: 'Saved Views' })).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
)
|
||||
await page.goto('/settings/mail')
|
||||
await expect(page.getByRole('tab', { name: 'Mail' })).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
)
|
||||
await page.goto('/settings/usersgroups')
|
||||
await expect(
|
||||
page.getByRole('tab', { name: 'Users & Groups' })
|
||||
).toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
|
||||
test('should show a list of mail accounts & support creation', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
|
||||
await page.goto('/settings/mail')
|
||||
await expect(
|
||||
page.getByRole('listitem').filter({ hasText: 'imap.gmail.com' })
|
||||
).toHaveCount(1)
|
||||
await expect(
|
||||
page.getByRole('listitem').filter({ hasText: 'imap.domain.com' })
|
||||
).toHaveCount(1)
|
||||
await page.getByRole('button', { name: /Add Account/ }).click()
|
||||
await expect(page.getByRole('dialog')).toHaveCount(1)
|
||||
await page.getByLabel('Name', { exact: true }).fill('Test Account')
|
||||
await page.getByLabel('IMAP Server', { exact: true }).fill('imap.server.com')
|
||||
await page.getByLabel('IMAP Port', { exact: true }).fill('993')
|
||||
await page.getByLabel('Username', { exact: true }).fill('username')
|
||||
await page.getByLabel('Password', { exact: true }).fill('password')
|
||||
const createPromise = page.waitForRequest((request) => {
|
||||
const data = request.postDataJSON()
|
||||
const isValid = data['imap_server'] === 'imap.server.com'
|
||||
return (
|
||||
isValid &&
|
||||
request.method() === 'POST' &&
|
||||
request.url().includes('/api/mail_accounts/')
|
||||
)
|
||||
})
|
||||
await page.getByRole('button', { name: 'Save' }).click()
|
||||
await createPromise
|
||||
})
|
||||
|
||||
test('should show a list of mail rules & support creation', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR3, { notFound: 'fallback' })
|
||||
await page.goto('/settings/mail')
|
||||
await expect(
|
||||
page.getByRole('listitem').filter({ hasText: 'domain' })
|
||||
).toHaveCount(2)
|
||||
await expect(
|
||||
page.getByRole('listitem').filter({ hasText: 'gmail' })
|
||||
).toHaveCount(2)
|
||||
await page.getByRole('button', { name: /Add Rule/ }).click()
|
||||
await expect(page.getByRole('dialog')).toHaveCount(1)
|
||||
await page.getByLabel('Name', { exact: true }).fill('Test Rule')
|
||||
await page.getByTitle('Account').locator('span').first().click()
|
||||
await page.getByRole('option', { name: 'gmail' }).click()
|
||||
await page.getByLabel('Maximum age (days)').fill('0')
|
||||
const createPromise = page.waitForRequest((request) => {
|
||||
const data = request.postDataJSON()
|
||||
const isValid = data['name'] === 'Test Rule'
|
||||
return (
|
||||
isValid &&
|
||||
request.method() === 'POST' &&
|
||||
request.url().includes('/api/mail_rules/')
|
||||
)
|
||||
})
|
||||
await page.getByRole('button', { name: 'Save' }).scrollIntoViewIfNeeded()
|
||||
await page.getByRole('button', { name: 'Save' }).click()
|
||||
await createPromise
|
||||
})
|
@@ -1,25 +0,0 @@
|
||||
import { AppPage } from './app.po'
|
||||
import { browser, logging } from 'protractor'
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage()
|
||||
})
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo()
|
||||
expect(page.getTitleText()).toEqual('paperless-ui app is running!')
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER)
|
||||
expect(logs).not.toContain(
|
||||
jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry)
|
||||
)
|
||||
})
|
||||
})
|
@@ -1,13 +0,0 @@
|
||||
import { browser, by, element } from 'protractor'
|
||||
|
||||
export class AppPage {
|
||||
navigateTo(): Promise<unknown> {
|
||||
return browser.get(browser.baseUrl) as Promise<unknown>
|
||||
}
|
||||
|
||||
getTitleText(): Promise<string> {
|
||||
return element(
|
||||
by.css('app-root .content span')
|
||||
).getText() as Promise<string>
|
||||
}
|
||||
}
|
252
src-ui/e2e/tasks/requests/api-tasks.har
Normal file
252
src-ui/e2e/tasks/requests/api-tasks.har
Normal file
File diff suppressed because one or more lines are too long
71
src-ui/e2e/tasks/tasks.spec.ts
Normal file
71
src-ui/e2e/tasks/tasks.spec.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
const REQUESTS_HAR = 'e2e/tasks/requests/api-tasks.har'
|
||||
|
||||
test('should show a list of dismissable tasks in tabs', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/tasks')
|
||||
await expect(page.getByRole('tab', { name: /Failed/ })).toHaveText(/1/)
|
||||
await expect(
|
||||
page.getByRole('cell').filter({ hasText: 'Dismiss' })
|
||||
).toHaveCount(1)
|
||||
await expect(page.getByRole('tab', { name: /Complete/ })).toHaveText(/8/)
|
||||
await page.getByRole('tab', { name: /Complete/ }).click()
|
||||
await expect(
|
||||
page.getByRole('cell').filter({ hasText: 'Dismiss' })
|
||||
).toHaveCount(8)
|
||||
await page.getByRole('tab', { name: /Started/ }).click()
|
||||
await expect(
|
||||
page.getByRole('cell').filter({ hasText: 'Dismiss' })
|
||||
).toHaveCount(0)
|
||||
await page.getByRole('tab', { name: /Queued/ }).click()
|
||||
await expect(
|
||||
page.getByRole('cell').filter({ hasText: 'Dismiss' })
|
||||
).toHaveCount(0)
|
||||
})
|
||||
|
||||
test('should support dismissing tasks', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/tasks')
|
||||
await page.getByRole('tab', { name: /Failed/ }).click()
|
||||
const dismissPromise = page.waitForRequest((request) => {
|
||||
const data = request.postDataJSON()
|
||||
const isValid = Array.isArray(data['tasks']) && data['tasks'].includes(255)
|
||||
return (
|
||||
isValid &&
|
||||
request.method() === 'POST' &&
|
||||
request.url().includes('/api/acknowledge_tasks/')
|
||||
)
|
||||
})
|
||||
await page
|
||||
.getByRole('button', { name: 'Dismiss', exact: true })
|
||||
.first()
|
||||
.click()
|
||||
await dismissPromise
|
||||
})
|
||||
|
||||
test('should support dismiss all tasks', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/tasks')
|
||||
await expect(page.getByRole('button', { name: 'Dismiss all' })).toBeEnabled()
|
||||
await page.getByRole('button', { name: 'Dismiss all' }).click()
|
||||
const dismissPromise = page.waitForRequest((request) => {
|
||||
const data = request.postDataJSON()
|
||||
const isValid = Array.isArray(data['tasks'])
|
||||
return (
|
||||
isValid &&
|
||||
request.method() === 'POST' &&
|
||||
request.url().includes('/api/acknowledge_tasks/')
|
||||
)
|
||||
})
|
||||
await page.getByRole('button', { name: /Dismiss/ }).click()
|
||||
await dismissPromise
|
||||
})
|
||||
|
||||
test('should warn on dismiss all tasks', async ({ page }) => {
|
||||
await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' })
|
||||
await page.goto('/tasks')
|
||||
await expect(page.getByRole('button', { name: 'Dismiss all' })).toBeEnabled()
|
||||
await page.getByRole('button', { name: 'Dismiss all' }).click()
|
||||
await expect(page.getByRole('dialog')).toHaveCount(1)
|
||||
})
|
@@ -1,14 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es2018",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
@@ -1,8 +1,14 @@
|
||||
module.exports = {
|
||||
moduleNameMapper: {
|
||||
'@core/(.*)': '<rootDir>/src/app/core/$1',
|
||||
},
|
||||
preset: 'jest-preset-angular',
|
||||
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
|
||||
testPathIgnorePatterns: ['/node_modules/', '/cypress/'],
|
||||
testPathIgnorePatterns: [
|
||||
'/node_modules/',
|
||||
'/e2e/',
|
||||
'abstract-name-filter-service',
|
||||
'abstract-paperless-service',
|
||||
],
|
||||
transformIgnorePatterns: [`<rootDir>/node_modules/(?!.*\\.mjs$|lodash-es)`],
|
||||
moduleNameMapper: {
|
||||
'^src/(.*)': '<rootDir>/src/$1',
|
||||
},
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
1944
src-ui/package-lock.json
generated
1944
src-ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,11 +5,8 @@
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e",
|
||||
"cy:run": "cypress run",
|
||||
"e2e:ci": "concurrently 'npm run start' 'wait-on http-get://localhost:4200 && npm run cy:run' --kill-others --success first"
|
||||
"test": "ng test --no-watch --coverage",
|
||||
"lint": "ng lint"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@@ -21,11 +18,11 @@
|
||||
"@angular/platform-browser": "~15.2.8",
|
||||
"@angular/platform-browser-dynamic": "~15.2.8",
|
||||
"@angular/router": "~15.2.8",
|
||||
"@ng-bootstrap/ng-bootstrap": "^14.1.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^14.2.0",
|
||||
"@ng-select/ng-select": "^10.0.4",
|
||||
"@ngneat/dirty-check-forms": "^3.0.3",
|
||||
"@popperjs/core": "^2.11.7",
|
||||
"bootstrap": "^5.2.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"bootstrap": "^5.3.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"mime-names": "^1.0.0",
|
||||
"ng2-pdf-viewer": "^9.1.5",
|
||||
@@ -34,7 +31,7 @@
|
||||
"ngx-file-drop": "^15.0.0",
|
||||
"ngx-ui-tour-ng-bootstrap": "^12.6.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"tslib": "^2.4.1",
|
||||
"tslib": "^2.5.2",
|
||||
"uuid": "^9.0.0",
|
||||
"zone.js": "^0.13.0"
|
||||
},
|
||||
@@ -48,21 +45,19 @@
|
||||
"@angular-eslint/template-parser": "15.2.1",
|
||||
"@angular/cli": "~15.2.7",
|
||||
"@angular/compiler-cli": "~15.2.8",
|
||||
"@playwright/test": "^1.35.1",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/node": "^18.16.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||
"@typescript-eslint/parser": "^5.59.2",
|
||||
"concurrently": "^8.0.1",
|
||||
"eslint": "^8.39.0",
|
||||
"@types/node": "^20.2.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
||||
"@typescript-eslint/parser": "^5.59.8",
|
||||
"concurrently": "^8.1.0",
|
||||
"eslint": "^8.41.0",
|
||||
"jest": "28.1.3",
|
||||
"jest-environment-jsdom": "^29.5.0",
|
||||
"jest-preset-angular": "^12.2.6",
|
||||
"jest-websocket-mock": "^2.4.0",
|
||||
"ts-node": "~10.9.1",
|
||||
"typescript": "~4.9.5",
|
||||
"wait-on": "^7.0.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@cypress/schematic": "^2.1.1",
|
||||
"cypress": "^12.11.0"
|
||||
}
|
||||
}
|
||||
|
74
src-ui/playwright.config.ts
Normal file
74
src-ui/playwright.config.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { defineConfig, devices } from '@playwright/test'
|
||||
|
||||
const port = 4200
|
||||
const baseURL = `http://localhost:${port}`
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: './e2e',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 3 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: {
|
||||
port,
|
||||
command: 'npm run start',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
timeout: 2 * 60 * 1000,
|
||||
},
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL,
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
|
||||
// {
|
||||
// name: 'firefox',
|
||||
// use: { ...devices['Desktop Firefox'] },
|
||||
// },
|
||||
|
||||
// {
|
||||
// name: 'webkit',
|
||||
// use: { ...devices['Desktop Safari'] },
|
||||
// },
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: { ...devices['iPhone 12'] },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { ..devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
})
|
@@ -1,4 +1,59 @@
|
||||
import { jest } from '@jest/globals'
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
require('jest-preset-angular/setup-jest')
|
||||
}
|
||||
import '@angular/localize/init'
|
||||
import { TextEncoder, TextDecoder } from 'util'
|
||||
global.TextEncoder = TextEncoder
|
||||
global.TextDecoder = TextDecoder
|
||||
|
||||
import { registerLocaleData } from '@angular/common'
|
||||
import localeAr from '@angular/common/locales/ar'
|
||||
import localeBe from '@angular/common/locales/be'
|
||||
import localeCa from '@angular/common/locales/ca'
|
||||
import localeCs from '@angular/common/locales/cs'
|
||||
import localeDa from '@angular/common/locales/da'
|
||||
import localeDe from '@angular/common/locales/de'
|
||||
import localeEnGb from '@angular/common/locales/en-GB'
|
||||
import localeEs from '@angular/common/locales/es'
|
||||
import localeFi from '@angular/common/locales/fi'
|
||||
import localeFr from '@angular/common/locales/fr'
|
||||
import localeIt from '@angular/common/locales/it'
|
||||
import localeLb from '@angular/common/locales/lb'
|
||||
import localeNl from '@angular/common/locales/nl'
|
||||
import localePl from '@angular/common/locales/pl'
|
||||
import localePt from '@angular/common/locales/pt'
|
||||
import localeRo from '@angular/common/locales/ro'
|
||||
import localeRu from '@angular/common/locales/ru'
|
||||
import localeSl from '@angular/common/locales/sl'
|
||||
import localeSr from '@angular/common/locales/sr'
|
||||
import localeSv from '@angular/common/locales/sv'
|
||||
import localeTr from '@angular/common/locales/tr'
|
||||
import localeZh from '@angular/common/locales/zh'
|
||||
|
||||
registerLocaleData(localeAr)
|
||||
registerLocaleData(localeBe)
|
||||
registerLocaleData(localeCa)
|
||||
registerLocaleData(localeCs)
|
||||
registerLocaleData(localeDa)
|
||||
registerLocaleData(localeDe)
|
||||
registerLocaleData(localeEnGb)
|
||||
registerLocaleData(localeEs)
|
||||
registerLocaleData(localeFi)
|
||||
registerLocaleData(localeFr)
|
||||
registerLocaleData(localeIt)
|
||||
registerLocaleData(localeLb)
|
||||
registerLocaleData(localeNl)
|
||||
registerLocaleData(localePl)
|
||||
registerLocaleData(localePt, 'pt-BR')
|
||||
registerLocaleData(localePt, 'pt-PT')
|
||||
registerLocaleData(localeRo)
|
||||
registerLocaleData(localeRu)
|
||||
registerLocaleData(localeSl)
|
||||
registerLocaleData(localeSr)
|
||||
registerLocaleData(localeSv)
|
||||
registerLocaleData(localeTr)
|
||||
registerLocaleData(localeZh)
|
||||
|
||||
/* global mocks for jsdom */
|
||||
const mock = () => {
|
||||
@@ -17,6 +72,8 @@ Object.defineProperty(window, 'getComputedStyle', {
|
||||
value: () => ['-webkit-appearance'],
|
||||
})
|
||||
|
||||
Object.defineProperty(window, 'ResizeObserver', { value: mock() })
|
||||
|
||||
Object.defineProperty(document.body.style, 'transform', {
|
||||
value: () => {
|
||||
return {
|
||||
|
@@ -22,7 +22,7 @@ import {
|
||||
PermissionType,
|
||||
} from './services/permissions.service'
|
||||
|
||||
const routes: Routes = [
|
||||
export const routes: Routes = [
|
||||
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
|
||||
{
|
||||
path: '',
|
||||
|
182
src-ui/src/app/app.component.spec.ts
Normal file
182
src-ui/src/app/app.component.spec.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
discardPeriodicTasks,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { Router } from '@angular/router'
|
||||
import { RouterTestingModule } from '@angular/router/testing'
|
||||
import { NgxFileDropModule } from 'ngx-file-drop'
|
||||
import { TourService, TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap'
|
||||
import { Subject } from 'rxjs'
|
||||
import { routes } from './app-routing.module'
|
||||
import { AppComponent } from './app.component'
|
||||
import { ToastsComponent } from './components/common/toasts/toasts.component'
|
||||
import {
|
||||
ConsumerStatusService,
|
||||
FileStatus,
|
||||
} from './services/consumer-status.service'
|
||||
import { PermissionsService } from './services/permissions.service'
|
||||
import { ToastService, Toast } from './services/toast.service'
|
||||
import { UploadDocumentsService } from './services/upload-documents.service'
|
||||
import { SettingsService } from './services/settings.service'
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let component: AppComponent
|
||||
let fixture: ComponentFixture<AppComponent>
|
||||
let tourService: TourService
|
||||
let consumerStatusService: ConsumerStatusService
|
||||
let permissionsService: PermissionsService
|
||||
let toastService: ToastService
|
||||
let router: Router
|
||||
let settingsService: SettingsService
|
||||
let uploadDocumentsService: UploadDocumentsService
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AppComponent, ToastsComponent],
|
||||
providers: [],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
TourNgBootstrapModule,
|
||||
RouterTestingModule.withRoutes(routes),
|
||||
NgxFileDropModule,
|
||||
],
|
||||
}).compileComponents()
|
||||
|
||||
tourService = TestBed.inject(TourService)
|
||||
consumerStatusService = TestBed.inject(ConsumerStatusService)
|
||||
permissionsService = TestBed.inject(PermissionsService)
|
||||
settingsService = TestBed.inject(SettingsService)
|
||||
toastService = TestBed.inject(ToastService)
|
||||
router = TestBed.inject(Router)
|
||||
uploadDocumentsService = TestBed.inject(UploadDocumentsService)
|
||||
fixture = TestBed.createComponent(AppComponent)
|
||||
component = fixture.componentInstance
|
||||
})
|
||||
|
||||
it('should initialize the tour service & toggle class on body for styling', fakeAsync(() => {
|
||||
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
fixture.detectChanges()
|
||||
const tourSpy = jest.spyOn(tourService, 'initialize')
|
||||
component.ngOnInit()
|
||||
expect(tourSpy).toHaveBeenCalled()
|
||||
tourService.start()
|
||||
expect(document.body.classList).toContain('tour-active')
|
||||
tourService.end()
|
||||
tick(500)
|
||||
expect(document.body.classList).not.toContain('tour-active')
|
||||
}))
|
||||
|
||||
it('should display toast on document consumed with link if user has access', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
let toast: Toast
|
||||
toastService.getToasts().subscribe((toasts) => (toast = toasts[0]))
|
||||
const toastSpy = jest.spyOn(toastService, 'show')
|
||||
const fileStatusSubject = new Subject<FileStatus>()
|
||||
jest
|
||||
.spyOn(consumerStatusService, 'onDocumentConsumptionFinished')
|
||||
.mockReturnValue(fileStatusSubject)
|
||||
component.ngOnInit()
|
||||
fileStatusSubject.next(new FileStatus())
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
expect(toast.action).not.toBeUndefined()
|
||||
})
|
||||
|
||||
it('should display toast on document consumed without link if user does not have access', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(false)
|
||||
let toast: Toast
|
||||
toastService.getToasts().subscribe((toasts) => (toast = toasts[0]))
|
||||
const toastSpy = jest.spyOn(toastService, 'show')
|
||||
const fileStatusSubject = new Subject<FileStatus>()
|
||||
jest
|
||||
.spyOn(consumerStatusService, 'onDocumentConsumptionFinished')
|
||||
.mockReturnValue(fileStatusSubject)
|
||||
component.ngOnInit()
|
||||
fileStatusSubject.next(new FileStatus())
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
expect(toast.action).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should display toast on document added', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
const toastSpy = jest.spyOn(toastService, 'show')
|
||||
const fileStatusSubject = new Subject<FileStatus>()
|
||||
jest
|
||||
.spyOn(consumerStatusService, 'onDocumentDetected')
|
||||
.mockReturnValue(fileStatusSubject)
|
||||
component.ngOnInit()
|
||||
fileStatusSubject.next(new FileStatus())
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should suppress dashboard notifications if set', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
jest.spyOn(settingsService, 'get').mockReturnValue(true)
|
||||
jest.spyOn(router, 'url', 'get').mockReturnValue('/dashboard')
|
||||
const toastSpy = jest.spyOn(toastService, 'show')
|
||||
const fileStatusSubject = new Subject<FileStatus>()
|
||||
jest
|
||||
.spyOn(consumerStatusService, 'onDocumentDetected')
|
||||
.mockReturnValue(fileStatusSubject)
|
||||
component.ngOnInit()
|
||||
fileStatusSubject.next(new FileStatus())
|
||||
expect(toastSpy).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should display toast on document failed', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
const fileStatusSubject = new Subject<FileStatus>()
|
||||
jest
|
||||
.spyOn(consumerStatusService, 'onDocumentConsumptionFailed')
|
||||
.mockReturnValue(fileStatusSubject)
|
||||
component.ngOnInit()
|
||||
fileStatusSubject.next(new FileStatus())
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should disable drag-drop if on dashboard', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
jest.spyOn(router, 'url', 'get').mockReturnValueOnce('/dashboard')
|
||||
expect(component.dragDropEnabled).toBeFalsy()
|
||||
jest.spyOn(router, 'url', 'get').mockReturnValueOnce('/documents')
|
||||
expect(component.dragDropEnabled).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should enable drag-drop if user has permissions', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
expect(component.dragDropEnabled).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should disable drag-drop if user does not have permissions', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(false)
|
||||
expect(component.dragDropEnabled).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support drag drop', fakeAsync(() => {
|
||||
expect(component.fileIsOver).toBeFalsy()
|
||||
component.fileOver()
|
||||
tick(1)
|
||||
fixture.detectChanges()
|
||||
expect(component.fileIsOver).toBeTruthy()
|
||||
const dropzone = fixture.debugElement.query(
|
||||
By.css('.global-dropzone-overlay')
|
||||
)
|
||||
expect(dropzone).not.toBeNull()
|
||||
component.fileLeave()
|
||||
tick(700)
|
||||
fixture.detectChanges()
|
||||
expect(dropzone.classes['hide']).toBeTruthy()
|
||||
// drop
|
||||
const toastSpy = jest.spyOn(toastService, 'show')
|
||||
const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFiles')
|
||||
component.dropped([])
|
||||
tick(3000)
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
expect(uploadSpy).toHaveBeenCalled()
|
||||
}))
|
||||
})
|
272
src-ui/src/app/components/app-frame/app-frame.component.spec.ts
Normal file
272
src-ui/src/app/components/app-frame/app-frame.component.spec.ts
Normal file
@@ -0,0 +1,272 @@
|
||||
import {
|
||||
HttpClientTestingModule,
|
||||
HttpTestingController,
|
||||
} from '@angular/common/http/testing'
|
||||
import { AppFrameComponent } from './app-frame.component'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { RouterTestingModule } from '@angular/router/testing'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
|
||||
import { RemoteVersionService } from 'src/app/services/rest/remote-version.service'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { of } from 'rxjs'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { environment } from 'src/environments/environment'
|
||||
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { DocumentDetailComponent } from '../document-detail/document-detail.component'
|
||||
import { SearchService } from 'src/app/services/rest/search.service'
|
||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
||||
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
|
||||
import { routes } from 'src/app/app-routing.module'
|
||||
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||
|
||||
const document = { id: 2, title: 'Hello world' }
|
||||
|
||||
describe('AppFrameComponent', () => {
|
||||
let component: AppFrameComponent
|
||||
let fixture: ComponentFixture<AppFrameComponent>
|
||||
let httpTestingController: HttpTestingController
|
||||
let settingsService: SettingsService
|
||||
let permissionsService: PermissionsService
|
||||
let remoteVersionService: RemoteVersionService
|
||||
let toastService: ToastService
|
||||
let openDocumentsService: OpenDocumentsService
|
||||
let searchService: SearchService
|
||||
let documentListViewService: DocumentListViewService
|
||||
let router: Router
|
||||
let savedViewSpy
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AppFrameComponent, IfPermissionsDirective],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
BrowserModule,
|
||||
RouterTestingModule.withRoutes(routes),
|
||||
NgbModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
providers: [
|
||||
SettingsService,
|
||||
SavedViewService,
|
||||
PermissionsService,
|
||||
RemoteVersionService,
|
||||
IfPermissionsDirective,
|
||||
ToastService,
|
||||
OpenDocumentsService,
|
||||
SearchService,
|
||||
{
|
||||
provide: ActivatedRoute,
|
||||
useValue: {
|
||||
firstChild: {
|
||||
component: DocumentDetailComponent,
|
||||
},
|
||||
snapshot: {
|
||||
firstChild: {
|
||||
component: DocumentDetailComponent,
|
||||
params: {
|
||||
id: document.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PermissionsGuard,
|
||||
],
|
||||
}).compileComponents()
|
||||
|
||||
settingsService = TestBed.inject(SettingsService)
|
||||
const savedViewService = TestBed.inject(SavedViewService)
|
||||
permissionsService = TestBed.inject(PermissionsService)
|
||||
remoteVersionService = TestBed.inject(RemoteVersionService)
|
||||
toastService = TestBed.inject(ToastService)
|
||||
openDocumentsService = TestBed.inject(OpenDocumentsService)
|
||||
searchService = TestBed.inject(SearchService)
|
||||
documentListViewService = TestBed.inject(DocumentListViewService)
|
||||
router = TestBed.inject(Router)
|
||||
|
||||
jest
|
||||
.spyOn(settingsService, 'displayName', 'get')
|
||||
.mockReturnValue('Hello World')
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
|
||||
savedViewSpy = jest.spyOn(savedViewService, 'initialize')
|
||||
|
||||
fixture = TestBed.createComponent(AppFrameComponent)
|
||||
component = fixture.componentInstance
|
||||
|
||||
httpTestingController = TestBed.inject(HttpTestingController)
|
||||
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
it('should initialize the saved view service', () => {
|
||||
expect(savedViewSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should check for update if enabled', () => {
|
||||
const updateCheckSpy = jest.spyOn(remoteVersionService, 'checkForUpdates')
|
||||
updateCheckSpy.mockImplementation(() => {
|
||||
return of({
|
||||
version: 'v100.0',
|
||||
update_available: true,
|
||||
})
|
||||
})
|
||||
settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, true)
|
||||
component.ngOnInit()
|
||||
expect(updateCheckSpy).toHaveBeenCalled()
|
||||
fixture.detectChanges()
|
||||
expect(fixture.nativeElement.textContent).toContain('Update available')
|
||||
})
|
||||
|
||||
it('should check not for update if disabled', () => {
|
||||
const updateCheckSpy = jest.spyOn(remoteVersionService, 'checkForUpdates')
|
||||
settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, false)
|
||||
component.ngOnInit()
|
||||
fixture.detectChanges()
|
||||
expect(updateCheckSpy).not.toHaveBeenCalled()
|
||||
expect(fixture.nativeElement.textContent).not.toContain('Update available')
|
||||
})
|
||||
|
||||
it('should check for update if was disabled and then enabled', () => {
|
||||
const updateCheckSpy = jest.spyOn(remoteVersionService, 'checkForUpdates')
|
||||
settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, false)
|
||||
component.setUpdateChecking(true)
|
||||
fixture.detectChanges()
|
||||
expect(updateCheckSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should show error on toggle update checking if store settings fails', () => {
|
||||
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, false)
|
||||
component.setUpdateChecking(true)
|
||||
httpTestingController
|
||||
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
|
||||
.flush('error', {
|
||||
status: 500,
|
||||
statusText: 'error',
|
||||
})
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support toggling slim sidebar and saving', fakeAsync(() => {
|
||||
const saveSettingSpy = jest.spyOn(settingsService, 'set')
|
||||
expect(component.slimSidebarEnabled).toBeFalsy()
|
||||
expect(component.slimSidebarAnimating).toBeFalsy()
|
||||
component.toggleSlimSidebar()
|
||||
expect(component.slimSidebarAnimating).toBeTruthy()
|
||||
tick(200)
|
||||
expect(component.slimSidebarAnimating).toBeFalsy()
|
||||
expect(component.slimSidebarEnabled).toBeTruthy()
|
||||
expect(saveSettingSpy).toHaveBeenCalledWith(
|
||||
SETTINGS_KEYS.SLIM_SIDEBAR,
|
||||
true
|
||||
)
|
||||
}))
|
||||
|
||||
it('should show error on toggle slim sidebar if store settings fails', () => {
|
||||
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
component.toggleSlimSidebar()
|
||||
httpTestingController
|
||||
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
|
||||
.flush('error', {
|
||||
status: 500,
|
||||
statusText: 'error',
|
||||
})
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support collapsable menu', () => {
|
||||
const button: HTMLButtonElement = (
|
||||
fixture.nativeElement as HTMLDivElement
|
||||
).querySelector('button[data-toggle=collapse]')
|
||||
button.dispatchEvent(new MouseEvent('click'))
|
||||
expect(component.isMenuCollapsed).toBeFalsy()
|
||||
component.closeMenu()
|
||||
expect(component.isMenuCollapsed).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should support close document & navigate on close current doc', () => {
|
||||
const closeSpy = jest.spyOn(openDocumentsService, 'closeDocument')
|
||||
closeSpy.mockReturnValue(of(true))
|
||||
const routerSpy = jest.spyOn(router, 'navigate')
|
||||
component.closeDocument(document)
|
||||
expect(closeSpy).toHaveBeenCalledWith(document)
|
||||
expect(routerSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support close all documents & navigate on close current doc', () => {
|
||||
const closeAllSpy = jest.spyOn(openDocumentsService, 'closeAll')
|
||||
closeAllSpy.mockReturnValue(of(true))
|
||||
const routerSpy = jest.spyOn(router, 'navigate')
|
||||
component.closeAll()
|
||||
expect(closeAllSpy).toHaveBeenCalled()
|
||||
expect(routerSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should close all documents on logout', () => {
|
||||
const closeAllSpy = jest.spyOn(openDocumentsService, 'closeAll')
|
||||
component.onLogout()
|
||||
expect(closeAllSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should warn before close if dirty documents', () => {
|
||||
jest.spyOn(openDocumentsService, 'hasDirty').mockReturnValue(true)
|
||||
expect(component.canDeactivate()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should call autocomplete endpoint on input', fakeAsync(() => {
|
||||
const autocompleteSpy = jest.spyOn(searchService, 'autocomplete')
|
||||
component.searchAutoComplete(of('hello')).subscribe()
|
||||
tick(250)
|
||||
expect(autocompleteSpy).toHaveBeenCalled()
|
||||
|
||||
component.searchAutoComplete(of('hello world 1')).subscribe()
|
||||
tick(250)
|
||||
expect(autocompleteSpy).toHaveBeenCalled()
|
||||
}))
|
||||
|
||||
it('should support reset search field', () => {
|
||||
const resetSpy = jest.spyOn(component, 'resetSearchField')
|
||||
const input = (fixture.nativeElement as HTMLDivElement).querySelector(
|
||||
'input'
|
||||
) as HTMLInputElement
|
||||
input.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }))
|
||||
expect(resetSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support choosing a search item', () => {
|
||||
expect(component.searchField.value).toEqual('')
|
||||
component.itemSelected({ item: 'hello', preventDefault: () => true })
|
||||
expect(component.searchField.value).toEqual('hello ')
|
||||
component.itemSelected({ item: 'world', preventDefault: () => true })
|
||||
expect(component.searchField.value).toEqual('hello world ')
|
||||
})
|
||||
|
||||
it('should navigate via quickFilter on search', () => {
|
||||
const str = 'hello world '
|
||||
component.searchField.patchValue(str)
|
||||
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
|
||||
component.search()
|
||||
expect(qfSpy).toHaveBeenCalledWith([
|
||||
{
|
||||
rule_type: FILTER_FULLTEXT_QUERY,
|
||||
value: str.trim(),
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
@@ -53,7 +53,7 @@ export class AppFrameComponent
|
||||
public settingsService: SettingsService,
|
||||
public tasksService: TasksService,
|
||||
private readonly toastService: ToastService,
|
||||
private permissionsService: PermissionsService
|
||||
permissionsService: PermissionsService
|
||||
) {
|
||||
super()
|
||||
|
||||
@@ -75,7 +75,7 @@ export class AppFrameComponent
|
||||
}
|
||||
|
||||
versionString = `${environment.appTitle} ${environment.version}`
|
||||
appRemoteVersion
|
||||
appRemoteVersion: AppRemoteVersion
|
||||
|
||||
isMenuCollapsed: boolean = true
|
||||
|
||||
@@ -103,7 +103,7 @@ export class AppFrameComponent
|
||||
this.toastService.showError(
|
||||
$localize`An error occurred while saving settings.`
|
||||
)
|
||||
console.log(error)
|
||||
console.warn(error)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -236,7 +236,7 @@ export class AppFrameComponent
|
||||
this.toastService.showError(
|
||||
$localize`An error occurred while saving update checking settings.`
|
||||
)
|
||||
console.log(error)
|
||||
console.warn(error)
|
||||
},
|
||||
})
|
||||
if (enable) {
|
||||
|
@@ -0,0 +1,43 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { ClearableBadgeComponent } from './clearable-badge.component'
|
||||
|
||||
describe('ClearableBadgeComponent', () => {
|
||||
let component: ClearableBadgeComponent
|
||||
let fixture: ComponentFixture<ClearableBadgeComponent>
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ClearableBadgeComponent],
|
||||
}).compileComponents()
|
||||
|
||||
fixture = TestBed.createComponent(ClearableBadgeComponent)
|
||||
component = fixture.componentInstance
|
||||
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
it('should support selected', () => {
|
||||
component.selected = true
|
||||
expect(component.active).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should support numbered', () => {
|
||||
component.number = 3
|
||||
fixture.detectChanges()
|
||||
expect(component.active).toBeTruthy()
|
||||
expect((fixture.nativeElement as HTMLDivElement).textContent).toContain('3')
|
||||
})
|
||||
|
||||
it('should support selected', () => {
|
||||
let clearedResult
|
||||
component.selected = true
|
||||
fixture.detectChanges()
|
||||
component.cleared.subscribe((clear) => {
|
||||
clearedResult = clear
|
||||
})
|
||||
fixture.nativeElement
|
||||
.querySelectorAll('button')[0]
|
||||
.dispatchEvent(new MouseEvent('click'))
|
||||
expect(clearedResult).toBeTruthy()
|
||||
})
|
||||
})
|
@@ -0,0 +1,99 @@
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
discardPeriodicTasks,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ConfirmDialogComponent } from './confirm-dialog.component'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||
import { Subject } from 'rxjs'
|
||||
|
||||
describe('ConfirmDialogComponent', () => {
|
||||
let component: ConfirmDialogComponent
|
||||
let modal: NgbActiveModal
|
||||
let fixture: ComponentFixture<ConfirmDialogComponent>
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ConfirmDialogComponent, SafeHtmlPipe],
|
||||
providers: [NgbActiveModal, SafeHtmlPipe],
|
||||
imports: [],
|
||||
}).compileComponents()
|
||||
|
||||
modal = TestBed.inject(NgbActiveModal)
|
||||
|
||||
fixture = TestBed.createComponent(ConfirmDialogComponent)
|
||||
component = fixture.componentInstance
|
||||
component.title = 'Confirm delete'
|
||||
component.messageBold = 'Do you really want to delete document file.pdf?'
|
||||
component.message =
|
||||
'The files for this document will be deleted permanently. This operation cannot be undone.'
|
||||
component.btnClass = 'btn-danger'
|
||||
component.btnCaption = 'Delete document'
|
||||
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
it('should support alternative', () => {
|
||||
let alternativeClickedResult
|
||||
let alternativeSubjectResult
|
||||
component.alternativeClicked.subscribe((result) => {
|
||||
alternativeClickedResult = true
|
||||
})
|
||||
component.alternative()
|
||||
// with subject
|
||||
const subject = new Subject<boolean>()
|
||||
component.alternativeSubject = subject
|
||||
subject.asObservable().subscribe((result) => {
|
||||
alternativeSubjectResult = result
|
||||
})
|
||||
component.alternative()
|
||||
expect(alternativeClickedResult).toBeTruthy()
|
||||
expect(alternativeSubjectResult).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should support confirm', () => {
|
||||
let confirmClickedResult
|
||||
let confirmSubjectResult
|
||||
component.confirmClicked.subscribe((result) => {
|
||||
confirmClickedResult = true
|
||||
})
|
||||
component.confirm()
|
||||
// with subject
|
||||
const subject = new Subject<boolean>()
|
||||
component.confirmSubject = subject
|
||||
subject.asObservable().subscribe((result) => {
|
||||
confirmSubjectResult = result
|
||||
})
|
||||
component.confirm()
|
||||
expect(confirmClickedResult).toBeTruthy()
|
||||
expect(confirmSubjectResult).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should support cancel & close modal', () => {
|
||||
let confirmSubjectResult
|
||||
const closeModalSpy = jest.spyOn(modal, 'close')
|
||||
component.cancel()
|
||||
const subject = new Subject<boolean>()
|
||||
component.confirmSubject = subject
|
||||
subject.asObservable().subscribe((result) => {
|
||||
confirmSubjectResult = result
|
||||
})
|
||||
component.cancel()
|
||||
// with subject
|
||||
expect(closeModalSpy).toHaveBeenCalled()
|
||||
expect(confirmSubjectResult).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support delay confirm', fakeAsync(() => {
|
||||
component.confirmButtonEnabled = false
|
||||
component.delayConfirm(1)
|
||||
expect(component.confirmButtonEnabled).toBeFalsy()
|
||||
tick(1500)
|
||||
fixture.detectChanges()
|
||||
expect(component.confirmButtonEnabled).toBeTruthy()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
})
|
@@ -5,13 +5,22 @@
|
||||
</button>
|
||||
<div class="dropdown-menu date-dropdown shadow pt-0" ngbDropdownMenu attr.aria-labelledby="dropdown{{title}}">
|
||||
<div class="list-group list-group-flush">
|
||||
<button *ngFor="let rd of relativeDates" class="list-group-item small list-goup list-group-item-action d-flex p-2" role="menuitem" (click)="setRelativeDate(rd.date)">
|
||||
<div class="selected-icon me-1">
|
||||
<svg *ngIf="relativeDate === rd.date" fill="currentColor" class="buttonicon-sm">
|
||||
<button *ngFor="let rd of relativeDates" class="list-group-item small list-goup list-group-item-action d-flex p-2" role="menuitem" (click)="setRelativeDate(rd.id)">
|
||||
<div class="selected-icon">
|
||||
<svg *ngIf="relativeDate === rd.id" fill="currentColor" class="buttonicon-sm">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#check"/>
|
||||
</svg>
|
||||
</div>
|
||||
{{rd.name}}
|
||||
<div class="d-flex justify-content-between w-100 align-items-center ps-2">
|
||||
<div class="pe-2 pe-lg-4">
|
||||
{{rd.name}}
|
||||
</div>
|
||||
<div class="text-muted small pe-2">
|
||||
<span class="small">
|
||||
{{ rd.date | customDate:'mediumDate' }} – <ng-container i18n>now</ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="list-group-item d-flex flex-column align-items-start" role="menuitem">
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
.date-dropdown {
|
||||
min-width: 250px;
|
||||
white-space: nowrap;
|
||||
|
||||
.btn-link {
|
||||
line-height: 1;
|
||||
|
@@ -0,0 +1,141 @@
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
let fixture: ComponentFixture<DateDropdownComponent>
|
||||
import {
|
||||
DateDropdownComponent,
|
||||
DateSelection,
|
||||
RelativeDate,
|
||||
} from './date-dropdown.component'
|
||||
import {
|
||||
HttpClientTestingModule,
|
||||
HttpTestingController,
|
||||
} from '@angular/common/http/testing'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { DatePipe } from '@angular/common'
|
||||
|
||||
describe('DateDropdownComponent', () => {
|
||||
let component: DateDropdownComponent
|
||||
let httpTestingController: HttpTestingController
|
||||
let settingsService: SettingsService
|
||||
let settingsSpy
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
DateDropdownComponent,
|
||||
ClearableBadgeComponent,
|
||||
CustomDatePipe,
|
||||
],
|
||||
providers: [SettingsService, CustomDatePipe, DatePipe],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
NgbModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
}).compileComponents()
|
||||
|
||||
httpTestingController = TestBed.inject(HttpTestingController)
|
||||
settingsService = TestBed.inject(SettingsService)
|
||||
settingsSpy = jest.spyOn(settingsService, 'getLocalizedDateInputFormat')
|
||||
|
||||
fixture = TestBed.createComponent(DateDropdownComponent)
|
||||
component = fixture.componentInstance
|
||||
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
it('should use a localized date placeholder', () => {
|
||||
expect(component.datePlaceHolder).toEqual('mm/dd/yyyy')
|
||||
expect(settingsSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support date input, emit change', fakeAsync(() => {
|
||||
let result: string
|
||||
component.dateAfterChange.subscribe((date) => (result = date))
|
||||
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
|
||||
input.value = '5/30/2023'
|
||||
input.dispatchEvent(new Event('change'))
|
||||
tick(500)
|
||||
expect(result).not.toBeNull()
|
||||
}))
|
||||
|
||||
it('should support date select, emit datesSet change', fakeAsync(() => {
|
||||
let result: DateSelection
|
||||
component.datesSet.subscribe((date) => (result = date))
|
||||
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
|
||||
input.value = '5/30/2023'
|
||||
input.dispatchEvent(new Event('dateSelect'))
|
||||
tick(500)
|
||||
expect(result).not.toBeNull()
|
||||
}))
|
||||
|
||||
it('should support relative dates', fakeAsync(() => {
|
||||
let result: DateSelection
|
||||
component.datesSet.subscribe((date) => (result = date))
|
||||
component.setRelativeDate(null)
|
||||
component.setRelativeDate(RelativeDate.LAST_7_DAYS)
|
||||
tick(500)
|
||||
expect(result).toEqual({
|
||||
after: null,
|
||||
before: null,
|
||||
relativeDateID: RelativeDate.LAST_7_DAYS,
|
||||
})
|
||||
}))
|
||||
|
||||
it('should support report if active', () => {
|
||||
component.relativeDate = RelativeDate.LAST_7_DAYS
|
||||
expect(component.isActive).toBeTruthy()
|
||||
component.relativeDate = null
|
||||
component.dateAfter = '2023-05-30'
|
||||
expect(component.isActive).toBeTruthy()
|
||||
component.dateAfter = null
|
||||
component.dateBefore = '2023-05-30'
|
||||
expect(component.isActive).toBeTruthy()
|
||||
component.dateBefore = null
|
||||
expect(component.isActive).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support reset', () => {
|
||||
component.dateAfter = '2023-05-30'
|
||||
component.reset()
|
||||
expect(component.dateAfter).toBeNull()
|
||||
})
|
||||
|
||||
it('should support clearAfter', () => {
|
||||
component.dateAfter = '2023-05-30'
|
||||
component.clearAfter()
|
||||
expect(component.dateAfter).toBeNull()
|
||||
})
|
||||
|
||||
it('should support clearBefore', () => {
|
||||
component.dateBefore = '2023-05-30'
|
||||
component.clearBefore()
|
||||
expect(component.dateBefore).toBeNull()
|
||||
})
|
||||
|
||||
it('should limit keyboard events', () => {
|
||||
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
|
||||
let event: KeyboardEvent = new KeyboardEvent('keypress', {
|
||||
key: '9',
|
||||
})
|
||||
let eventSpy = jest.spyOn(event, 'preventDefault')
|
||||
input.dispatchEvent(event)
|
||||
expect(eventSpy).not.toHaveBeenCalled()
|
||||
|
||||
event = new KeyboardEvent('keypress', {
|
||||
key: '{',
|
||||
})
|
||||
eventSpy = jest.spyOn(event, 'preventDefault')
|
||||
input.dispatchEvent(event)
|
||||
expect(eventSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
@@ -1,4 +1,3 @@
|
||||
import { formatDate } from '@angular/common'
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
@@ -39,20 +38,24 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
|
||||
|
||||
relativeDates = [
|
||||
{
|
||||
date: RelativeDate.LAST_7_DAYS,
|
||||
id: RelativeDate.LAST_7_DAYS,
|
||||
name: $localize`Last 7 days`,
|
||||
date: new Date().setDate(new Date().getDate() - 7),
|
||||
},
|
||||
{
|
||||
date: RelativeDate.LAST_MONTH,
|
||||
id: RelativeDate.LAST_MONTH,
|
||||
name: $localize`Last month`,
|
||||
date: new Date().setMonth(new Date().getMonth() - 1),
|
||||
},
|
||||
{
|
||||
date: RelativeDate.LAST_3_MONTHS,
|
||||
id: RelativeDate.LAST_3_MONTHS,
|
||||
name: $localize`Last 3 months`,
|
||||
date: new Date().setMonth(new Date().getMonth() - 3),
|
||||
},
|
||||
{
|
||||
date: RelativeDate.LAST_YEAR,
|
||||
id: RelativeDate.LAST_YEAR,
|
||||
name: $localize`Last year`,
|
||||
date: new Date().setFullYear(new Date().getFullYear() - 1),
|
||||
},
|
||||
]
|
||||
|
||||
|
@@ -0,0 +1,55 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog.component'
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing'
|
||||
import { EditDialogMode } from '../edit-dialog.component'
|
||||
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { SelectComponent } from '../../input/select/select.component'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { TextComponent } from '../../input/text/text.component'
|
||||
import { NgSelectModule } from '@ng-select/ng-select'
|
||||
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
|
||||
|
||||
describe('CorrespondentEditDialogComponent', () => {
|
||||
let component: CorrespondentEditDialogComponent
|
||||
let fixture: ComponentFixture<CorrespondentEditDialogComponent>
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
CorrespondentEditDialogComponent,
|
||||
IfPermissionsDirective,
|
||||
IfOwnerDirective,
|
||||
SelectComponent,
|
||||
TextComponent,
|
||||
PermissionsFormComponent,
|
||||
],
|
||||
providers: [NgbActiveModal],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
NgSelectModule,
|
||||
NgbModule,
|
||||
],
|
||||
}).compileComponents()
|
||||
|
||||
fixture = TestBed.createComponent(CorrespondentEditDialogComponent)
|
||||
component = fixture.componentInstance
|
||||
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
it('should support create and edit modes', () => {
|
||||
component.dialogMode = EditDialogMode.CREATE
|
||||
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
|
||||
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
|
||||
fixture.detectChanges()
|
||||
expect(createTitleSpy).toHaveBeenCalled()
|
||||
expect(editTitleSpy).not.toHaveBeenCalled()
|
||||
component.dialogMode = EditDialogMode.EDIT
|
||||
fixture.detectChanges()
|
||||
expect(editTitleSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user