Compare commits
196 commits
develop
...
feature/oi
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f9fda55d43 | ||
![]() |
f5381ea01b | ||
![]() |
5d31603a99 | ||
![]() |
7f66c73506 | ||
![]() |
02ec17317f | ||
![]() |
9755cec3d2 | ||
![]() |
c98bfe9493 | ||
![]() |
1dfc052d0a | ||
![]() |
6c8ec3eb3f | ||
![]() |
c86569f34e | ||
![]() |
21aa828a20 | ||
![]() |
48a68d0ec9 | ||
![]() |
9cd87a57e0 | ||
![]() |
8d11f4ad8d | ||
![]() |
c2451b9892 | ||
![]() |
ed2c9d2883 | ||
![]() |
9701817b02 | ||
![]() |
812ffac1e3 | ||
![]() |
dd8a242710 | ||
![]() |
9ea55e6cd9 | ||
![]() |
ce61e97c00 | ||
![]() |
537f64cb9e | ||
![]() |
86f6cf0de9 | ||
![]() |
9ccf5f634a | ||
![]() |
ff31f401be | ||
![]() |
2c0fdf89d6 | ||
![]() |
e61a8af05c | ||
![]() |
1b5b88a66f | ||
![]() |
7d6849fddd | ||
![]() |
c61d045811 | ||
![]() |
65e4ba4eea | ||
![]() |
e5b70a5fb2 | ||
![]() |
df9d8c7bd2 | ||
![]() |
0654acdb78 | ||
![]() |
7829dd1803 | ||
![]() |
cec5686f3b | ||
![]() |
3dc390f8cb | ||
![]() |
3dff2efbb0 | ||
![]() |
e3ecfb5a94 | ||
![]() |
f96fd688b0 | ||
![]() |
0e3d6f3ee9 | ||
![]() |
f5f836e433 | ||
![]() |
3c4486d430 | ||
![]() |
b76f6712df | ||
![]() |
373549a931 | ||
![]() |
7686c730e1 | ||
![]() |
cb0752da9d | ||
![]() |
0f9852ace4 | ||
![]() |
5a3ed38abb | ||
![]() |
068feb58fc | ||
![]() |
7b124357b4 | ||
![]() |
2e471cf6f9 | ||
![]() |
25614d6678 | ||
![]() |
0207d23ec8 | ||
![]() |
4f5c326268 | ||
![]() |
20dbb90f5f | ||
![]() |
baece3e4af | ||
![]() |
2186b923c9 | ||
![]() |
a68f6c1bef | ||
![]() |
d39cc9e1de | ||
![]() |
4ebad81d76 | ||
![]() |
91c4faa048 | ||
![]() |
b28931d8e7 | ||
![]() |
f1fe2c20d6 | ||
![]() |
c39060b0a6 | ||
![]() |
a60bb5f6d2 | ||
![]() |
178c160338 | ||
![]() |
b8761aa49c | ||
![]() |
ae882f948b | ||
![]() |
9a0b4da149 | ||
![]() |
02d9578f7b | ||
![]() |
56bf1c0b75 | ||
![]() |
7e973c3429 | ||
![]() |
b8618db336 | ||
![]() |
52049c7c93 | ||
![]() |
e24c4e86da | ||
![]() |
2cd832b6b2 | ||
![]() |
8e6a6e6108 | ||
![]() |
f69d4d0787 | ||
![]() |
8d3ae13f2c | ||
![]() |
4d86c8ecf6 | ||
![]() |
637a0548b2 | ||
![]() |
7f244c7301 | ||
![]() |
d4e7eebb4d | ||
![]() |
289bc0c5c3 | ||
![]() |
3ec492d470 | ||
![]() |
0a908ec443 | ||
![]() |
722df90e60 | ||
![]() |
fc3cf33d3f | ||
![]() |
7ef6d83b28 | ||
![]() |
cb151d158f | ||
![]() |
e107bfa8d0 | ||
![]() |
16ee40501e | ||
![]() |
4eacb28d8a | ||
![]() |
1ec9fc89a9 | ||
![]() |
31cdbec95c | ||
![]() |
950a3e84f8 | ||
![]() |
9a7c070604 | ||
![]() |
2ae440d1d1 | ||
![]() |
16eb023d89 | ||
![]() |
2dce7c37e5 | ||
![]() |
150328b6e5 | ||
![]() |
f9966beaec | ||
![]() |
d890c66446 | ||
![]() |
dd578b7142 | ||
![]() |
55bee9871f | ||
![]() |
b58da79a94 | ||
![]() |
61acb8a200 | ||
![]() |
a3e9e03e99 | ||
![]() |
2c41ed3862 | ||
![]() |
4455f11d58 | ||
![]() |
dd600aabd5 | ||
![]() |
f94744eee1 | ||
![]() |
1b73c51d3f | ||
![]() |
aa5f20e415 | ||
![]() |
3d75269a76 | ||
![]() |
1b2e60a7eb | ||
![]() |
b92b590635 | ||
![]() |
d983ec7c18 | ||
![]() |
49522c06a4 | ||
![]() |
aec89b53bd | ||
![]() |
e32966d43b | ||
![]() |
43d54a8e9c | ||
![]() |
1e654352f8 | ||
![]() |
ab602eda19 | ||
![]() |
c385ec73f4 | ||
![]() |
20c2e0ffdd | ||
![]() |
044490e6da | ||
![]() |
d8342d4546 | ||
![]() |
2f11587174 | ||
![]() |
39063d892d | ||
![]() |
039c9d9d57 | ||
![]() |
ab910b6f57 | ||
![]() |
9f19551adf | ||
![]() |
7d576e4e6b | ||
![]() |
4b51282762 | ||
![]() |
c836c8ea9d | ||
![]() |
2e6c0e0dc0 | ||
![]() |
4c91cd82d6 | ||
![]() |
8bfdafdffe | ||
![]() |
fdcacb58d0 | ||
![]() |
2be9da7a54 | ||
![]() |
76af6e9ccf | ||
![]() |
7842f50728 | ||
![]() |
117c3d7abb | ||
![]() |
2d7a063ae4 | ||
![]() |
9a4b0cadaf | ||
![]() |
31381627b4 | ||
![]() |
c8c7de09d5 | ||
![]() |
9811c86ee8 | ||
![]() |
d817a42ea2 | ||
![]() |
246d29e68a | ||
![]() |
34fb9b3b73 | ||
![]() |
0e09eee673 | ||
![]() |
e29dfcab1c | ||
![]() |
0facf42981 | ||
![]() |
77a249667b | ||
![]() |
e6680d7e1f | ||
![]() |
4c5ef8841b | ||
![]() |
01c32ff15b | ||
![]() |
304b52f3a1 | ||
![]() |
1a00fed2c9 | ||
![]() |
d1cfa2f3d9 | ||
![]() |
b1b991da6c | ||
![]() |
ca0abb1a95 | ||
![]() |
c660a8f9c4 | ||
![]() |
1d0949dad8 | ||
![]() |
b70a4b9831 | ||
![]() |
f81dc35b04 | ||
![]() |
cc6be6ba2f | ||
![]() |
1eb94f9b34 | ||
![]() |
10a6ec8eef | ||
![]() |
d6e81cc0b3 | ||
![]() |
23f12e38ca | ||
![]() |
8d8f0df58f | ||
![]() |
e3099e0cb6 | ||
![]() |
8b00cd068c | ||
![]() |
f278fb69c1 | ||
![]() |
a7400d6477 | ||
![]() |
5bac7ba11d | ||
![]() |
24f6041a76 | ||
![]() |
1ae85a0e04 | ||
![]() |
b5ddd0d3bb | ||
![]() |
d51f24bde9 | ||
![]() |
da7035c106 | ||
![]() |
9bdc38be9a | ||
![]() |
dea527591e | ||
![]() |
0cc9dcb2b9 | ||
![]() |
d6fd6cf297 | ||
![]() |
457085d7bd | ||
![]() |
d7aa739d26 | ||
![]() |
5d26325b7d | ||
![]() |
7e754e8cfa | ||
![]() |
e7db639c54 | ||
![]() |
6634e8a56b | ||
![]() |
aa8ebc2076 |
153 changed files with 12499 additions and 986 deletions
|
@ -37,6 +37,7 @@ steps:
|
|||
when:
|
||||
branch:
|
||||
- develop
|
||||
- feature/*
|
||||
|
||||
- name: test
|
||||
commands:
|
||||
|
@ -177,6 +178,6 @@ steps:
|
|||
|
||||
---
|
||||
kind: signature
|
||||
hmac: 61a27b3c3142a9a1d1972f0fe00a8aa19d99623e6fea6e1d5ad44b6b20cd5cd1
|
||||
hmac: c2c78e8f8a5537005233ef69ea85f0b5ce6131913037b2a44f401505b887de3f
|
||||
|
||||
...
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
FROM photoprism/development:20211210
|
||||
FROM photoprism/develop:20211218
|
||||
|
||||
# Copy latest entrypoint script
|
||||
COPY --chown=root:root /docker/development/entrypoint.sh /entrypoint.sh
|
||||
COPY --chown=root:root /docker/develop/entrypoint.sh /entrypoint.sh
|
||||
COPY --chown=root:root /docker/scripts/Makefile /root/Makefile
|
||||
|
||||
# Set up project directory
|
||||
|
|
145
Makefile
145
Makefile
|
@ -22,7 +22,7 @@ else
|
|||
GOTEST=go test
|
||||
endif
|
||||
|
||||
all: dep build
|
||||
all: tidy dep build
|
||||
dep: dep-tensorflow dep-js dep-go
|
||||
build: generate build-js build-go
|
||||
install: install-bin install-assets
|
||||
|
@ -33,42 +33,32 @@ test-api: reset-testdb run-test-api
|
|||
test-short: reset-testdb run-test-short
|
||||
acceptance-private-run-chromium: acceptance-private-restart acceptance-private acceptance-private-stop
|
||||
acceptance-public-run-chromium: acceptance-restart acceptance acceptance-stop
|
||||
acceptance-openid-run-chromium: acceptance-openid-restart acceptance-openid acceptance-openid-stop
|
||||
acceptance-private-run-firefox: acceptance-private-restart acceptance-private-firefox acceptance-private-stop
|
||||
acceptance-public-run-firefox: acceptance-restart acceptance-firefox acceptance-stop
|
||||
acceptance-run-chromium: acceptance-private-restart acceptance-private acceptance-private-stop acceptance-restart acceptance acceptance-stop
|
||||
acceptance-run-firefox: acceptance-private-restart acceptance-private-firefox acceptance-private-stop acceptance-restart acceptance-firefox acceptance-stop
|
||||
acceptance-openid-run-firefox: acceptance-openid-restart acceptance-openid-firefox acceptance-openid-stop
|
||||
acceptance-run-chromium: acceptance-private-restart acceptance-private acceptance-private-stop acceptance-restart acceptance acceptance-stop acceptance-openid-restart acceptance-openid acceptance-openid-stop
|
||||
acceptance-run-firefox: acceptance-private-restart acceptance-private-firefox acceptance-private-stop acceptance-restart acceptance-firefox acceptance-stop acceptance-openid-restart acceptance-openid-firefox acceptance-openid-stop
|
||||
test-all: test acceptance-run-chromium
|
||||
fmt: fmt-js fmt-go
|
||||
upgrade: dep-upgrade-js dep-upgrade
|
||||
clean-local: clean-local-config clean-local-cache
|
||||
clean-install: clean-local dep build-js install-bin install-assets
|
||||
dev: dev-npm dev-go-amd64
|
||||
dev-npm:
|
||||
$(info Upgrading NPM in local dev environment...)
|
||||
debug-go: build-go-remote start-debug
|
||||
### Development Environment
|
||||
upgrade-amd64: upgrade-npm upgrade-go-amd64 # Upgrades NPM & Go in local AMD64 dev environment
|
||||
upgrade-arm64: upgrade-npm upgrade-go-arm64 # Upgrades NPM & Go in local ARM64 dev environment
|
||||
upgrade-npm:
|
||||
$(info Upgrading NPM in dev environment...)
|
||||
sudo npm update -g npm
|
||||
dev-go-amd64:
|
||||
$(info Installing Go in local AMD64 dev environment...)
|
||||
upgrade-go-amd64:
|
||||
$(info Upgrading Go in AMD64 dev environment...)
|
||||
sudo docker/scripts/install-go.sh amd64
|
||||
go build -v ./...
|
||||
acceptance-restart:
|
||||
cp -f storage/acceptance/backup.db storage/acceptance/index.db
|
||||
cp -f storage/acceptance/config/settingsBackup.yml storage/acceptance/config/settings.yml
|
||||
rm -rf storage/acceptance/sidecar/2020
|
||||
rm -rf storage/acceptance/sidecar/2011
|
||||
rm -rf storage/acceptance/originals/2010
|
||||
rm -rf storage/acceptance/originals/2020
|
||||
rm -rf storage/acceptance/originals/2011
|
||||
rm -rf storage/acceptance/originals/2013
|
||||
rm -rf storage/acceptance/originals/2017
|
||||
go run cmd/photoprism/photoprism.go --public --upload-nsfw=false --database-driver sqlite --database-dsn ./storage/acceptance/index.db --import-path ./storage/acceptance/import --http-port=2343 --config-path ./storage/acceptance/config --originals-path ./storage/acceptance/originals --storage-path ./storage/acceptance --test --backup-path ./storage/acceptance/backup --disable-backups start -d
|
||||
acceptance-stop:
|
||||
go run cmd/photoprism/photoprism.go --public --upload-nsfw=false --database-driver sqlite --database-dsn ./storage/acceptance/index.db --import-path ./storage/acceptance/import --http-port=2343 --config-path ./storage/acceptance/config --originals-path ./storage/acceptance/originals --storage-path ./storage/acceptance --test --backup-path ./storage/acceptance/backup --disable-backups stop
|
||||
acceptance-private-restart:
|
||||
cp -f storage/acceptance/backup.db storage/acceptance/index.db
|
||||
cp -f storage/acceptance/config/settingsBackup.yml storage/acceptance/config/settings.yml
|
||||
go run cmd/photoprism/photoprism.go --public=false --upload-nsfw=false --database-driver sqlite --database-dsn ./storage/acceptance/index.db --import-path ./storage/acceptance/import --http-port=2343 --config-path ./storage/acceptance/config --originals-path ./storage/acceptance/originals --storage-path ./storage/acceptance --test --backup-path ./storage/acceptance/backup --disable-backups start -d
|
||||
acceptance-private-stop:
|
||||
go run cmd/photoprism/photoprism.go --public=false --upload-nsfw=false --database-driver sqlite --database-dsn ./storage/acceptance/index.db --import-path ./storage/acceptance/import --http-port=2343 --config-path ./storage/acceptance/config --originals-path ./storage/acceptance/originals --storage-path ./storage/acceptance --test --backup-path ./storage/acceptance/backup --disable-backups stop
|
||||
upgrade-go-arm64:
|
||||
$(info Upgrading Go in ARM64 dev environment...)
|
||||
sudo docker/scripts/install-go.sh arm64
|
||||
go build -v ./...
|
||||
start:
|
||||
go run cmd/photoprism/photoprism.go start -d
|
||||
stop:
|
||||
|
@ -87,6 +77,7 @@ generate:
|
|||
@if [ ${$(shell git diff --shortstat assets/locales/messages.pot):1:45} == $(POT_UNCHANGED) ]; then\
|
||||
git checkout -- assets/locales/messages.pot;\
|
||||
fi
|
||||
### Production Build & Installation
|
||||
install-bin:
|
||||
scripts/build.sh prod ~/.local/bin/$(BINARY_NAME)
|
||||
install-assets:
|
||||
|
@ -99,12 +90,7 @@ install-assets:
|
|||
mkdir -p ~/Pictures/Import
|
||||
cp -r assets/locales assets/facenet assets/nasnet assets/nsfw assets/profiles assets/static assets/templates ~/.photoprism/assets
|
||||
find ~/.photoprism/assets -name '.*' -type f -delete
|
||||
clean-local-assets:
|
||||
rm -rf ~/.photoprism/assets/*
|
||||
clean-local-cache:
|
||||
rm -rf ~/.photoprism/storage/cache/*
|
||||
clean-local-config:
|
||||
rm -f ~/.photoprism/storage/config/*
|
||||
### Dependencies
|
||||
dep-list:
|
||||
go list -u -m -json all | go-mod-outdated -direct
|
||||
dep-js:
|
||||
|
@ -125,11 +111,14 @@ zip-nasnet:
|
|||
(cd assets && zip -r nasnet.zip nasnet -x "*/.*" -x "*/version.txt")
|
||||
zip-nsfw:
|
||||
(cd assets && zip -r nsfw.zip nsfw -x "*/.*" -x "*/version.txt")
|
||||
### Build Commands
|
||||
build-js:
|
||||
(cd frontend && env NODE_ENV=production npm run build)
|
||||
build-go:
|
||||
rm -f $(BINARY_NAME)
|
||||
scripts/build.sh debug $(BINARY_NAME)
|
||||
build-go-remote:
|
||||
docker-compose exec -u root photoprism make build-go
|
||||
build-race:
|
||||
rm -f $(BINARY_NAME)
|
||||
scripts/build.sh race $(BINARY_NAME)
|
||||
|
@ -142,11 +131,16 @@ build-tensorflow:
|
|||
build-tensorflow-arm64:
|
||||
docker build -t photoprism/tensorflow:arm64 docker/tensorflow/arm64
|
||||
docker run -ti photoprism/tensorflow:arm64 bash
|
||||
start-debug:
|
||||
docker-compose exec -u root photoprism go install github.com/go-delve/delve/cmd/dlv@latest
|
||||
docker-compose exec -u root photoprism dlv --listen=:40000 --headless=true --api-version=2 --accept-multiclient exec ./photoprism start
|
||||
### Frontend Tests
|
||||
watch-js:
|
||||
(cd frontend && env NODE_ENV=development npm run watch)
|
||||
test-js:
|
||||
$(info Running JS unit tests...)
|
||||
(cd frontend && env NODE_ENV=development BABEL_ENV=test npm run test)
|
||||
### Acceptance Tests
|
||||
acceptance:
|
||||
$(info Running JS acceptance tests in Chrome...)
|
||||
(cd frontend && npm run acceptance && cd ..)
|
||||
|
@ -159,6 +153,38 @@ acceptance-private:
|
|||
acceptance-private-firefox:
|
||||
$(info Running JS acceptance-private tests in Firefox...)
|
||||
(cd frontend && npm run acceptance-private-firefox && cd ..)
|
||||
acceptance-openid:
|
||||
$(info Running JS acceptance-private tests in Chrome...)
|
||||
(cd frontend && npm run acceptance-openid && cd ..)
|
||||
acceptance-openid-firefox:
|
||||
$(info Running JS acceptance-private tests in Firefox...)
|
||||
(cd frontend && npm run acceptance-openid-firefox && cd ..)
|
||||
acceptance-restart:
|
||||
cp -f storage/acceptance/backup.db storage/acceptance/index.db
|
||||
cp -f storage/acceptance/config/settingsBackup.yml storage/acceptance/config/settings.yml
|
||||
rm -rf storage/acceptance/sidecar/2020
|
||||
rm -rf storage/acceptance/sidecar/2011
|
||||
rm -rf storage/acceptance/originals/2010
|
||||
rm -rf storage/acceptance/originals/2020
|
||||
rm -rf storage/acceptance/originals/2011
|
||||
rm -rf storage/acceptance/originals/2013
|
||||
rm -rf storage/acceptance/originals/2017
|
||||
go run cmd/photoprism/photoprism.go --public --upload-nsfw=false --database-driver="sqlite" --database-dsn="./storage/acceptance/index.db" --import-path="./storage/acceptance/import" --http-port=2343 --config-path="./storage/acceptance/config" --originals-path="./storage/acceptance/originals" --storage-path="./storage/acceptance" --test --backup-path="./storage/acceptance/backup" --disable-backups start -d
|
||||
acceptance-stop:
|
||||
go run cmd/photoprism/photoprism.go --public --upload-nsfw=false --database-driver="sqlite" --database-dsn="./storage/acceptance/index.db" --import-path="./storage/acceptance/import" --http-port=2343 --config-path="./storage/acceptance/config" --originals-path="./storage/acceptance/originals" --storage-path="./storage/acceptance" --test --backup-path="./storage/acceptance/backup" --disable-backups stop
|
||||
acceptance-private-restart:
|
||||
cp -f storage/acceptance/backup.db storage/acceptance/index.db
|
||||
cp -f storage/acceptance/config/settingsBackup.yml storage/acceptance/config/settings.yml
|
||||
go run cmd/photoprism/photoprism.go --oidc-issuer-url="" --oidc-client-id="" --oidc-client-secret="" --public=false --upload-nsfw=false --database-driver="sqlite" --database-dsn="./storage/acceptance/index.db" --import-path="./storage/acceptance/import" --http-port=2343 --config-path="./storage/acceptance/config" --originals-path="./storage/acceptance/originals" --storage-path="./storage/acceptance" --test --backup-path="./storage/acceptance/backup" --disable-backups start -d
|
||||
acceptance-private-stop:
|
||||
go run cmd/photoprism/photoprism.go --oidc-issuer-url="" --oidc-client-id="" --oidc-client-secret="" --public=false --upload-nsfw=false --database-driver="sqlite" --database-dsn="./storage/acceptance/index.db" --import-path="./storage/acceptance/import" --http-port=2343 --config-path="./storage/acceptance/config" --originals-path="./storage/acceptance/originals" --storage-path="./storage/acceptance" --test --backup-path="./storage/acceptance/backup" --disable-backups stop
|
||||
acceptance-openid-restart:
|
||||
cp -f storage/acceptance/backup.db storage/acceptance/index.db
|
||||
cp -f storage/acceptance/config/settingsBackup.yml storage/acceptance/config/settings.yml
|
||||
go run cmd/photoprism/photoprism.go --public=false --upload-nsfw=false --database-driver="sqlite" --database-dsn="./storage/acceptance/index.db" --import-path="./storage/acceptance/import" --http-port=2342 --config-path="./storage/acceptance/config" --originals-path="./storage/acceptance/originals" --storage-path="./storage/acceptance" --test --backup-path="./storage/acceptance/backup" --disable-backups start -d
|
||||
acceptance-openid-stop:
|
||||
go run cmd/photoprism/photoprism.go --public=false --upload-nsfw=false --database-driver="sqlite" --database-dsn="./storage/acceptance/index.db" --import-path="./storage/acceptance/import" --http-port=2342 --config-path="./storage/acceptance/config" --originals-path="./storage/acceptance/originals" --storage-path="./storage/acceptance" --test --backup-path="./storage/acceptance/backup" --disable-backups stop
|
||||
### Backend Tests
|
||||
reset-mariadb:
|
||||
$(info Resetting photoprism database...)
|
||||
mysql < scripts/sql/reset-mariadb.sql
|
||||
|
@ -194,38 +220,29 @@ test-coverage:
|
|||
$(info Running all Go unit tests with code coverage report...)
|
||||
go test -parallel 1 -count 1 -cpu 1 -failfast -tags slow -timeout 30m -coverprofile coverage.txt -covermode atomic ./pkg/... ./internal/...
|
||||
go tool cover -html=coverage.txt -o coverage.html
|
||||
clean:
|
||||
rm -f $(BINARY_NAME)
|
||||
rm -f *.log
|
||||
rm -rf node_modules
|
||||
rm -rf storage/testdata
|
||||
rm -rf storage/backup
|
||||
rm -rf storage/cache
|
||||
rm -rf frontend/node_modules
|
||||
docker-development:
|
||||
### 64-bit Multi-Arch Docker Image
|
||||
docker-develop:
|
||||
docker pull --platform=amd64 ubuntu:21.10
|
||||
docker pull --platform=arm64 ubuntu:21.10
|
||||
scripts/docker/multiarch.sh development linux/amd64,linux/arm64 $(DOCKER_TAG)
|
||||
scripts/docker/multiarch.sh develop linux/amd64,linux/arm64 $(DOCKER_TAG)
|
||||
docker-preview:
|
||||
scripts/docker/multiarch.sh photoprism linux/amd64,linux/arm64
|
||||
docker-release:
|
||||
scripts/docker/multiarch.sh photoprism linux/amd64,linux/arm64 $(DOCKER_TAG)
|
||||
docker-armv7-development:
|
||||
docker pull --platform=arm ubuntu:21.10
|
||||
scripts/docker/arch.sh development linux/arm armv7 /armv7
|
||||
docker-armv7-preview:
|
||||
docker pull --platform=arm photoprism/development:armv7
|
||||
### ARMv7 32-bit Docker Image
|
||||
armv7-develop:
|
||||
scripts/docker/arch.sh develop linux/arm armv7 /armv7
|
||||
armv7-preview:
|
||||
docker pull --platform=arm photoprism/develop:armv7
|
||||
scripts/docker/arch.sh photoprism linux/arm armv7-preview /armv7
|
||||
docker-armv7-release:
|
||||
docker pull --platform=arm photoprism/development:armv7
|
||||
armv7-release:
|
||||
docker pull --platform=arm photoprism/develop:armv7
|
||||
scripts/docker/arch.sh photoprism linux/arm armv7 /armv7
|
||||
### Additional Docker Images / Commands for Development & Testing
|
||||
docker-local:
|
||||
scripts/docker/build.sh photoprism
|
||||
docker-pull:
|
||||
docker pull photoprism/photoprism:preview photoprism/photoprism:latest
|
||||
docker-goproxy:
|
||||
docker pull golang:alpine
|
||||
scripts/docker/multiarch.sh goproxy linux/amd64,linux/arm64 $(DOCKER_TAG)
|
||||
docker-demo:
|
||||
scripts/docker/build.sh demo $(DOCKER_TAG)
|
||||
scripts/docker/push.sh demo $(DOCKER_TAG)
|
||||
|
@ -234,20 +251,20 @@ docker-demo-local:
|
|||
scripts/docker/build.sh demo $(DOCKER_TAG)
|
||||
scripts/docker/push.sh demo $(DOCKER_TAG)
|
||||
docker-dummy-webdav:
|
||||
docker pull --platform=amd64 golang:1
|
||||
docker pull --platform=arm64 golang:1
|
||||
docker pull golang:alpine
|
||||
scripts/docker/multiarch.sh dummy-webdav linux/amd64,linux/arm64 $(DOCKER_TAG)
|
||||
docker-dummy-oidc:
|
||||
docker pull --platform=amd64 golang:1
|
||||
docker pull --platform=arm64 golang:1
|
||||
docker pull golang:alpine
|
||||
scripts/docker/multiarch.sh dummy-oidc linux/amd64,linux/arm64 $(DOCKER_TAG)
|
||||
packer-digitalocean:
|
||||
$(info Buildinng DigitalOcean marketplace image...)
|
||||
(cd ./docker/examples/cloud && packer build digitalocean.json)
|
||||
### CI
|
||||
drone-sign:
|
||||
drone sign photoprism/photoprism --save
|
||||
lint-js:
|
||||
(cd frontend && npm run lint)
|
||||
### Build & Code Clean-Up
|
||||
fmt-js:
|
||||
(cd frontend && npm run fmt)
|
||||
fmt-go:
|
||||
|
@ -255,3 +272,17 @@ fmt-go:
|
|||
goimports -w pkg internal cmd
|
||||
tidy:
|
||||
go mod tidy
|
||||
clean:
|
||||
rm -f $(BINARY_NAME)
|
||||
rm -f *.log
|
||||
rm -rf node_modules
|
||||
rm -rf storage/testdata
|
||||
rm -rf storage/backup
|
||||
rm -rf storage/cache
|
||||
rm -rf frontend/node_modules
|
||||
clean-local-assets:
|
||||
rm -rf ~/.photoprism/assets/*
|
||||
clean-local-cache:
|
||||
rm -rf ~/.photoprism/storage/cache/*
|
||||
clean-local-config:
|
||||
rm -f ~/.photoprism/storage/config/*
|
||||
|
|
58
assets/templates/callback.tmpl
Normal file
58
assets/templates/callback.tmpl
Normal file
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0{{if not .config.Settings.UI.Zoom }}, maximum-scale=1.0, user-scalable=no{{end}}">
|
||||
<title>{{ .config.SiteTitle }}</title>
|
||||
|
||||
<meta property="og:url" content="{{ .config.SiteUrl }}">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="{{ .config.SiteTitle }}{{if .config.SiteCaption}}: {{ .config.SiteCaption }}{{end}}">
|
||||
<meta property="og:image" content="{{ .config.SitePreview }}">
|
||||
<meta property="og:description" content="{{ .config.SiteDescription }}">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="{{ .config.SiteTitle }}{{if .config.SiteCaption}}: {{ .config.SiteCaption }}{{end}}">
|
||||
<meta name="twitter:image" content="{{ .config.SitePreview }}">
|
||||
<meta name="twitter:description" content="{{ .config.SiteDescription }}">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="{{ .config.SiteTitle }}">
|
||||
|
||||
{{if .config.SiteAuthor}}<meta name="author" content="{{ .config.SiteAuthor }}">{{end}}
|
||||
{{if .config.SiteDescription}}<meta name="description" content="{{ .config.SiteDescription }}"/>{{end}}
|
||||
|
||||
{{template "favicons.tmpl" .}}
|
||||
|
||||
<link rel="stylesheet" href="/static/build/app.css?{{ .config.CSSHash }}">
|
||||
<link rel="manifest" href="/static/manifest.json?{{ .config.ManifestHash }}">
|
||||
|
||||
<script>
|
||||
window.__CONFIG__ = {{ .config }};
|
||||
</script>
|
||||
</head>
|
||||
<body class="{{ .config.Flags }}">
|
||||
{{ if and .status (eq .status "ok") }}
|
||||
<p class="browserupgrade">Login successful. You can safely close this tab.</p>
|
||||
{{ else if and .status (eq .status "error") }}
|
||||
<p class="browserupgrade">Login Error: {{ .errors }}</p>
|
||||
{{ else }}
|
||||
<p class="browserupgrade">Undefined State or deprecated link user flow...</p>
|
||||
{{ end }}
|
||||
|
||||
<script>
|
||||
{{ if and .status (eq .status "ok") }}
|
||||
window.localStorage.setItem("session_id", {{ .id }})
|
||||
window.localStorage.setItem("data", JSON.stringify({{ .data }}));
|
||||
window.localStorage.setItem("config", JSON.stringify({{ .config }}));
|
||||
window.sessionStorage.removeItem("preventAutoLogin");
|
||||
window.location.href = '/login';
|
||||
{{ else if and .status (eq .status "error") }}
|
||||
window.localStorage.setItem("auth_error", {{ .errors }});
|
||||
window.location.href = '/login?preventAutoLogin=true';
|
||||
{{ end }}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -39,7 +39,7 @@ import (
|
|||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var version = "development"
|
||||
var version = "develop"
|
||||
var log = event.Log
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -17,11 +17,11 @@ services:
|
|||
- "~/.cache/npm:/root/.cache/npm"
|
||||
- "~/.cache/go-mod:/go/pkg/mod"
|
||||
environment:
|
||||
GOPROXY: ${GOPROXY:-http://goproxy:8888,direct}
|
||||
GOPROXY: "https://proxy.golang.org,direct"
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
||||
PHOTOPRISM_SITE_CAPTION: "Browse Your Life"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "AI-Powered Photos App. Tags and finds pictures without getting in your way!"
|
||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||
PHOTOPRISM_DEBUG: "false"
|
||||
PHOTOPRISM_READONLY: "false"
|
||||
|
@ -152,7 +152,7 @@ services:
|
|||
expose:
|
||||
- "4001" # Database port (internal)
|
||||
volumes:
|
||||
- "./scripts/sql/init-test-databases.sql:/docker-entrypoint-initdb.d/init-test-databases.sql"
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
|
@ -176,7 +176,11 @@ services:
|
|||
|
||||
## Dummy WebDAV Server
|
||||
dummy-webdav:
|
||||
image: photoprism/dummy-webdav:20211109
|
||||
image: photoprism/dummy-webdav:20211218
|
||||
environment:
|
||||
WEBDAV_USERNAME: admin
|
||||
WEBDAV_PASSWORD: photoprism
|
||||
|
||||
## Dummy OpenID Connect Server
|
||||
dummy-oidc:
|
||||
image: photoprism/dummy-oidc:20211218
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
version: '3.5'
|
||||
|
||||
# Stable Release (for developers only)
|
||||
|
||||
## Latest Stable Release for QA
|
||||
services:
|
||||
## App Server (required)
|
||||
## App Server
|
||||
## Docs: https://docs.photoprism.org/
|
||||
photoprism-latest:
|
||||
image: photoprism/photoprism:latest
|
||||
security_opt:
|
||||
|
@ -11,13 +11,23 @@ services:
|
|||
- apparmor:unconfined
|
||||
ports:
|
||||
- "2344:2342" # HTTP port (host:container)
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.photoprism-latest.loadbalancer.server.port=2342"
|
||||
- "traefik.http.routers.photoprism-latest.entrypoints=websecure"
|
||||
- "traefik.http.routers.photoprism-latest.rule=Host(`photoprism-latest.traefik.net`)"
|
||||
- "traefik.http.routers.photoprism-latest.tls.domains[0].main=traefik.net"
|
||||
- "traefik.http.routers.photoprism-latest.tls.domains[0].sans=*.traefik.net"
|
||||
- "traefik.http.routers.photoprism-latest.tls=true"
|
||||
environment:
|
||||
PHOTOPRISM_UID: ${UID:-1000}
|
||||
PHOTOPRISM_GID: ${GID:-1000}
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2344/"
|
||||
PHOTOPRISM_UID: ${UID:-1000} # User ID
|
||||
PHOTOPRISM_GID: ${GID:-1000} # Group ID
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # Admin password (min 4 characters)
|
||||
## Public server URL incl http:// or https:// and /path, :port is optional
|
||||
PHOTOPRISM_SITE_URL: "https://photoprism-latest.traefik.net/"
|
||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
||||
PHOTOPRISM_SITE_CAPTION: "Browse Your Life"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "AI-Powered Photos App. Tags and finds pictures without getting in your way!"
|
||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||
PHOTOPRISM_DEBUG: "true"
|
||||
PHOTOPRISM_READONLY: "false"
|
||||
|
@ -29,10 +39,9 @@ services:
|
|||
PHOTOPRISM_HTTP_COMPRESSION: "gzip" # Improves transfer speed and bandwidth utilization (none or gzip)
|
||||
PHOTOPRISM_DATABASE_DRIVER: "mysql"
|
||||
PHOTOPRISM_DATABASE_SERVER: "mariadb:4001"
|
||||
PHOTOPRISM_DATABASE_NAME: "latest"
|
||||
PHOTOPRISM_DATABASE_USER: "root"
|
||||
PHOTOPRISM_DATABASE_PASSWORD: "photoprism"
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # The initial admin password (min 4 characters)
|
||||
PHOTOPRISM_DATABASE_NAME: "photoprism_latest"
|
||||
PHOTOPRISM_DATABASE_USER: "photoprism_latest"
|
||||
PHOTOPRISM_DATABASE_PASSWORD: "photoprism_latest"
|
||||
PHOTOPRISM_DISABLE_CHOWN: "false" # Disables storage permission updates on startup
|
||||
PHOTOPRISM_DISABLE_BACKUPS: "false" # Don't backup photo and album metadata to YAML files
|
||||
PHOTOPRISM_DISABLE_WEBDAV: "false" # Disables built-in WebDAV server
|
||||
|
@ -56,7 +65,8 @@ services:
|
|||
- "./storage/latest:/photoprism/storage"
|
||||
- "./storage/originals:/photoprism/originals"
|
||||
|
||||
## Join shared "photoprism-develop" network
|
||||
networks:
|
||||
default:
|
||||
external:
|
||||
name: shared
|
||||
name: photoprism-develop
|
||||
|
|
|
@ -1,9 +1,40 @@
|
|||
version: '3.5'
|
||||
|
||||
# Legacy Databases Servers (for developers only)
|
||||
|
||||
## MariaDB Server Versions for Development & Testing
|
||||
services:
|
||||
## Affected by MDEV-25362: Incorrect name resolution for subqueries in ON expressions
|
||||
## MariaDB 10.7 Database Server
|
||||
mariadb-10-7:
|
||||
image: mariadb:10.7
|
||||
command: mysqld --port=4001 --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # Database port (internal)
|
||||
volumes:
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
MYSQL_PASSWORD: photoprism
|
||||
MYSQL_DATABASE: photoprism
|
||||
|
||||
## MariaDB 10.6 Database Server
|
||||
## Docs: https://mariadb.com/docs/reference/cs10.6/
|
||||
mariadb:
|
||||
image: mariadb:10.6
|
||||
command: mysqld --port=4001 --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001"
|
||||
ports:
|
||||
- "4001:4001" # Database port (host:container)
|
||||
volumes:
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
MYSQL_PASSWORD: photoprism
|
||||
MYSQL_DATABASE: photoprism
|
||||
|
||||
## MariaDB 10.5.5 Database Server
|
||||
## affected by MDEV-25362: Incorrect name resolution for subqueries in ON expressions
|
||||
## see https://jira.mariadb.org/browse/MDEV-25362
|
||||
mariadb-10-5-5:
|
||||
image: mariadb:10.5.5
|
||||
|
@ -11,53 +42,59 @@ services:
|
|||
expose:
|
||||
- "4001" # Database port (internal)
|
||||
volumes:
|
||||
- "./scripts/sql/init-test-databases.sql:/docker-entrypoint-initdb.d/init-test-databases.sql"
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
MYSQL_PASSWORD: photoprism
|
||||
MYSQL_DATABASE: photoprism
|
||||
|
||||
## MariaDB 10.3 Database Server
|
||||
## Docs: https://mariadb.com/docs/reference/cs10.3/
|
||||
mariadb-10-3:
|
||||
image: mariadb:10.3
|
||||
command: mysqld --port=4001 --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # Database port (internal)
|
||||
volumes:
|
||||
- "./scripts/sql/init-test-databases.sql:/docker-entrypoint-initdb.d/init-test-databases.sql"
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
MYSQL_PASSWORD: photoprism
|
||||
MYSQL_DATABASE: photoprism
|
||||
|
||||
## MariaDB 10.2 Database Server
|
||||
## Docs: https://mariadb.com/docs/reference/cs10.2/
|
||||
mariadb-10-2:
|
||||
image: mariadb:10.2
|
||||
command: mysqld --port=4001 --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # Database port (internal)
|
||||
volumes:
|
||||
- "./scripts/sql/init-test-databases.sql:/docker-entrypoint-initdb.d/init-test-databases.sql"
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
MYSQL_PASSWORD: photoprism
|
||||
MYSQL_DATABASE: photoprism
|
||||
|
||||
## MariaDB 10.1 Database Server
|
||||
mariadb-10-1:
|
||||
image: mariadb:10.1
|
||||
command: mysqld --port=4001 --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
expose:
|
||||
- "4001" # Database port (internal)
|
||||
volumes:
|
||||
- "./scripts/sql/init-test-databases.sql:/docker-entrypoint-initdb.d/init-test-databases.sql"
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
MYSQL_PASSWORD: photoprism
|
||||
MYSQL_DATABASE: photoprism
|
||||
|
||||
## Join shared "photoprism-develop" network
|
||||
networks:
|
||||
default:
|
||||
external:
|
||||
name: shared
|
||||
name: photoprism-develop
|
||||
|
|
|
@ -29,10 +29,14 @@ services:
|
|||
- "go-mod:/go/pkg/mod"
|
||||
shm_size: "2gb"
|
||||
environment:
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
||||
GOPROXY: "https://proxy.golang.org,direct"
|
||||
PHOTOPRISM_UID: ${UID:-1000} # User ID
|
||||
PHOTOPRISM_GID: ${GID:-1000} # Group ID
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # Admin password (min 4 characters)
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/" # Public server URL incl http:// or https:// and /path, :port is option
|
||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
||||
PHOTOPRISM_SITE_CAPTION: "Browse Your Life"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "AI-Powered Photos App. Tags and finds pictures without getting in your way!"
|
||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||
PHOTOPRISM_DEBUG: "true"
|
||||
PHOTOPRISM_READONLY: "false"
|
||||
|
@ -49,7 +53,6 @@ services:
|
|||
PHOTOPRISM_DATABASE_PASSWORD: "photoprism"
|
||||
PHOTOPRISM_TEST_DRIVER: "sqlite"
|
||||
PHOTOPRISM_TEST_DSN: ".test.db"
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # The initial admin password (min 4 characters)
|
||||
PHOTOPRISM_ASSETS_PATH: "/go/src/github.com/photoprism/photoprism/assets"
|
||||
PHOTOPRISM_STORAGE_PATH: "/go/src/github.com/photoprism/photoprism/storage"
|
||||
PHOTOPRISM_ORIGINALS_PATH: "/go/src/github.com/photoprism/photoprism/storage/originals"
|
||||
|
@ -85,11 +88,18 @@ services:
|
|||
|
||||
## Dummy WebDAV Server
|
||||
dummy-webdav:
|
||||
image: photoprism/dummy-webdav:20211109
|
||||
image: photoprism/dummy-webdav:20211218
|
||||
environment:
|
||||
WEBDAV_USERNAME: admin
|
||||
WEBDAV_PASSWORD: photoprism
|
||||
|
||||
## Dummy OpenID Connect Server
|
||||
dummy-oidc:
|
||||
image: photoprism/dummy-oidc:20211218
|
||||
# Expose port 9998 on host
|
||||
# ports:
|
||||
# - "9998:9998"
|
||||
|
||||
volumes:
|
||||
go-mod:
|
||||
driver: local
|
||||
|
|
|
@ -7,7 +7,19 @@ version: '3.5'
|
|||
## - Keycloak OpenID Connect Provider
|
||||
## - and Dummy Services
|
||||
services:
|
||||
## App Server
|
||||
## Traefik v2.5.5 HTTPS Reverse Proxy
|
||||
## Includes test certificates for https://*.traefik.net/
|
||||
## Docs: https://doc.traefik.io/traefik/
|
||||
traefik:
|
||||
image: photoprism/traefik:20211218
|
||||
ports:
|
||||
# - "80:80" # HTTP (redirects to HTTPS)
|
||||
- "443:443" # HTTPS (required)
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock" # Host names are configured with Docker labels
|
||||
|
||||
## App Build Environment
|
||||
## Docs: https://docs.photoprism.org/developer-guide/
|
||||
photoprism:
|
||||
build: .
|
||||
image: photoprism/photoprism:develop
|
||||
|
@ -18,18 +30,33 @@ services:
|
|||
- seccomp:unconfined
|
||||
- apparmor:unconfined
|
||||
ports:
|
||||
- "2342:2342" # Default HTTP port (host:container)
|
||||
- "2343:2343" # Acceptance Test HTTP port (host:container)
|
||||
- "2342:2342" # Default HTTP port (host:container)
|
||||
- "2343:2343" # Acceptance Test HTTP port (host:container)
|
||||
- "40000:40000" # Go Debugger (host:container)
|
||||
shm_size: "2gb"
|
||||
links:
|
||||
- "traefik:keycloak.traefik.net"
|
||||
- "traefik:photoprism.traefik.net"
|
||||
- "traefik:dummy-webdav.traefik.net"
|
||||
- "traefik:dummy-oidc.traefik.net"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.photoprism.loadbalancer.server.port=2342"
|
||||
- "traefik.http.routers.photoprism.entrypoints=websecure"
|
||||
- "traefik.http.routers.photoprism.rule=Host(`photoprism.traefik.net`)"
|
||||
- "traefik.http.routers.photoprism.tls.domains[0].main=traefik.net"
|
||||
- "traefik.http.routers.photoprism.tls.domains[0].sans=*.traefik.net"
|
||||
- "traefik.http.routers.photoprism.tls=true"
|
||||
environment:
|
||||
GOPROXY: "https://proxy.golang.org,direct"
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # The initial admin password (min 4 characters)
|
||||
PHOTOPRISM_UID: ${UID:-1000}
|
||||
PHOTOPRISM_GID: ${GID:-1000}
|
||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
||||
PHOTOPRISM_UID: ${UID:-1000} # User ID
|
||||
PHOTOPRISM_GID: ${GID:-1000} # Group ID
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # Admin password (min 4 characters)
|
||||
## External development server URL incl http:// or https:// and /path, :port is optional
|
||||
PHOTOPRISM_SITE_URL: "https://photoprism.traefik.net/"
|
||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
||||
PHOTOPRISM_SITE_CAPTION: "Browse Your Life"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "AI-powered app for browsing, organizing & sharing your photo collection."
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "AI-Powered Photos App. Tags and finds pictures without getting in your way!"
|
||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||
PHOTOPRISM_DEBUG: "true"
|
||||
PHOTOPRISM_READONLY: "false"
|
||||
|
@ -69,6 +96,10 @@ services:
|
|||
PHOTOPRISM_JPEG_SIZE: 7680 # Size limit for converted image files in pixels (720-30000)
|
||||
PHOTOPRISM_JPEG_QUALITY: 92 # Set to 95 for high-quality thumbnails (25-100)
|
||||
TF_CPP_MIN_LOG_LEVEL: 0 # Show TensorFlow log messages for development
|
||||
## OpenID Connect Provider (pre-configured for local Keycloak test server):
|
||||
PHOTOPRISM_OIDC_ISSUER_URL: "https://keycloak.traefik.net/auth/realms/master"
|
||||
PHOTOPRISM_OIDC_CLIENT_ID: "photoprism-develop"
|
||||
PHOTOPRISM_OIDC_CLIENT_SECRET: "9d8351a0-ca01-4556-9c37-85eb634869b9"
|
||||
## Enable TensorFlow AVX2 support for modern Intel CPUs (requires starting the container as root):
|
||||
# PHOTOPRISM_INIT: "tensorflow-amd64-avx2"
|
||||
## Hardware video transcoding config (optional):
|
||||
|
@ -98,7 +129,7 @@ services:
|
|||
ports:
|
||||
- "4001:4001" # Database port (host:container)
|
||||
volumes:
|
||||
- "./scripts/sql/init-test-databases.sql:/docker-entrypoint-initdb.d/init-test-databases.sql"
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
|
@ -113,25 +144,73 @@ services:
|
|||
expose:
|
||||
- "4001" # Database port (internal)
|
||||
volumes:
|
||||
- "./scripts/sql/init-test-databases.sql:/docker-entrypoint-initdb.d/init-test-databases.sql"
|
||||
- "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
MYSQL_PASSWORD: photoprism
|
||||
MYSQL_DATABASE: photoprism
|
||||
|
||||
## Keycloak OpenID Connect Provider
|
||||
## Test User: user / photoprism
|
||||
## Test Admin: admin / photoprism
|
||||
## Docs: https://www.keycloak.org/getting-started/getting-started-docker
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:16.0.0
|
||||
links:
|
||||
- "traefik:photoprism.traefik.net"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.keycloak.loadbalancer.server.port=8080"
|
||||
- "traefik.http.routers.keycloak.entrypoints=websecure"
|
||||
- "traefik.http.routers.keycloak.rule=Host(`keycloak.traefik.net`)"
|
||||
- "traefik.http.routers.keycloak.tls.domains[0].main=traefik.net"
|
||||
- "traefik.http.routers.keycloak.tls.domains[0].sans=*.traefik.net"
|
||||
- "traefik.http.routers.keycloak.tls=true"
|
||||
environment:
|
||||
KEYCLOAK_USER: "admin"
|
||||
KEYCLOAK_PASSWORD: "photoprism"
|
||||
KEYCLOAK_FRONTEND_URL: "https://keycloak.traefik.net/auth"
|
||||
DB_VENDOR: "mariadb"
|
||||
DB_PORT: 4001
|
||||
DB_DATABASE: "keycloak"
|
||||
DB_USER: "keycloak"
|
||||
DB_PASSWORD: "keycloak"
|
||||
|
||||
## Dummy WebDAV Server
|
||||
dummy-webdav:
|
||||
image: photoprism/dummy-webdav:20211109
|
||||
image: photoprism/dummy-webdav:20211218
|
||||
environment:
|
||||
WEBDAV_USERNAME: admin
|
||||
WEBDAV_PASSWORD: photoprism
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.dummy-webdav.loadbalancer.server.port=80"
|
||||
- "traefik.http.routers.dummy-webdav.entrypoints=websecure"
|
||||
- "traefik.http.routers.dummy-webdav.rule=Host(`dummy-webdav.traefik.net`)"
|
||||
- "traefik.http.routers.dummy-webdav.tls.domains[0].main=traefik.net"
|
||||
- "traefik.http.routers.dummy-webdav.tls.domains[0].sans=*.traefik.net"
|
||||
- "traefik.http.routers.dummy-webdav.tls=true"
|
||||
|
||||
## Dummy OpenID Connect Server
|
||||
dummy-oidc:
|
||||
image: photoprism/dummy-oidc:20211218
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.dummy-oidc.loadbalancer.server.port=9998"
|
||||
- "traefik.http.routers.dummy-oidc.entrypoints=websecure"
|
||||
- "traefik.http.routers.dummy-oidc.rule=Host(`dummy-oidc.traefik.net`)"
|
||||
- "traefik.http.routers.dummy-oidc.tls.domains[0].main=traefik.net"
|
||||
- "traefik.http.routers.dummy-oidc.tls.domains[0].sans=*.traefik.net"
|
||||
- "traefik.http.routers.dummy-oidc.tls=true"
|
||||
|
||||
## Create named volume for Go module cache
|
||||
volumes:
|
||||
go-mod:
|
||||
driver: local
|
||||
|
||||
## Create shared "photoprism-develop" network for connecting with services in other docker-compose.yml files
|
||||
networks:
|
||||
default:
|
||||
name: shared
|
||||
name: photoprism-develop
|
||||
driver: bridge
|
||||
|
|
|
@ -101,7 +101,7 @@ RUN rm -rf /tmp/* && mkdir -p /tmp/photoprism && \
|
|||
# Copy additional scripts to image
|
||||
COPY --chown=root:root /docker/scripts/heif-convert.sh /usr/local/bin/heif-convert
|
||||
COPY --chown=root:root /docker/scripts/Makefile /root/Makefile
|
||||
COPY --chown=root:root /docker/development/entrypoint.sh /entrypoint.sh
|
||||
COPY --chown=root:root /docker/develop/entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Install Go tools
|
||||
RUN /usr/local/go/bin/go install github.com/tianon/gosu@latest && \
|
||||
|
@ -124,8 +124,8 @@ RUN useradd -m -U -u 1000 -d /photoprism photoprism && chmod a+rwx /photoprism &
|
|||
find /go -type d -print0 | xargs -0 chmod 777
|
||||
|
||||
# Copy mysql client config for development
|
||||
COPY --chown=root:root /docker/development/.my.cnf /root/.my.cnf
|
||||
COPY --chown=photoprism:photoprism /docker/development/.my.cnf /photoprism/.my.cnf
|
||||
COPY --chown=root:root /docker/develop/.my.cnf /root/.my.cnf
|
||||
COPY --chown=photoprism:photoprism /docker/develop/.my.cnf /photoprism/.my.cnf
|
||||
RUN chmod 644 /root/.my.cnf /photoprism/.my.cnf
|
||||
|
||||
# Set up project directory
|
|
@ -93,7 +93,7 @@ RUN rm -rf /tmp/* && mkdir -p /tmp/photoprism && \
|
|||
# Copy additional scripts to image
|
||||
COPY --chown=root:root /docker/scripts/heif-convert.sh /usr/local/bin/heif-convert
|
||||
COPY --chown=root:root /docker/scripts/Makefile /root/Makefile
|
||||
COPY --chown=root:root /docker/development/entrypoint.sh /entrypoint.sh
|
||||
COPY --chown=root:root /docker/develop/entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Install Go tools
|
||||
RUN /usr/local/go/bin/go install github.com/tianon/gosu@latest && \
|
71
docker/develop/keycloak/client.json
Normal file
71
docker/develop/keycloak/client.json
Normal file
|
@ -0,0 +1,71 @@
|
|||
{
|
||||
"clientId": "photoprism-develop",
|
||||
"secret": "9d8351a0-ca01-4556-9c37-85eb634869b9",
|
||||
"name": "PhotoPrism",
|
||||
"rootUrl": "https://photoprism.traefik.net/",
|
||||
"adminUrl": "https://photoprism.traefik.net/",
|
||||
"surrogateAuthRequired": false,
|
||||
"enabled": true,
|
||||
"alwaysDisplayInConsole": false,
|
||||
"clientAuthenticatorType": "client-secret",
|
||||
"redirectUris": [
|
||||
"https://photoprism.traefik.net/api/v1/auth/callback"
|
||||
],
|
||||
"webOrigins": [
|
||||
"https://photoprism.traefik.net"
|
||||
],
|
||||
"notBefore": 0,
|
||||
"bearerOnly": false,
|
||||
"consentRequired": false,
|
||||
"standardFlowEnabled": true,
|
||||
"implicitFlowEnabled": false,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"serviceAccountsEnabled": false,
|
||||
"publicClient": false,
|
||||
"frontchannelLogout": false,
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"id.token.as.detached.signature": "false",
|
||||
"saml.assertion.signature": "false",
|
||||
"saml.force.post.binding": "false",
|
||||
"saml.multivalued.roles": "false",
|
||||
"saml.encrypt": "false",
|
||||
"oauth2.device.authorization.grant.enabled": "false",
|
||||
"backchannel.logout.revoke.offline.tokens": "false",
|
||||
"saml.server.signature": "false",
|
||||
"saml.server.signature.keyinfo.ext": "false",
|
||||
"use.refresh.tokens": "true",
|
||||
"exclude.session.state.from.auth.response": "false",
|
||||
"oidc.ciba.grant.enabled": "false",
|
||||
"saml.artifact.binding": "false",
|
||||
"backchannel.logout.session.required": "true",
|
||||
"client_credentials.use_refresh_token": "false",
|
||||
"saml_force_name_id_format": "false",
|
||||
"require.pushed.authorization.requests": "false",
|
||||
"saml.client.signature": "false",
|
||||
"tls.client.certificate.bound.access.tokens": "false",
|
||||
"saml.authnstatement": "false",
|
||||
"display.on.consent.screen": "false",
|
||||
"saml.onetimeuse.condition": "false"
|
||||
},
|
||||
"authenticationFlowBindingOverrides": {},
|
||||
"fullScopeAllowed": true,
|
||||
"nodeReRegistrationTimeout": -1,
|
||||
"defaultClientScopes": [
|
||||
"web-origins",
|
||||
"profile",
|
||||
"roles",
|
||||
"email"
|
||||
],
|
||||
"optionalClientScopes": [
|
||||
"address",
|
||||
"phone",
|
||||
"offline_access",
|
||||
"microprofile-jwt"
|
||||
],
|
||||
"access": {
|
||||
"view": true,
|
||||
"configure": true,
|
||||
"manage": true
|
||||
}
|
||||
}
|
75
docker/develop/keycloak/clients.json
Normal file
75
docker/develop/keycloak/clients.json
Normal file
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"clients": [
|
||||
{
|
||||
"clientId": "photoprism-develop",
|
||||
"secret": "9d8351a0-ca01-4556-9c37-85eb634869b9",
|
||||
"name": "PhotoPrism",
|
||||
"rootUrl": "https://photoprism.traefik.net/",
|
||||
"adminUrl": "https://photoprism.traefik.net/",
|
||||
"surrogateAuthRequired": false,
|
||||
"enabled": true,
|
||||
"alwaysDisplayInConsole": false,
|
||||
"clientAuthenticatorType": "client-secret",
|
||||
"redirectUris": [
|
||||
"https://photoprism.traefik.net/api/v1/auth/callback"
|
||||
],
|
||||
"webOrigins": [
|
||||
"https://photoprism.traefik.net"
|
||||
],
|
||||
"notBefore": 0,
|
||||
"bearerOnly": false,
|
||||
"consentRequired": false,
|
||||
"standardFlowEnabled": true,
|
||||
"implicitFlowEnabled": false,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"serviceAccountsEnabled": false,
|
||||
"publicClient": false,
|
||||
"frontchannelLogout": false,
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"id.token.as.detached.signature": "false",
|
||||
"saml.assertion.signature": "false",
|
||||
"saml.force.post.binding": "false",
|
||||
"saml.multivalued.roles": "false",
|
||||
"saml.encrypt": "false",
|
||||
"oauth2.device.authorization.grant.enabled": "false",
|
||||
"backchannel.logout.revoke.offline.tokens": "false",
|
||||
"saml.server.signature": "false",
|
||||
"saml.server.signature.keyinfo.ext": "false",
|
||||
"use.refresh.tokens": "true",
|
||||
"exclude.session.state.from.auth.response": "false",
|
||||
"oidc.ciba.grant.enabled": "false",
|
||||
"saml.artifact.binding": "false",
|
||||
"backchannel.logout.session.required": "true",
|
||||
"client_credentials.use_refresh_token": "false",
|
||||
"saml_force_name_id_format": "false",
|
||||
"require.pushed.authorization.requests": "false",
|
||||
"saml.client.signature": "false",
|
||||
"tls.client.certificate.bound.access.tokens": "false",
|
||||
"saml.authnstatement": "false",
|
||||
"display.on.consent.screen": "false",
|
||||
"saml.onetimeuse.condition": "false"
|
||||
},
|
||||
"authenticationFlowBindingOverrides": {},
|
||||
"fullScopeAllowed": true,
|
||||
"nodeReRegistrationTimeout": -1,
|
||||
"defaultClientScopes": [
|
||||
"web-origins",
|
||||
"profile",
|
||||
"roles",
|
||||
"email"
|
||||
],
|
||||
"optionalClientScopes": [
|
||||
"address",
|
||||
"phone",
|
||||
"offline_access",
|
||||
"microprofile-jwt"
|
||||
],
|
||||
"access": {
|
||||
"view": true,
|
||||
"configure": true,
|
||||
"manage": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
FROM golang:1
|
||||
##################################################### BUILD STAGE ######################################################
|
||||
FROM golang:alpine AS build
|
||||
|
||||
# Move to working directory /app
|
||||
WORKDIR /app
|
||||
LABEL maintainer="Michael Mayer <hello@photoprism.app>"
|
||||
|
||||
ARG GOPROXY
|
||||
ARG GODEBUG
|
||||
|
||||
# Move to working directory /app
|
||||
WORKDIR /app
|
||||
|
||||
# Copy files and download dependency using go mod
|
||||
COPY /docker/dummy/oidc/app/. .
|
||||
RUN go mod download
|
||||
|
|
|
@ -3,7 +3,7 @@ module caos-test-op
|
|||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/caos/oidc v0.15.10
|
||||
github.com/caos/oidc v1.0.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
)
|
||||
|
|
|
@ -37,6 +37,8 @@ github.com/caos/logging v0.0.2 h1:ebg5C/HN0ludYR+WkvnFjwSExF4wvyiWPyWGcKMYsoo=
|
|||
github.com/caos/logging v0.0.2/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0=
|
||||
github.com/caos/oidc v0.15.10 h1:dSzkIvsZR2PSZgvBFFkLJt8A/MujsyLac1yNvBShXuw=
|
||||
github.com/caos/oidc v0.15.10/go.mod h1:4l0PPwdc6BbrdCFhNrRTUddsG292uHGa7gE2DSEIqoU=
|
||||
github.com/caos/oidc v1.0.0 h1:3sHkYf8zsuARR89qO9CyvfYhHGdliWPcou4glzGMXmQ=
|
||||
github.com/caos/oidc v1.0.0/go.mod h1:4l0PPwdc6BbrdCFhNrRTUddsG292uHGa7gE2DSEIqoU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
|
|
|
@ -37,10 +37,16 @@ func NewAuthStorage() op.Storage {
|
|||
type AuthRequest struct {
|
||||
ID string
|
||||
ResponseType oidc.ResponseType
|
||||
ResponseMode oidc.ResponseMode
|
||||
RedirectURI string
|
||||
Nonce string
|
||||
ClientID string
|
||||
CodeChallenge *oidc.CodeChallenge
|
||||
State string
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetResponseMode() oidc.ResponseMode {
|
||||
return a.ResponseMode
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetACR() string {
|
||||
|
@ -102,7 +108,7 @@ func (a *AuthRequest) GetScopes() []string {
|
|||
func (a *AuthRequest) SetCurrentScopes(scopes []string) {}
|
||||
|
||||
func (a *AuthRequest) GetState() string {
|
||||
return state
|
||||
return a.State
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetSubject() string {
|
||||
|
@ -114,10 +120,9 @@ func (a *AuthRequest) Done() bool {
|
|||
}
|
||||
|
||||
var (
|
||||
a = &AuthRequest{}
|
||||
t bool
|
||||
c string
|
||||
state string
|
||||
a = &AuthRequest{}
|
||||
t bool
|
||||
c string
|
||||
)
|
||||
|
||||
func (s *AuthStorage) Health(ctx context.Context) error {
|
||||
|
@ -126,7 +131,6 @@ func (s *AuthStorage) Health(ctx context.Context) error {
|
|||
|
||||
func (s *AuthStorage) CreateAuthRequest(_ context.Context, authReq *oidc.AuthRequest, userId string) (op.AuthRequest, error) {
|
||||
fmt.Println("Userid: ", userId)
|
||||
fmt.Println("CreateAuthRequest ID: ", authReq.ID)
|
||||
fmt.Println("CreateAuthRequest CodeChallenge: ", authReq.CodeChallenge)
|
||||
fmt.Println("CreateAuthRequest CodeChallengeMethod: ", authReq.CodeChallengeMethod)
|
||||
fmt.Println("CreateAuthRequest State: ", authReq.State)
|
||||
|
@ -137,14 +141,13 @@ func (s *AuthStorage) CreateAuthRequest(_ context.Context, authReq *oidc.AuthReq
|
|||
fmt.Println("CreateAuthRequest Display: ", authReq.Display)
|
||||
fmt.Println("CreateAuthRequest LoginHint: ", authReq.LoginHint)
|
||||
fmt.Println("CreateAuthRequest IDTokenHint: ", authReq.IDTokenHint)
|
||||
a = &AuthRequest{ID: "authReqUserAgentId", ClientID: authReq.ClientID, ResponseType: authReq.ResponseType, Nonce: authReq.Nonce, RedirectURI: authReq.RedirectURI}
|
||||
a = &AuthRequest{ID: "authReqUserAgentId", ClientID: authReq.ClientID, ResponseType: authReq.ResponseType, Nonce: authReq.Nonce, RedirectURI: authReq.RedirectURI, State: authReq.State}
|
||||
if authReq.CodeChallenge != "" {
|
||||
a.CodeChallenge = &oidc.CodeChallenge{
|
||||
Challenge: authReq.CodeChallenge,
|
||||
Method: authReq.CodeChallengeMethod,
|
||||
}
|
||||
}
|
||||
state = authReq.State
|
||||
t = false
|
||||
return a, nil
|
||||
}
|
||||
|
@ -294,6 +297,10 @@ func (s *AuthStorage) ValidateJWTProfileScopes(ctx context.Context, userID strin
|
|||
return scope, nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) RevokeToken(ctx context.Context, token string, userID string, clientID string) *oidc.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ConfClient struct {
|
||||
applicationType op.ApplicationType
|
||||
authMethod oidc.AuthMethod
|
||||
|
|
|
@ -6,47 +6,64 @@ import (
|
|||
"crypto/sha256"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"os"
|
||||
|
||||
"caos-test-op/mock"
|
||||
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
var port, issuer string
|
||||
|
||||
if port = os.Getenv("DUMMY_OIDC_PORT"); port == "" {
|
||||
port = "9998"
|
||||
}
|
||||
if issuer = os.Getenv("DUMMY_OIDC_ISSUER"); issuer == "" {
|
||||
issuer = "http://dummy-oidc:9998"
|
||||
}
|
||||
|
||||
b := make([]byte, 32)
|
||||
rand.Read(b)
|
||||
cryptoKey := sha256.Sum256(b)
|
||||
|
||||
port := "9998"
|
||||
config := &op.Config{
|
||||
Issuer: "http://dummy-oidc:9998",
|
||||
CryptoKey: sha256.Sum256(b),
|
||||
Issuer: issuer,
|
||||
CryptoKey: cryptoKey,
|
||||
CodeMethodS256: true,
|
||||
}
|
||||
storage := mock.NewAuthStorage()
|
||||
|
||||
handler, err := op.NewOpenIDProvider(ctx, config, storage)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
router := handler.HttpHandler().(*mux.Router)
|
||||
router.Methods("GET").Path("/login").HandlerFunc(HandleLogin)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: ":" + port,
|
||||
Handler: router,
|
||||
}
|
||||
|
||||
err = server.ListenAndServe()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func HandleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
requestId := r.Form.Get("id")
|
||||
|
||||
// simulate user login and retrieve a token that indicates a successfully logged-in user
|
||||
usertoken := requestId + ":usertoken"
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
FROM golang:1
|
||||
##################################################### BUILD STAGE ######################################################
|
||||
FROM golang:alpine AS build
|
||||
|
||||
LABEL maintainer="Michael Mayer <hello@photoprism.app>"
|
||||
|
||||
|
@ -19,4 +20,4 @@ EXPOSE 80
|
|||
COPY /docker/dummy/webdav/config.yml /webdav/config.yml
|
||||
COPY /docker/dummy/webdav/files /webdav/files
|
||||
|
||||
CMD webdav -c /webdav/config.yml
|
||||
CMD ["/go/bin/webdav", "-c", "/webdav/config.yml"]
|
||||
|
|
|
@ -6,8 +6,8 @@ SOFTWARE INCLUDED
|
|||
|
||||
PhotoPrism latest, AGPL 3
|
||||
Docker CE 20.10, Apache 2
|
||||
Traefik 2.4, MIT
|
||||
MariaDB 10.5, GPL 2
|
||||
Traefik 2.5, MIT
|
||||
MariaDB 10.6, GPL 2
|
||||
Ofelia 0.3.4, MIT
|
||||
Watchtower 1.3, Apache 2
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ SOFTWARE INCLUDED
|
|||
|
||||
- [PhotoPrism latest](https://docs.photoprism.app/release-notes/), AGPL 3
|
||||
- [Docker CE 20.10](https://docs.docker.com/engine/release-notes/), Apache 2
|
||||
- [Traefik 2.4](https://github.com/traefik/traefik/releases), MIT
|
||||
- [MariaDB 10.5](https://mariadb.com/kb/en/release-notes/), GPL 2
|
||||
- [Traefik 2.5](https://github.com/traefik/traefik/releases), MIT
|
||||
- [MariaDB 10.6](https://mariadb.com/kb/en/release-notes/), GPL 2
|
||||
- [Ofelia 0.3.4](https://github.com/mcuadros/ofelia/releases), MIT
|
||||
- [Watchtower 1.3](https://github.com/containrrr/watchtower/releases), Apache 2
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ services:
|
|||
## see https://docs.photoprism.app/getting-started/proxies/traefik/
|
||||
traefik:
|
||||
restart: always
|
||||
image: traefik:v2.4
|
||||
image: traefik:v2.5
|
||||
container_name: traefik
|
||||
ports:
|
||||
- "80:80"
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
##################################################### BUILD STAGE ######################################################
|
||||
FROM golang:alpine AS build
|
||||
|
||||
ARG GOPROXY
|
||||
ARG GODEBUG
|
||||
|
||||
RUN apk add --no-cache -U make git mercurial subversion
|
||||
|
||||
RUN git clone https://github.com/goproxyio/goproxy.git /src/goproxy && \
|
||||
cd /src/goproxy && \
|
||||
export CGO_ENABLED=0 && \
|
||||
make
|
||||
|
||||
################################################## PRODUCTION STAGE ####################################################
|
||||
FROM golang:alpine
|
||||
|
||||
ENV TINI_VERSION v0.19.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static-amd64 /usr/bin/tini
|
||||
RUN chmod +x /usr/bin/tini
|
||||
|
||||
RUN apk add --no-cache -U git mercurial subversion
|
||||
|
||||
COPY --from=build /src/goproxy/bin/goproxy /goproxy
|
||||
VOLUME "/go"
|
||||
|
||||
EXPOSE 8888
|
||||
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
CMD ["/goproxy", "-listen", "0.0.0.0:8888"]
|
|
@ -1,5 +1,5 @@
|
|||
##################################################### BUILD STAGE ######################################################
|
||||
FROM photoprism/development:20211210 as build
|
||||
FROM photoprism/develop:20211218 as build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG TARGETPLATFORM
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
##################################################### BUILD STAGE ######################################################
|
||||
FROM photoprism/development:armv7 as build
|
||||
FROM photoprism/develop:armv7 as build
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG TARGETPLATFORM
|
||||
|
|
|
@ -10,10 +10,10 @@ if [[ -z $1 ]]; then
|
|||
else
|
||||
set -eux;
|
||||
if [[ $1 == "amd64" ]]; then
|
||||
URL="https://go.dev/dl/go${GOLANG_VERSION}.linux-$1.tar.gz"
|
||||
URL="https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz"
|
||||
CHECKSUM="bd78114b0d441b029c8fe0341f4910370925a4d270a6a590668840675b0c653e *go.tgz"
|
||||
elif [[ $1 == "arm64" ]]; then
|
||||
URL="https://go.dev/dl/go{GOLANG_VERSION}.linux-$1.tar.gz"
|
||||
URL="https://go.dev/dl/go${GOLANG_VERSION}.linux-arm64.tar.gz"
|
||||
CHECKSUM="6f95ce3da40d9ce1355e48f31f4eb6508382415ca4d7413b1e7a3314e6430e7e *go.tgz"
|
||||
elif [[ $1 == "arm" ]]; then
|
||||
URL="https://go.dev/dl/go${GOLANG_VERSION}.linux-armv6l.tar.gz"
|
||||
|
|
485
frontend/package-lock.json
generated
485
frontend/package-lock.json
generated
|
@ -1885,9 +1885,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/eslint-scope": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz",
|
||||
"integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==",
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.2.tgz",
|
||||
"integrity": "sha512-TzgYCWoPiTeRg6RQYgtuW7iODtVoKu3RVL72k3WohqhjfaOLK5Mg2T4Tg1o2bSfu0vPkoI48wdQFv5b/Xe04wQ==",
|
||||
"dependencies": {
|
||||
"@types/eslint": "*",
|
||||
"@types/estree": "*"
|
||||
|
@ -3051,9 +3051,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001287",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001287.tgz",
|
||||
"integrity": "sha512-4udbs9bc0hfNrcje++AxBuc6PfLNHwh3PO9kbwnfCQWyqtlzg3py0YgFu8jyRTTo85VAz4U+VLxSlID09vNtWA==",
|
||||
"version": "1.0.30001289",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001289.tgz",
|
||||
"integrity": "sha512-hV6x4IfrYViN8cJbGFVbjD7KCrhS/O7wfDgvevYRanJ/IN+hhxpTcXXqaxy3CzPNFe5rlqdimdEB/k7H0YzxHg==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
|
@ -3233,9 +3233,9 @@
|
|||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"node_modules/colord": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz",
|
||||
"integrity": "sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw=="
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz",
|
||||
"integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ=="
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.16",
|
||||
|
@ -3400,16 +3400,35 @@
|
|||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
||||
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.1.2"
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition/node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/content-security-policy-builder": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz",
|
||||
|
@ -4235,9 +4254,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.20",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.20.tgz",
|
||||
"integrity": "sha512-N7ZVNrdzX8NE90OXEFBMsBf3fp8P/vVDUER3WCUZjzC7OkNTXHVoF6W9qVhq8+dA8tGnbDajzUpj2ISNVVyj+Q=="
|
||||
"version": "1.4.24",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz",
|
||||
"integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
|
@ -5428,16 +5447,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz",
|
||||
"integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.7",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.19.0",
|
||||
"content-disposition": "0.5.3",
|
||||
"body-parser": "1.19.1",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
"cookie": "0.4.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
|
@ -5451,13 +5470,13 @@
|
|||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~2.0.5",
|
||||
"qs": "6.7.0",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.9.6",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.1.2",
|
||||
"send": "0.17.1",
|
||||
"serve-static": "1.14.1",
|
||||
"setprototypeof": "1.1.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.17.2",
|
||||
"serve-static": "1.14.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "~1.5.0",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
|
@ -5533,42 +5552,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"node_modules/express/node_modules/body-parser": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.0",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"http-errors": "1.7.2",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "~2.3.0",
|
||||
"qs": "6.7.0",
|
||||
"raw-body": "2.4.0",
|
||||
"type-is": "~1.6.17"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/bytes": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/cookie": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
|
@ -5577,26 +5560,6 @@
|
|||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/http-errors": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
||||
"dependencies": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.3",
|
||||
"setprototypeof": "1.1.1",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
@ -5607,40 +5570,24 @@
|
|||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||
},
|
||||
"node_modules/express/node_modules/qs": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/raw-body": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
||||
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.0",
|
||||
"http-errors": "1.7.2",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/setprototypeof": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||
},
|
||||
"node_modules/express/node_modules/toidentifier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
"node_modules/express/node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/ext": {
|
||||
"version": "1.6.0",
|
||||
|
@ -5919,19 +5866,19 @@
|
|||
"integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw=="
|
||||
},
|
||||
"node_modules/flow-parser": {
|
||||
"version": "0.167.1",
|
||||
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.167.1.tgz",
|
||||
"integrity": "sha512-KQGKp9VvVXRUI+xznTOVthj1aroBHGOe3/1t9h1KaSWEP6GzMI59L8KtKJPsFYgTFTvQpRmHK8lTR4by9VAJeA==",
|
||||
"version": "0.168.0",
|
||||
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.168.0.tgz",
|
||||
"integrity": "sha512-YMlc+6vvyDPqWKOpzmyifJXBbwlNdqznuy8YBHxX1/90F8d+NnhsxMe1u/ok5LNvNJVJ2TVMkWudu0BUKOSawA==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/flow-remove-types": {
|
||||
"version": "2.167.1",
|
||||
"resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.167.1.tgz",
|
||||
"integrity": "sha512-R9TUXoC+6FPEE/v775DljM3Ro0xOiPwa0wUy9t8sQ9goNMqlePFR6w8YHAPENg4EXZIU4x/Jfyi6ocYIsJcSWw==",
|
||||
"version": "2.168.0",
|
||||
"resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.168.0.tgz",
|
||||
"integrity": "sha512-WNXmqPtxgm94OLzvVLokX/MMDrbfs5snI4QDc/up/mx746+aLuM9L+dFUqGg4u4GSwDa5CR69aqT2Zyn9t3AZA==",
|
||||
"dependencies": {
|
||||
"flow-parser": "^0.167.1",
|
||||
"flow-parser": "^0.168.0",
|
||||
"pirates": "^3.0.2",
|
||||
"vlq": "^0.2.1"
|
||||
},
|
||||
|
@ -6412,9 +6359,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/hls.js": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.1.1.tgz",
|
||||
"integrity": "sha512-2VEVzO/gr5LOD6K/DEmBkKEary6hr4YZ/SLb0PV81jOAM/Tl9DviL0sFMoUHDq05/j4OZxIj691Zo0p40u3Gtw=="
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.1.2.tgz",
|
||||
"integrity": "sha512-ujditC4vvBmZd00RRNfNPLgFVlqEeUX4sAFv5lGhBHuql8iAZodOdlZTD3em/1zo7vyjQp12up/lCVqQk8dvxA=="
|
||||
},
|
||||
"node_modules/hpkp": {
|
||||
"version": "2.0.0",
|
||||
|
@ -8362,9 +8309,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz",
|
||||
"integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==",
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
|
||||
"integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
|
@ -10579,9 +10526,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
|
||||
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
|
||||
"version": "0.17.2",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
|
||||
"integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
|
@ -10590,9 +10537,9 @@
|
|||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "~1.7.2",
|
||||
"http-errors": "1.8.1",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.1",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "~2.3.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "~1.5.0"
|
||||
|
@ -10614,21 +10561,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"node_modules/send/node_modules/http-errors": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
|
||||
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
|
||||
"dependencies": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.1.1",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
|
@ -10641,22 +10573,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||
},
|
||||
"node_modules/send/node_modules/setprototypeof": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||
},
|
||||
"node_modules/send/node_modules/toidentifier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.0",
|
||||
|
@ -10746,14 +10665,14 @@
|
|||
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
|
||||
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
|
||||
"integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
|
||||
"dependencies": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.17.1"
|
||||
"send": "0.17.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
|
@ -11473,11 +11392,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin": {
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz",
|
||||
"integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz",
|
||||
"integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==",
|
||||
"dependencies": {
|
||||
"jest-worker": "^27.0.6",
|
||||
"jest-worker": "^27.4.1",
|
||||
"schema-utils": "^3.1.1",
|
||||
"serialize-javascript": "^6.0.0",
|
||||
"source-map": "^0.6.1",
|
||||
|
@ -14078,9 +13997,9 @@
|
|||
}
|
||||
},
|
||||
"@types/eslint-scope": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz",
|
||||
"integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==",
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.2.tgz",
|
||||
"integrity": "sha512-TzgYCWoPiTeRg6RQYgtuW7iODtVoKu3RVL72k3WohqhjfaOLK5Mg2T4Tg1o2bSfu0vPkoI48wdQFv5b/Xe04wQ==",
|
||||
"requires": {
|
||||
"@types/eslint": "*",
|
||||
"@types/estree": "*"
|
||||
|
@ -14995,9 +14914,9 @@
|
|||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001287",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001287.tgz",
|
||||
"integrity": "sha512-4udbs9bc0hfNrcje++AxBuc6PfLNHwh3PO9kbwnfCQWyqtlzg3py0YgFu8jyRTTo85VAz4U+VLxSlID09vNtWA=="
|
||||
"version": "1.0.30001289",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001289.tgz",
|
||||
"integrity": "sha512-hV6x4IfrYViN8cJbGFVbjD7KCrhS/O7wfDgvevYRanJ/IN+hhxpTcXXqaxy3CzPNFe5rlqdimdEB/k7H0YzxHg=="
|
||||
},
|
||||
"chai": {
|
||||
"version": "4.3.4",
|
||||
|
@ -15135,9 +15054,9 @@
|
|||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"colord": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz",
|
||||
"integrity": "sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw=="
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz",
|
||||
"integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ=="
|
||||
},
|
||||
"colorette": {
|
||||
"version": "2.0.16",
|
||||
|
@ -15282,11 +15201,18 @@
|
|||
}
|
||||
},
|
||||
"content-disposition": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
||||
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.2"
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"content-security-policy-builder": {
|
||||
|
@ -15873,9 +15799,9 @@
|
|||
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA=="
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.20",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.20.tgz",
|
||||
"integrity": "sha512-N7ZVNrdzX8NE90OXEFBMsBf3fp8P/vVDUER3WCUZjzC7OkNTXHVoF6W9qVhq8+dA8tGnbDajzUpj2ISNVVyj+Q=="
|
||||
"version": "1.4.24",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz",
|
||||
"integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
|
@ -16748,16 +16674,16 @@
|
|||
}
|
||||
},
|
||||
"express": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz",
|
||||
"integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==",
|
||||
"requires": {
|
||||
"accepts": "~1.3.7",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.19.0",
|
||||
"content-disposition": "0.5.3",
|
||||
"body-parser": "1.19.1",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
"cookie": "0.4.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
|
@ -16771,46 +16697,19 @@
|
|||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~2.0.5",
|
||||
"qs": "6.7.0",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.9.6",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.1.2",
|
||||
"send": "0.17.1",
|
||||
"serve-static": "1.14.1",
|
||||
"setprototypeof": "1.1.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.17.2",
|
||||
"serve-static": "1.14.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "~1.5.0",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
||||
"requires": {
|
||||
"bytes": "3.1.0",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
"http-errors": "1.7.2",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "~2.3.0",
|
||||
"qs": "6.7.0",
|
||||
"raw-body": "2.4.0",
|
||||
"type-is": "~1.6.17"
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
|
@ -16819,23 +16718,6 @@
|
|||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
||||
"requires": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.3",
|
||||
"setprototypeof": "1.1.1",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.0"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
@ -16846,31 +16728,10 @@
|
|||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
||||
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
|
||||
"requires": {
|
||||
"bytes": "3.1.0",
|
||||
"http-errors": "1.7.2",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
}
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||
},
|
||||
"toidentifier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -17142,16 +17003,16 @@
|
|||
"integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw=="
|
||||
},
|
||||
"flow-parser": {
|
||||
"version": "0.167.1",
|
||||
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.167.1.tgz",
|
||||
"integrity": "sha512-KQGKp9VvVXRUI+xznTOVthj1aroBHGOe3/1t9h1KaSWEP6GzMI59L8KtKJPsFYgTFTvQpRmHK8lTR4by9VAJeA=="
|
||||
"version": "0.168.0",
|
||||
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.168.0.tgz",
|
||||
"integrity": "sha512-YMlc+6vvyDPqWKOpzmyifJXBbwlNdqznuy8YBHxX1/90F8d+NnhsxMe1u/ok5LNvNJVJ2TVMkWudu0BUKOSawA=="
|
||||
},
|
||||
"flow-remove-types": {
|
||||
"version": "2.167.1",
|
||||
"resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.167.1.tgz",
|
||||
"integrity": "sha512-R9TUXoC+6FPEE/v775DljM3Ro0xOiPwa0wUy9t8sQ9goNMqlePFR6w8YHAPENg4EXZIU4x/Jfyi6ocYIsJcSWw==",
|
||||
"version": "2.168.0",
|
||||
"resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-2.168.0.tgz",
|
||||
"integrity": "sha512-WNXmqPtxgm94OLzvVLokX/MMDrbfs5snI4QDc/up/mx746+aLuM9L+dFUqGg4u4GSwDa5CR69aqT2Zyn9t3AZA==",
|
||||
"requires": {
|
||||
"flow-parser": "^0.167.1",
|
||||
"flow-parser": "^0.168.0",
|
||||
"pirates": "^3.0.2",
|
||||
"vlq": "^0.2.1"
|
||||
},
|
||||
|
@ -17482,9 +17343,9 @@
|
|||
"integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg=="
|
||||
},
|
||||
"hls.js": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.1.1.tgz",
|
||||
"integrity": "sha512-2VEVzO/gr5LOD6K/DEmBkKEary6hr4YZ/SLb0PV81jOAM/Tl9DviL0sFMoUHDq05/j4OZxIj691Zo0p40u3Gtw=="
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.1.2.tgz",
|
||||
"integrity": "sha512-ujditC4vvBmZd00RRNfNPLgFVlqEeUX4sAFv5lGhBHuql8iAZodOdlZTD3em/1zo7vyjQp12up/lCVqQk8dvxA=="
|
||||
},
|
||||
"hpkp": {
|
||||
"version": "2.0.0",
|
||||
|
@ -18912,9 +18773,9 @@
|
|||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||
},
|
||||
"object-inspect": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz",
|
||||
"integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA=="
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
|
||||
"integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
|
||||
},
|
||||
"object-keys": {
|
||||
"version": "1.1.1",
|
||||
|
@ -20405,9 +20266,9 @@
|
|||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"send": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
|
||||
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
|
||||
"version": "0.17.2",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
|
||||
"integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.2",
|
||||
|
@ -20416,9 +20277,9 @@
|
|||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "~1.7.2",
|
||||
"http-errors": "1.8.1",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.1",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "~2.3.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "~1.5.0"
|
||||
|
@ -20439,37 +20300,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
|
||||
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
|
||||
"requires": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.1.1",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.0"
|
||||
}
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||
},
|
||||
"toidentifier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20556,14 +20395,14 @@
|
|||
}
|
||||
},
|
||||
"serve-static": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
|
||||
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
|
||||
"integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
|
||||
"requires": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.17.1"
|
||||
"send": "0.17.2"
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
|
@ -21126,11 +20965,11 @@
|
|||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz",
|
||||
"integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz",
|
||||
"integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==",
|
||||
"requires": {
|
||||
"jest-worker": "^27.0.6",
|
||||
"jest-worker": "^27.4.1",
|
||||
"schema-utils": "^3.1.1",
|
||||
"serialize-javascript": "^6.0.0",
|
||||
"source-map": "^0.6.1",
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
"acceptance-firefox": "testcafe firefox:headless --skip-js-errors --quarantine-mode --selector-timeout 5000 -S -s tests/screenshots tests/acceptance",
|
||||
"acceptance-private": "testcafe chrome:headless --skip-js-errors --quarantine-mode --selector-timeout 5000 -S -s tests/screenshots tests/acceptance-private",
|
||||
"acceptance-private-firefox": "testcafe firefox:headless --skip-js-errors --quarantine-mode --selector-timeout 5000 -S -s tests/screenshots tests/acceptance-private",
|
||||
"acceptance-openid": "testcafe chrome:headless --skip-js-errors --quarantine-mode --selector-timeout 5000 -S -s tests/screenshots tests/acceptance-openid",
|
||||
"acceptance-openid-firefox": "testcafe firefox:headless --skip-js-errors --quarantine-mode --selector-timeout 5000 -S -s tests/screenshots tests/acceptance-openid",
|
||||
"acceptance-local": "testcafe chrome --selector-timeout 5000 -S -s tests/screenshots tests/acceptance",
|
||||
"gettext-extract": "gettext-extract --output src/locales/translations.pot $(find src -type f \\( -iname \\*.vue -o -iname \\*.js \\) -not -path src/common/vm.js)",
|
||||
"gettext-compile": "gettext-compile --output src/locales/translations.json src/locales/*.po"
|
||||
|
|
|
@ -57,6 +57,7 @@ import Hls from "hls.js";
|
|||
import "common/maptiler-lang";
|
||||
import { $gettext, Mount } from "common/vm";
|
||||
import * as offline from "@lcdp/offline-plugin/runtime";
|
||||
import { aclMixin } from "./common/acl";
|
||||
|
||||
// Initialize helpers
|
||||
const viewer = new Viewer();
|
||||
|
@ -100,6 +101,8 @@ Vue.prototype.$earlyAccess = () => {
|
|||
});
|
||||
};
|
||||
|
||||
Vue.mixin(aclMixin);
|
||||
|
||||
// Register Vuetify
|
||||
Vue.use(Vuetify, { rtl, icons, theme });
|
||||
|
||||
|
|
128
frontend/src/common/acl.js
Normal file
128
frontend/src/common/acl.js
Normal file
|
@ -0,0 +1,128 @@
|
|||
import { config } from "../session";
|
||||
|
||||
export const Constants = {
|
||||
roles: {
|
||||
RoleDefault: "*", // used if no role matches
|
||||
RoleAdmin: "admin",
|
||||
RolePartner: "partner",
|
||||
RoleFamily: "family",
|
||||
RoleSibling: "sibling",
|
||||
RoleParent: "parent",
|
||||
RoleGrandparent: "grandparent",
|
||||
RoleChild: "child",
|
||||
RoleFriend: "friend",
|
||||
RoleBestFriend: "best-friend",
|
||||
RoleClassmate: "classmate",
|
||||
RoleWorkmate: "workmate",
|
||||
RoleGuest: "guest",
|
||||
RoleMember: "member",
|
||||
},
|
||||
actions: {
|
||||
ActionDefault: "*", // allows a subject/role to execute all other actions
|
||||
ActionSearch: "search",
|
||||
ActionCreate: "create",
|
||||
ActionRead: "read",
|
||||
ActionUpdate: "update",
|
||||
ActionUpdateSelf: "update-self",
|
||||
ActionDelete: "delete",
|
||||
ActionArchive: "archive",
|
||||
ActionPrivate: "private",
|
||||
ActionUpload: "upload",
|
||||
ActionDownload: "download",
|
||||
ActionShare: "share",
|
||||
ActionLike: "like",
|
||||
ActionComment: "comment",
|
||||
ActionExport: "export",
|
||||
ActionImport: "import",
|
||||
},
|
||||
resources: {
|
||||
ResourceDefault: "*",
|
||||
ResourceConfig: "config",
|
||||
ResourceConfigOptions: "config_options",
|
||||
ResourceSettings: "settings",
|
||||
ResourceLogs: "logs",
|
||||
ResourceAccounts: "accounts",
|
||||
ResourceSubjects: "subjects",
|
||||
ResourceAlbums: "albums",
|
||||
ResourceCameras: "cameras",
|
||||
ResourceCategories: "categories",
|
||||
ResourceCountries: "countries",
|
||||
ResourceFiles: "files",
|
||||
ResourceFolders: "folders",
|
||||
ResourceLabels: "labels",
|
||||
ResourceLenses: "lenses",
|
||||
ResourceLinks: "links",
|
||||
ResourceGeo: "geo",
|
||||
ResourcePasswords: "passwords",
|
||||
ResourceUsers: "users",
|
||||
ResourcePhotos: "photos",
|
||||
ResourcePlaces: "places",
|
||||
ResourceFeedback: "feedback",
|
||||
ResourceReview: "review",
|
||||
ResourceArchive: "archive",
|
||||
ResourcePrivate: "private",
|
||||
ResourceLibrary: "library",
|
||||
},
|
||||
};
|
||||
|
||||
export default class Acl {
|
||||
constructor(acl) {
|
||||
this.acl = acl;
|
||||
}
|
||||
accessAllowed(role, resource, action) {
|
||||
if (!this.acl) return false;
|
||||
let res;
|
||||
if (!this.acl[resource]) {
|
||||
if (!this.acl[Constants.resources.ResourceDefault]) return false;
|
||||
res = this.acl[Constants.resources.ResourceDefault];
|
||||
} else {
|
||||
res = this.acl[resource];
|
||||
}
|
||||
|
||||
let rol;
|
||||
if (!res[role]) {
|
||||
if (!res[Constants.roles.RoleDefault]) return false;
|
||||
rol = res[Constants.roles.RoleDefault];
|
||||
} else {
|
||||
rol = res[role];
|
||||
}
|
||||
|
||||
let act;
|
||||
if (!rol[action]) {
|
||||
if (!rol[Constants.actions.ActionDefault]) return false;
|
||||
act = rol[Constants.actions.ActionDefault];
|
||||
} else {
|
||||
act = rol[action];
|
||||
}
|
||||
return act;
|
||||
}
|
||||
accessAllowedAny(role, resource, ...actions) {
|
||||
return actions.some((action) => {
|
||||
return this.accessAllowed(role, resource, action);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const aclMixin = {
|
||||
data() {
|
||||
return {
|
||||
aclResources: Constants.resources,
|
||||
aclActions: Constants.actions,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
acl() {
|
||||
const c = config.values.acl ? config.values : window.__CONFIG__;
|
||||
return new Acl(c.acl);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(resource, ...actions) {
|
||||
const c = config.values.acl ? config.values : window.__CONFIG__;
|
||||
console.log(c);
|
||||
if (c.public) return true;
|
||||
const role = this.$session.getUser().getRole();
|
||||
return this.acl.accessAllowedAny(role, resource, ...actions);
|
||||
},
|
||||
},
|
||||
};
|
|
@ -232,6 +232,7 @@ export default class Session {
|
|||
}
|
||||
|
||||
logout(noRedirect) {
|
||||
sessionStorage.setItem("preventAutoLogin", Date.now().toString());
|
||||
if (this.hasId()) {
|
||||
return Api.delete("session/" + this.getId())
|
||||
.then(() => {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
</template>
|
||||
|
||||
<v-btn
|
||||
v-if="features.share"
|
||||
v-if="features.share && hasPermission(aclResources.ResourceAlbums, aclActions.ActionShare)"
|
||||
fab dark small
|
||||
:title="$gettext('Share')"
|
||||
color="share"
|
||||
|
@ -33,6 +33,7 @@
|
|||
<v-icon>share</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate)"
|
||||
fab dark small
|
||||
:title="$gettext('Edit')"
|
||||
color="edit"
|
||||
|
@ -54,7 +55,7 @@
|
|||
<v-icon>get_app</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="features.albums"
|
||||
v-if="features.albums && hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate, aclActions.ActionCreate)"
|
||||
fab dark small
|
||||
:title="$gettext('Add to album')"
|
||||
color="album"
|
||||
|
@ -65,7 +66,7 @@
|
|||
<v-icon>bookmark</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="deletable.includes(context)"
|
||||
v-if="deletable.includes(context) && hasPermission(aclResources.ResourceAlbums, aclActions.ActionDelete)"
|
||||
fab dark small
|
||||
color="remove"
|
||||
:title="$gettext('Delete')"
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
<v-icon>refresh</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn icon class="action-edit" :title="$gettext('Edit')" @click.stop="dialog.edit = true">
|
||||
<v-btn v-if="hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate)" icon class="action-edit" :title="$gettext('Edit')" @click.stop="dialog.edit = true">
|
||||
<v-icon>edit</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="$config.feature('share')" icon class="action-share" :title="$gettext('Share')"
|
||||
<v-btn v-if="$config.feature('share') && hasPermission(aclResources.ResourceAlbums, aclActions.ActionShare)" icon class="action-share" :title="$gettext('Share')"
|
||||
@click.stop="dialog.share = true">
|
||||
<v-icon>share</v-icon>
|
||||
</v-btn>
|
||||
|
@ -27,18 +27,18 @@
|
|||
<v-icon>get_app</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="settings.view === 'cards'" icon :title="$gettext('Toggle View')" @click.stop="setView('list')">
|
||||
<v-btn v-if="settings.view === 'cards'" icon class="action-list-view" :title="$gettext('Toggle View')" @click.stop="setView('list')">
|
||||
<v-icon>view_list</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="settings.view === 'list'" icon :title="$gettext('Toggle View')" @click.stop="setView('mosaic')">
|
||||
<v-btn v-else-if="settings.view === 'list'" icon class="action-mosaic-view" :title="$gettext('Toggle View')" @click.stop="setView('mosaic')">
|
||||
<v-icon>view_comfy</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon :title="$gettext('Toggle View')" @click.stop="setView('cards')">
|
||||
<v-btn v-else icon class="action-card-view" :title="$gettext('Toggle View')" @click.stop="setView('cards')">
|
||||
<v-icon>view_column</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="!$config.values.readonly && $config.feature('upload')" icon class="hidden-sm-and-down action-upload"
|
||||
:title="$gettext('Upload')" @click.stop="showUpload()">
|
||||
<v-btn v-if="!$config.values.readonly && $config.feature('upload') && hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpload)"
|
||||
icon class="hidden-sm-and-down action-upload" :title="$gettext('Upload')" @click.stop="showUpload()">
|
||||
<v-icon>cloud_upload</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
v-if="$config.feature('albums')"
|
||||
v-if="$config.feature('albums') && hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate, aclActions.ActionCreate)"
|
||||
fab dark small
|
||||
:title="$gettext('Add to album')"
|
||||
color="album"
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<v-icon>cloud_download</v-icon>
|
||||
</v-btn -->
|
||||
<v-btn
|
||||
v-if="$config.feature('albums')"
|
||||
v-if="$config.feature('albums') && hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate, aclActions.ActionCreate)"
|
||||
fab dark small
|
||||
:title="$gettext('Add to album')"
|
||||
color="album"
|
||||
|
@ -47,6 +47,7 @@
|
|||
<v-icon>bookmark</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="hasPermission(aclResources.ResourceLabels, aclActions.ActionDelete)"
|
||||
fab dark small
|
||||
color="remove"
|
||||
:title="$gettext('Delete')"
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<template v-else>{{ page.title }}</template>
|
||||
</v-toolbar-title>
|
||||
|
||||
<v-btn v-if="auth && !config.readonly && $config.feature('upload')" icon class="action-upload"
|
||||
<v-btn v-if="auth && !config.readonly && $config.feature('upload') && hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpload)" icon class="action-upload"
|
||||
:title="$gettext('Upload')" @click.stop="openUpload()">
|
||||
<v-icon>cloud_upload</v-icon>
|
||||
</v-btn>
|
||||
|
@ -118,7 +118,7 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile v-if="$config.feature('review')" to="/review" class="nav-review"
|
||||
<v-list-tile v-if="$config.feature('review') && hasPermission(aclResources.ResourceReview, aclActions.ActionRead)" to="/review" class="nav-review"
|
||||
@click.stop="">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title :class="`p-flex-menuitem menu-item ${rtl ? '--rtl' : ''}`">
|
||||
|
@ -128,7 +128,7 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile v-show="$config.feature('archive')" to="/archive" class="nav-archive" @click.stop="">
|
||||
<v-list-tile v-show="$config.feature('archive') && hasPermission(aclResources.ResourceArchive, aclActions.ActionRead)" to="/archive" class="nav-archive" @click.stop="">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title :class="`p-flex-menuitem menu-item ${rtl ? '--rtl' : ''}`">
|
||||
<translate>Archive</translate>
|
||||
|
@ -204,7 +204,7 @@
|
|||
</v-list-tile>
|
||||
</v-list-group>
|
||||
|
||||
<v-list-tile v-show="$config.feature('people')" :to="{ name: 'people' }" class="nav-people" @click.stop="">
|
||||
<v-list-tile v-show="$config.feature('people') && hasPermission(aclResources.ResourceSubjects, aclActions.ActionSearch, aclActions.ActionRead)" :to="{ name: 'people' }" class="nav-people" @click.stop="">
|
||||
<v-list-tile-action :title="$gettext('People')">
|
||||
<v-icon>person</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
@ -296,7 +296,7 @@
|
|||
</v-list-tile>
|
||||
</v-list-group>
|
||||
|
||||
<v-list-tile v-show="$config.feature('labels')" to="/labels" class="nav-labels" @click.stop="">
|
||||
<v-list-tile v-show="$config.feature('labels') && hasPermission(aclResources.ResourceLabels, aclActions.ActionRead, aclActions.ActionSearch)" to="/labels" class="nav-labels" @click.stop="">
|
||||
<v-list-tile-action :title="$gettext('Labels')">
|
||||
<v-icon>label</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
@ -324,7 +324,7 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile v-show="$config.feature('private')" to="/private" class="nav-private" @click.stop="">
|
||||
<v-list-tile v-show="$config.feature('private') && hasPermission(aclResources.ResourcePrivate, aclActions.ActionRead)" to="/private" class="nav-private" @click.stop="">
|
||||
<v-list-tile-action :title="$gettext('Private')">
|
||||
<v-icon>lock</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
@ -337,7 +337,7 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile v-if="isMini && $config.feature('library')" to="/library" class="nav-library" @click.stop="">
|
||||
<v-list-tile v-if="isMini && $config.feature('library') && hasPermission(aclResources.ResourceLibrary, aclActions.ActionRead)" to="/library" class="nav-library" @click.stop="">
|
||||
<v-list-tile-action :title="$gettext('Library')">
|
||||
<v-icon>camera_roll</v-icon>
|
||||
</v-list-tile-action>
|
||||
|
@ -349,7 +349,7 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-group v-if="!isMini && $config.feature('library')" prepend-icon="camera_roll" no-action>
|
||||
<v-list-group v-if="!isMini && $config.feature('library') && hasPermission(aclResources.ResourceLibrary, aclActions.ActionRead)" prepend-icon="camera_roll" no-action>
|
||||
<template #activator>
|
||||
<v-list-tile to="/library" class="nav-library" @click.stop="">
|
||||
<v-list-tile-content>
|
||||
|
@ -387,7 +387,7 @@
|
|||
</v-list-tile>
|
||||
</v-list-group>
|
||||
|
||||
<template v-if="!config.disable.settings">
|
||||
<template v-if="!config.disable.settings && hasPermission(aclResources.ResourceSettings, aclActions.ActionRead)">
|
||||
<v-list-tile v-if="isMini" to="/settings" class="nav-settings" @click.stop="">
|
||||
<v-list-tile-action :title="$gettext('Settings')">
|
||||
<v-icon>settings</v-icon>
|
||||
|
@ -466,7 +466,7 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile v-show="!isPublic && auth" to="/settings/account" class="p-profile">
|
||||
<v-list-tile v-show="!isPublic && auth" to="/account" class="nav-profile">
|
||||
<v-list-tile-avatar color="grey" size="36">
|
||||
<span class="white--text headline">{{ displayName.length >= 1 ? displayName[0].toUpperCase() : "E" }}</span>
|
||||
</v-list-tile-avatar>
|
||||
|
@ -475,11 +475,11 @@
|
|||
<v-list-tile-title>
|
||||
{{ displayName }}
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-sub-title>{{ accountInfo }}</v-list-tile-sub-title>
|
||||
<v-list-tile-sub-title style="font-size: smaller">{{ accountInfo }}</v-list-tile-sub-title>
|
||||
</v-list-tile-content>
|
||||
|
||||
<v-list-tile-action :title="$gettext('Logout')">
|
||||
<v-btn icon @click="logout">
|
||||
<v-btn icon @click.stop.prevent="logout">
|
||||
<v-icon>power_settings_new</v-icon>
|
||||
</v-btn>
|
||||
</v-list-tile-action>
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
<v-icon color="white" class="action-play">play_arrow</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="hidePrivate" :ripple="false"
|
||||
<v-btn v-if="hidePrivate && hasPermission(aclResources.ResourcePhotos, aclActions.ActionPrivate)" :ripple="false"
|
||||
icon flat absolute
|
||||
class="input-private">
|
||||
<v-icon color="white" class="select-on">lock</v-icon>
|
||||
|
@ -104,9 +104,10 @@
|
|||
icon flat absolute
|
||||
class="input-favorite"
|
||||
@touchstart.stop.prevent="input.touchStart($event, index)"
|
||||
@touchend.stop.prevent="toggleLike($event, index)"
|
||||
@touchend.stop.prevent="hasPermission(aclResources.ResourcePhotos, aclActions.ActionLike) && toggleLike($event, index)"
|
||||
@touchmove.stop.prevent
|
||||
@click.stop.prevent="toggleLike($event, index)">
|
||||
@click.stop.prevent="hasPermission(aclResources.ResourcePhotos, aclActions.ActionLike) && toggleLike($event, index)"
|
||||
>
|
||||
<v-icon color="white" class="select-on">favorite</v-icon>
|
||||
<v-icon color="white" class="select-off">favorite_border</v-icon>
|
||||
</v-btn>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</template>
|
||||
|
||||
<v-btn
|
||||
v-if="context !== 'archive' && context !== 'review' && features.share" fab dark
|
||||
v-if="context !== 'archive' && context !== 'review' && features.share && hasPermission(aclResources.ResourcePhotos, aclActions.ActionShare)" fab dark
|
||||
small
|
||||
:title="$gettext('Share')"
|
||||
color="share"
|
||||
|
@ -57,7 +57,7 @@
|
|||
<v-icon>edit</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="context !== 'archive' && features.private" fab dark
|
||||
v-if="context !== 'archive' && features.private && hasPermission(aclResources.ResourcePhotos, aclActions.ActionPrivate)" fab dark
|
||||
small
|
||||
:title="$gettext('Change private flag')"
|
||||
color="private"
|
||||
|
@ -78,7 +78,7 @@
|
|||
<v-icon>get_app</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="context !== 'archive' && features.albums" fab dark
|
||||
v-if="context !== 'archive' && features.albums && hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate, aclActions.ActionCreate)" fab dark
|
||||
small
|
||||
:title="$gettext('Add to album')"
|
||||
color="album"
|
||||
|
@ -89,7 +89,7 @@
|
|||
<v-icon>bookmark</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="!isAlbum && context !== 'archive' && features.archive" fab dark
|
||||
v-if="!isAlbum && context !== 'archive' && features.archive && hasPermission(aclResources.ResourcePhotos, aclActions.ActionArchive)" fab dark
|
||||
small
|
||||
color="remove"
|
||||
:title="$gettext('Archive')"
|
||||
|
@ -100,7 +100,7 @@
|
|||
<v-icon>archive</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="!album && context === 'archive'" fab dark
|
||||
v-if="!album && context === 'archive' && hasPermission(aclResources.ResourcePhotos, aclActions.ActionArchive)" fab dark
|
||||
small
|
||||
color="restore"
|
||||
:title="$gettext('Restore')"
|
||||
|
@ -111,7 +111,7 @@
|
|||
<v-icon>unarchive</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="isAlbum && features.albums" fab dark
|
||||
v-if="isAlbum && features.albums && hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate)" fab dark
|
||||
small
|
||||
:title="$gettext('Remove from album')"
|
||||
color="remove"
|
||||
|
@ -122,7 +122,7 @@
|
|||
<v-icon>eject</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="!album && context === 'archive' && features.delete" fab dark
|
||||
v-if="!album && context === 'archive' && features.delete && hasPermission(aclResources.ResourcePhotos, aclActions.ActionDelete)" fab dark
|
||||
small
|
||||
:title="$gettext('Delete')"
|
||||
color="remove"
|
||||
|
|
|
@ -93,13 +93,13 @@
|
|||
</span>
|
||||
</td>
|
||||
<td class="text-xs-center">
|
||||
<v-btn v-if="hidePrivate" class="input-private" icon small flat :ripple="false"
|
||||
<v-btn v-if="hidePrivate" class="input-private" icon small flat :ripple="false" :disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionPrivate)"
|
||||
:data-uid="props.item.UID" @click.stop.prevent="props.item.togglePrivate()">
|
||||
<v-icon v-if="props.item.Private" color="secondary-dark" class="select-on">lock</v-icon>
|
||||
<v-icon v-else color="secondary" class="select-off">lock_open</v-icon>
|
||||
</v-btn>
|
||||
<v-btn class="input-like" icon small flat :ripple="false"
|
||||
:data-uid="props.item.UID" @click.stop.prevent="props.item.toggleLike()">
|
||||
<v-btn class="input-like" icon small flat :ripple="false" :disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionLike)"
|
||||
:data-uid="props.item.UID" @click.stop.prevent="hasPermission(aclResources.ResourcePhotos, aclActions.ActionLike) && props.item.toggleLike()">
|
||||
<v-icon v-if="props.item.Favorite" color="pink lighten-3" :data-uid="props.item.UID" class="select-on">
|
||||
favorite
|
||||
</v-icon>
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
<v-icon color="white" class="action-play">play_arrow</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="hidePrivate" :ripple="false"
|
||||
<v-btn v-if="hidePrivate && hasPermission(aclResources.ResourcePhotos, aclActions.ActionPrivate)" :ripple="false"
|
||||
icon flat small absolute
|
||||
class="input-private">
|
||||
<v-icon color="white" class="select-on">lock</v-icon>
|
||||
|
@ -103,9 +103,9 @@
|
|||
icon flat small absolute
|
||||
class="input-favorite"
|
||||
@touchstart.stop.prevent="input.touchStart($event, index)"
|
||||
@touchend.stop.prevent="toggleLike($event, index)"
|
||||
@touchend.stop.prevent="hasPermission(aclResources.ResourcePhotos, aclActions.ActionLike) && toggleLike($event, index)"
|
||||
@touchmove.stop.prevent
|
||||
@click.stop.prevent="toggleLike($event, index)">
|
||||
@click.stop.prevent="hasPermission(aclResources.ResourcePhotos, aclActions.ActionLike) && toggleLike($event, index)">
|
||||
<v-icon color="white" class="select-on">favorite</v-icon>
|
||||
<v-icon color="white" class="select-off">favorite_border</v-icon>
|
||||
</v-btn>
|
||||
|
@ -220,7 +220,7 @@ export default {
|
|||
},
|
||||
selectRange(index) {
|
||||
this.$clipboard.addRange(index, this.photos);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<v-icon>view_column</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="!$config.values.readonly && $config.feature('upload')" icon class="hidden-sm-and-down action-upload"
|
||||
<v-btn v-if="!$config.values.readonly && $config.feature('upload') && hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpload)" icon class="hidden-sm-and-down action-upload"
|
||||
:title="$gettext('Upload')" @click.stop="showUpload()">
|
||||
<v-icon>cloud_upload</v-icon>
|
||||
</v-btn>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<v-icon v-else size="16" color="white">radio_button_off</v-icon>
|
||||
</button>
|
||||
|
||||
<button class="pswp__button action-like hidden-shared-only" style="background: none;"
|
||||
<button v-if="hasPermission(aclResources.ResourcePhotos, aclActions.ActionLike)" class="pswp__button action-like hidden-shared-only" style="background: none;"
|
||||
:title="$gettext('Like')" @click.exact="onLike">
|
||||
<v-icon v-if="item.favorite" size="16" color="white">favorite</v-icon>
|
||||
<v-icon v-else size="16" color="white">favorite_border</v-icon>
|
||||
|
@ -269,7 +269,7 @@ export default {
|
|||
g.close(); // Close Gallery
|
||||
|
||||
Event.publish("dialog.edit", {selection, album, index}); // Open Edit Dialog
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
v-if="features.albums"
|
||||
v-if="features.albums && hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate, aclActions.ActionCreate)"
|
||||
fab dark small
|
||||
:title="$gettext('Add to album')"
|
||||
color="album"
|
||||
|
|
|
@ -407,7 +407,6 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
disabled: !this.$config.feature("edit"),
|
||||
config: this.$config.values,
|
||||
all: {
|
||||
colors: [{label: this.$gettext("Unknown"), name: ""}],
|
||||
|
@ -430,6 +429,9 @@ export default {
|
|||
lensOptions() {
|
||||
return this.config.lenses;
|
||||
},
|
||||
disabled() {
|
||||
return !this.$config.feature("edit") || !this.hasPermission(this.aclResources.ResourcePhotos, this.aclActions.ActionUpdate);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
model() {
|
||||
|
|
|
@ -45,19 +45,21 @@
|
|||
@click.stop.prevent="downloadFile(file)">
|
||||
<translate>Download</translate>
|
||||
</v-btn>
|
||||
<v-btn v-if="features.edit && file.Type === 'jpg' && !file.Error && !file.Primary" small depressed dark
|
||||
<v-btn v-if="features.edit && file.Type === 'jpg' && !file.Error && !file.Primary && hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
small depressed dark
|
||||
color="primary-button"
|
||||
class="ma-0 action-primary"
|
||||
@click.stop.prevent="primaryFile(file)">
|
||||
<translate>Primary</translate>
|
||||
</v-btn>
|
||||
<v-btn v-if="features.edit && !file.Sidecar && !file.Error && !file.Primary && file.Root === '/'" small
|
||||
depressed dark color="primary-button"
|
||||
<v-btn v-if="features.edit && !file.Sidecar && !file.Error && !file.Primary && file.Root === '/' && hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
small depressed dark color="primary-button"
|
||||
class="ma-0 action-unstack"
|
||||
@click.stop.prevent="unstackFile(file)">
|
||||
<translate>Unstack</translate>
|
||||
</v-btn>
|
||||
<v-btn v-if="features.delete && !file.Primary" small depressed dark color="primary-button"
|
||||
<v-btn v-if="features.delete && !file.Primary && hasPermission(aclResources.ResourceFiles, aclActions.ActionDelete)"
|
||||
small depressed dark color="primary-button"
|
||||
class="ma-0 action-delete"
|
||||
@click.stop.prevent="showDeleteDialog(file)">
|
||||
<translate>Delete</translate>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<v-select
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
v-model="model.Type"
|
||||
flat solo
|
||||
browser-autocomplete="off"
|
||||
|
@ -47,6 +48,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<v-text-field
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
v-model="model.OriginalName"
|
||||
flat solo dense hide-details color="secondary-dark"
|
||||
@change="save"
|
||||
|
@ -100,6 +102,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
v-model="model.Stack"
|
||||
hide-details
|
||||
class="input-stackable"
|
||||
|
@ -116,6 +119,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionLike)"
|
||||
v-model="model.Favorite"
|
||||
hide-details
|
||||
class="input-favorite"
|
||||
|
@ -130,6 +134,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionPrivate)"
|
||||
v-model="model.Private"
|
||||
hide-details
|
||||
class="input-private"
|
||||
|
@ -144,6 +149,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
v-model="model.Scan"
|
||||
hide-details
|
||||
class="input-scan"
|
||||
|
@ -158,6 +164,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
v-model="model.Panorama"
|
||||
hide-details
|
||||
class="input-panorama"
|
||||
|
@ -205,6 +212,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<v-text-field
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
v-model="model.CellAccuracy"
|
||||
flat solo dense hide-details color="secondary-dark"
|
||||
type="number"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
@save="renameLabel(props.item.Label)"
|
||||
>
|
||||
{{ props.item.Label.Name }}
|
||||
<template #input>
|
||||
<template v-if="hasPermission(aclResources.ResourceLabels, aclActions.ActionUpdate)" #input>
|
||||
<v-text-field
|
||||
v-model="props.item.Label.Name"
|
||||
:rules="[nameRule]"
|
||||
|
@ -40,24 +40,27 @@
|
|||
<v-icon color="secondary-dark">search</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="props.item.Uncertainty < 100 && props.item.LabelSrc === 'manual'" icon
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
small flat :ripple="false"
|
||||
class="action-delete" title="Delete"
|
||||
@click.stop.prevent="removeLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">delete</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="props.item.Uncertainty < 100" icon small flat :ripple="false"
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
class="action-remove" title="Remove"
|
||||
@click.stop.prevent="removeLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">remove</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon small flat :ripple="false"
|
||||
:disabled="!hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate)"
|
||||
class="action-on" title="Activate"
|
||||
@click.stop.prevent="activateLabel(props.item.Label)">
|
||||
<v-icon color="secondary-dark">add</v-icon>
|
||||
</v-btn>
|
||||
</td>
|
||||
</template>
|
||||
<template v-if="!disabled" #footer>
|
||||
<template v-if="!disabled && hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpdate) && hasPermission(aclResources.ResourceLabels, aclActions.ActionCreate)" #footer>
|
||||
<td>
|
||||
<v-text-field
|
||||
v-model="newLabel"
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
:transition="false"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2">
|
||||
<v-btn v-if="!marker.SubjUID && !marker.Invalid" :ripple="false" :depressed="false" class="input-reject"
|
||||
<v-btn v-if="!marker.SubjUID && !marker.Invalid && hasPermission(aclResources.ResourceFiles, aclActions.ActionUpdate)" :ripple="false" :depressed="false" class="input-reject"
|
||||
icon flat small absolute :title="$gettext('Remove')"
|
||||
@click.stop.prevent="onReject(marker)">
|
||||
<v-icon color="white" class="action-reject">clear</v-icon>
|
||||
|
@ -52,7 +52,7 @@
|
|||
<v-text-field
|
||||
v-model="marker.Name"
|
||||
:rules="[textRule]"
|
||||
:disabled="busy"
|
||||
:disabled="busy || !hasPermission(aclResources.ResourceFiles, aclActions.ActionUpdate)"
|
||||
:readonly="true"
|
||||
browser-autocomplete="off"
|
||||
class="input-name pa-0 ma-0"
|
||||
|
@ -75,7 +75,7 @@
|
|||
:items="$config.values.people"
|
||||
item-value="Name"
|
||||
item-text="Name"
|
||||
:disabled="busy"
|
||||
:disabled="busy || !hasPermission(aclResources.ResourceFiles, aclActions.ActionUpdate)"
|
||||
:return-object="false"
|
||||
:menu-props="menuProps"
|
||||
:allow-overflow="false"
|
||||
|
|
|
@ -32,6 +32,7 @@ import RestModel from "model/rest";
|
|||
import Form from "common/form";
|
||||
import Api from "common/api";
|
||||
import { $gettext } from "common/vm";
|
||||
import { Constants } from "../common/acl";
|
||||
|
||||
export class User extends RestModel {
|
||||
getDefaults() {
|
||||
|
@ -114,6 +115,19 @@ export class User extends RestModel {
|
|||
);
|
||||
}
|
||||
|
||||
getRole() {
|
||||
const roles = Constants.roles;
|
||||
if (this.RoleAdmin) return roles.RoleAdmin;
|
||||
if (this.RoleChild) return roles.RoleChild;
|
||||
if (this.RoleFamily) return roles.RoleFamily;
|
||||
if (this.RoleFriend) return roles.RoleFriend;
|
||||
if (this.RoleGuest) return roles.RoleGuest;
|
||||
if (this.UserName.length >= 4 && this.UID.length === 16 && this.UID[0] === "u") {
|
||||
return roles.RoleMember;
|
||||
}
|
||||
return roles.RoleDefault;
|
||||
}
|
||||
|
||||
static getCollectionResource() {
|
||||
return "users";
|
||||
}
|
||||
|
|
|
@ -34,12 +34,12 @@
|
|||
<v-icon>refresh</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="!$config.values.readonly && $config.feature('upload')" icon class="hidden-sm-and-down action-upload"
|
||||
<v-btn v-if="!$config.values.readonly && $config.feature('upload') && hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpload)" icon class="hidden-sm-and-down action-upload"
|
||||
:title="$gettext('Upload')" @click.stop="showUpload()">
|
||||
<v-icon>cloud_upload</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="staticFilter.type === 'album'" icon class="action-add" :title="$gettext('Add Album')"
|
||||
<v-btn v-if="staticFilter.type === 'album' && hasPermission(aclResources.ResourceAlbums, aclActions.ActionCreate)" icon class="action-add" :title="$gettext('Add Album')"
|
||||
@click.prevent="create">
|
||||
<v-icon>add</v-icon>
|
||||
</v-btn>
|
||||
|
@ -101,9 +101,9 @@
|
|||
@mousedown="input.mouseDown($event, index)"
|
||||
@click.stop.prevent="onClick($event, index)"
|
||||
>
|
||||
<v-btn v-if="featureShare && album.LinkCount > 0" :ripple="false"
|
||||
<v-btn v-if="featureShare && album.LinkCount > 0 && hasPermission(aclResources.ResourceAlbums, aclActions.ActionShare)" :ripple="false"
|
||||
icon flat absolute
|
||||
class="action-share"
|
||||
class="input-share"
|
||||
@touchstart.stop.prevent="input.touchStart($event, index)"
|
||||
@touchend.stop.prevent="onShare($event, index)"
|
||||
@touchmove.stop.prevent
|
||||
|
@ -126,9 +126,9 @@
|
|||
icon flat absolute
|
||||
class="input-favorite"
|
||||
@touchstart.stop.prevent="input.touchStart($event, index)"
|
||||
@touchend.stop.prevent="toggleLike($event, index)"
|
||||
@touchend.stop.prevent="hasPermission(aclResources.ResourceAlbums, aclActions.ActionLike) && toggleLike($event, index)"
|
||||
@touchmove.stop.prevent
|
||||
@click.stop.prevent="toggleLike($event, index)">
|
||||
@click.stop.prevent="hasPermission(aclResources.ResourceAlbums, aclActions.ActionLike) && toggleLike($event, index)">
|
||||
<v-icon color="#FFD600" class="select-on">star</v-icon>
|
||||
<v-icon color="white" class="select-off">star_border</v-icon>
|
||||
</v-btn>
|
||||
|
@ -138,11 +138,11 @@
|
|||
<div>
|
||||
<h3 class="body-2 mb-0">
|
||||
<button v-if="album.Type !== 'month'" class="action-title-edit" :data-uid="album.UID"
|
||||
@click.stop.prevent="edit(album)">
|
||||
@click.stop.prevent="hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate) && edit(album)">
|
||||
{{ album.Title | truncate(80) }}
|
||||
</button>
|
||||
<button v-else class="action-title-edit" :data-uid="album.UID"
|
||||
@click.stop.prevent="edit(album)">
|
||||
@click.stop.prevent="hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate) && edit(album)">
|
||||
{{ album.getDateString() | capitalize }}
|
||||
</button>
|
||||
</h3>
|
||||
|
@ -152,13 +152,13 @@
|
|||
<v-card-text primary-title class="pb-2 pt-0 card-details" style="user-select: none;"
|
||||
@click.stop.prevent="">
|
||||
<div v-if="album.Description" class="caption mb-2" :title="$gettext('Description')">
|
||||
<button @click.exact="edit(album)">
|
||||
<button @click.exact="hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate) && edit(album)">
|
||||
{{ album.Description | truncate(100) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-else-if="album.Type === 'album'" class="caption mb-2">
|
||||
<button v-if="album.PhotoCount === 1" @click.exact="edit(album)">
|
||||
<button v-if="album.PhotoCount === 1" @click.exact="hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate) && edit(album)">
|
||||
<translate>Contains one picture.</translate>
|
||||
</button>
|
||||
<button v-else-if="album.PhotoCount > 0">
|
||||
|
@ -169,18 +169,18 @@
|
|||
</button>
|
||||
</div>
|
||||
<div v-else-if="album.Type === 'folder'" class="caption mb-2">
|
||||
<button @click.exact="edit(album)">
|
||||
<button @click.exact="hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate) && edit(album)">
|
||||
/{{ album.Path | truncate(100) }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="album.Category !== ''" class="caption mb-2 d-inline-block">
|
||||
<button @click.exact="edit(album)">
|
||||
<button @click.exact="hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate) && edit(album)">
|
||||
<v-icon size="14">local_offer</v-icon>
|
||||
{{ album.Category }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="album.getLocation() !== ''" class="caption mb-2 d-inline-block">
|
||||
<button @click.exact="edit(album)">
|
||||
<button @click.exact="hasPermission(aclResources.ResourceAlbums, aclActions.ActionUpdate) && edit(album)">
|
||||
<v-icon size="14">location_on</v-icon>
|
||||
{{ album.getLocation() }}
|
||||
</button>
|
||||
|
@ -189,7 +189,7 @@
|
|||
</v-card>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<div v-if="staticFilter.type === 'album' && config.count.albums === 0" class="text-xs-center my-2">
|
||||
<div v-if="staticFilter.type === 'album' && config.count.albums === 0 && hasPermission(aclResources.ResourceAlbums, aclActions.ActionCreate)" class="text-xs-center my-2">
|
||||
<v-btn class="action-add" color="secondary" round @click.prevent="create">
|
||||
<translate>Add Album</translate>
|
||||
</v-btn>
|
||||
|
|
|
@ -94,9 +94,9 @@
|
|||
icon flat absolute
|
||||
class="input-favorite"
|
||||
@touchstart.stop.prevent="input.touchStart($event, index)"
|
||||
@touchend.stop.prevent="toggleLike($event, index)"
|
||||
@touchend.stop.prevent="hasPermission(aclResources.ResourceLabels, aclActions.ActionLike) && toggleLike($event, index)"
|
||||
@touchmove.stop.prevent
|
||||
@click.stop.prevent="toggleLike($event, index)">
|
||||
@click.stop.prevent="hasPermission(aclResources.ResourceLabels, aclActions.ActionLike) && toggleLike($event, index)">
|
||||
<v-icon color="#FFD600" class="select-on">star</v-icon>
|
||||
<v-icon color="white" class="select-off">star_border</v-icon>
|
||||
</v-btn>
|
||||
|
@ -115,7 +115,7 @@
|
|||
<span v-else>
|
||||
<v-icon>edit</v-icon>
|
||||
</span>
|
||||
<template #input>
|
||||
<template v-if="hasPermission(aclResources.ResourceLabels, aclActions.ActionUpdate)" #input>
|
||||
<v-text-field
|
||||
v-model="label.Name"
|
||||
:rules="[titleRule]"
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
<translate>Cancel</translate>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="!$config.values.readonly && $config.feature('upload')"
|
||||
<v-btn v-if="!$config.values.readonly && $config.feature('upload') && hasPermission(aclResources.ResourcePhotos, aclActions.ActionUpload)"
|
||||
:disabled="busy || !ready"
|
||||
color="primary-button"
|
||||
class="white--text ml-0 hidden-xs-only action-upload"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
browser-autocomplete="off"
|
||||
color="secondary-dark"
|
||||
class="input-name"
|
||||
placeholder="••••••••"
|
||||
placeholder="username"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="pa-2">
|
||||
|
@ -47,6 +47,18 @@
|
|||
<translate>Sign in</translate>
|
||||
<v-icon :right="!rtl" :left="rtl" dark>login</v-icon>
|
||||
</v-btn>
|
||||
<v-btn color="primary-button"
|
||||
class="white--text ml-0 action-confirm"
|
||||
depressed
|
||||
:disabled="loading"
|
||||
@click.stop="loginExternal"
|
||||
v-if="!!authProvider" >
|
||||
<translate>Sign in with {{ authProvider }}</translate>
|
||||
<v-icon :right="!rtl" :left="rtl" dark>login</v-icon>
|
||||
</v-btn>
|
||||
<template v-if="authProvider">
|
||||
<p class="mt-3">⚠️ Sign in with local user accounts except admin is disabled when using OpenID Connect.</p>
|
||||
</template>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card-actions>
|
||||
|
@ -58,21 +70,54 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Notify from "../common/notify";
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
name: 'Login',
|
||||
data() {
|
||||
const c = this.$config.values;
|
||||
|
||||
return {
|
||||
loading: false,
|
||||
showPassword: false,
|
||||
username: "admin",
|
||||
username: "",
|
||||
password: "",
|
||||
siteDescription: c.siteDescription ? c.siteDescription : c.siteCaption,
|
||||
authProvider: c.oidc ? "OpenID Connect" : null,
|
||||
nextUrl: this.$route.params.nextUrl ? this.$route.params.nextUrl : "/",
|
||||
rtl: this.$rtl,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const c = window.__CONFIG__;
|
||||
const preventAutoLogin = sessionStorage.getItem("preventAutoLogin");
|
||||
const err = window.localStorage.getItem('auth_error');
|
||||
sessionStorage.removeItem("preventAutoLogin");
|
||||
if (!c.oidc || this.$route.query.preventAutoLogin || preventAutoLogin) {
|
||||
if (err) {
|
||||
Notify.error(err);
|
||||
window.localStorage.removeItem('auth_error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
const cleanup = () => {
|
||||
window.localStorage.removeItem('config');
|
||||
window.localStorage.removeItem('auth_error');
|
||||
};
|
||||
const redirect = () => {
|
||||
if (err) return;
|
||||
// check if oidc provider is available
|
||||
axios.get(c.oidc,{ timeout: 1000}).then(response => {
|
||||
// redirect to oidc provider
|
||||
sessionStorage.setItem("preventAutoLogin", Date.now().toString());
|
||||
window.location.href = '/api/v1/auth/external';
|
||||
}).catch(error => {
|
||||
if (c.debug) console.log(error);
|
||||
});
|
||||
};
|
||||
const externalLogin = this.onExternalLogin(cleanup, redirect);
|
||||
externalLogin();
|
||||
},
|
||||
methods: {
|
||||
login() {
|
||||
if (!this.username || !this.password) {
|
||||
|
@ -87,6 +132,50 @@ export default {
|
|||
}
|
||||
).catch(() => this.loading = false);
|
||||
},
|
||||
}
|
||||
loginExternal() {
|
||||
const c = this.$config.values;
|
||||
this.loading = true;
|
||||
axios.get(c.oidc,{ timeout: 3000}).then(response => {
|
||||
let popup = window.open('api/v1/auth/external', "external-login");
|
||||
window.localStorage.removeItem('auth_error');
|
||||
const onstorage = window.onstorage;
|
||||
const cleanup = () => {
|
||||
window.localStorage.removeItem('config');
|
||||
window.localStorage.removeItem('auth_error');
|
||||
window.onstorage = onstorage;
|
||||
popup.close();
|
||||
};
|
||||
window.onstorage = this.onExternalLogin(cleanup);
|
||||
}).catch(error => {
|
||||
if (c.debug) console.log(error);
|
||||
Notify.error(`Couldn't connect to OpenID Connect Provider. ${error}`);
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
onExternalLogin(cleanup, redirect) {
|
||||
return () => {
|
||||
const sid = window.localStorage.getItem('session_id');
|
||||
const data = window.localStorage.getItem('data');
|
||||
const config = window.localStorage.getItem('config');
|
||||
const error = window.localStorage.getItem('auth_error');
|
||||
|
||||
if (error !== null) {
|
||||
Notify.error(`${error}`);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
if (sid === null || data === null || config === null) {
|
||||
if (typeof redirect === 'function') redirect();
|
||||
return;
|
||||
}
|
||||
this.$session.setId(sid);
|
||||
this.$session.setData(JSON.parse(data));
|
||||
this.$session.setConfig(JSON.parse(config));
|
||||
this.$router.push(this.nextUrl);
|
||||
cleanup();
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
slider-color="secondary-dark"
|
||||
:height="$vuetify.breakpoint.smAndDown ? 48 : 64"
|
||||
>
|
||||
<v-tab v-for="(item, index) in tabs" :id="'tab-' + item.name" :key="index" :class="item.class"
|
||||
<v-tab v-for="(item, index) in permittedTabs" :id="'tab-' + item.name" :key="index" :class="item.class"
|
||||
ripple @click.stop.prevent="changePath(item.path)">
|
||||
<v-icon v-if="$vuetify.breakpoint.smAndDown" :title="item.label">{{ item.icon }}</v-icon>
|
||||
<template v-else>
|
||||
|
@ -23,7 +23,7 @@
|
|||
</v-tab>
|
||||
|
||||
<v-tabs-items touchless>
|
||||
<v-tab-item v-for="(item, index) in tabs" :key="index" lazy>
|
||||
<v-tab-item v-for="(item, index) in permittedTabs" :key="index" lazy>
|
||||
<component :is="item.component" :static-filter="item.filter" :active="active === index"
|
||||
@updateFaceCount="onUpdateFaceCount"></component>
|
||||
</v-tab-item>
|
||||
|
@ -53,6 +53,9 @@ export default {
|
|||
'class': '',
|
||||
'path': '/people',
|
||||
'icon': 'people_alt',
|
||||
'permission': () => {
|
||||
return this.hasPermission(this.aclResources.ResourceSubjects, this.aclActions.ActionRead, this.aclActions.ActionSearch);
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'people_faces',
|
||||
|
@ -63,6 +66,9 @@ export default {
|
|||
'path': '/people/new',
|
||||
'icon': 'person_add',
|
||||
'count': 0,
|
||||
'permission': () => {
|
||||
return this.hasPermission(this.aclResources.ResourceSubjects, this.aclActions.ActionUpdate);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -76,6 +82,14 @@ export default {
|
|||
rtl: this.$rtl,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
permittedTabs() {
|
||||
if (!this.tabs) return this.tabs;
|
||||
return this.tabs.filter(tab => {
|
||||
return tab.permission();
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
this.openTab();
|
||||
|
|
|
@ -9,13 +9,15 @@
|
|||
<v-btn icon overflow flat depressed color="secondary-dark" class="action-reload" :title="$gettext('Reload')" @click.stop="refresh">
|
||||
<v-icon>refresh</v-icon>
|
||||
</v-btn>
|
||||
<template v-if="">
|
||||
<v-btn v-if="!filter.hidden" icon class="action-show-hidden" :title="$gettext('Show hidden')" @click.stop="onShowHidden">
|
||||
<v-icon>visibility</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon class="action-exclude-hidden" :title="$gettext('Exclude hidden')" @click.stop="onExcludeHidden">
|
||||
<v-icon>visibility_off</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="!filter.hidden" icon class="action-show-hidden" :title="$gettext('Show hidden')" @click.stop="onShowHidden">
|
||||
<v-icon>visibility</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon class="action-exclude-hidden" :title="$gettext('Exclude hidden')" @click.stop="onExcludeHidden">
|
||||
<v-icon>visibility_off</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-toolbar>
|
||||
</v-form>
|
||||
|
||||
|
@ -66,6 +68,7 @@
|
|||
<v-layout v-if="model.SubjUID" row wrap align-center>
|
||||
<v-flex xs12 class="text-xs-left pa-0">
|
||||
<v-text-field
|
||||
v-if="hasPermission(aclResources.ResourceSubjects, aclActions.ActionUpdate)"
|
||||
v-model="model.Name"
|
||||
:rules="[textRule]"
|
||||
:readonly="readonly"
|
||||
|
|
|
@ -23,13 +23,14 @@
|
|||
<v-btn icon overflow flat depressed color="secondary-dark" class="action-reload" :title="$gettext('Reload')" @click.stop="refresh">
|
||||
<v-icon>refresh</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="!filter.hidden" icon class="action-show-hidden" :title="$gettext('Show hidden')" @click.stop="onShowHidden">
|
||||
<v-icon>visibility</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon class="action-exclude-hidden" :title="$gettext('Exclude hidden')" @click.stop="onExcludeHidden">
|
||||
<v-icon>visibility_off</v-icon>
|
||||
</v-btn>
|
||||
<template v-if="hasPermission(aclResources.ResourceSubjects, aclActions.ActionPrivate)">
|
||||
<v-btn v-if="!filter.hidden" icon class="action-show-hidden" :title="$gettext('Show hidden')" @click.stop="onShowHidden">
|
||||
<v-icon>visibility</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else icon class="action-exclude-hidden" :title="$gettext('Exclude hidden')" @click.stop="onExcludeHidden">
|
||||
<v-icon>visibility_off</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-toolbar>
|
||||
</v-form>
|
||||
|
||||
|
@ -84,7 +85,7 @@
|
|||
@mousedown="input.mouseDown($event, index)"
|
||||
@click.stop.prevent="onClick($event, index)"
|
||||
>
|
||||
<v-btn :ripple="false" :depressed="false" class="input-hidden"
|
||||
<v-btn v-if="hasPermission(aclResources.ResourceSubjects, aclActions.ActionPrivate)" :ripple="false" :depressed="false" class="input-hidden"
|
||||
icon flat small absolute
|
||||
@touchstart.stop.prevent="input.touchStart($event, index)"
|
||||
@touchend.stop.prevent="onToggleHidden($event, index)"
|
||||
|
@ -108,9 +109,9 @@
|
|||
icon flat absolute
|
||||
class="input-favorite"
|
||||
@touchstart.stop.prevent="input.touchStart($event, index)"
|
||||
@touchend.stop.prevent="toggleLike($event, index)"
|
||||
@touchend.stop.prevent="hasPermission(aclResources.ResourceSubjects, aclActions.ActionLike) && toggleLike($event, index)"
|
||||
@touchmove.stop.prevent
|
||||
@click.stop.prevent="toggleLike($event, index)">
|
||||
@click.stop.prevent="hasPermission(aclResources.ResourceSubjects, aclActions.ActionLike) && toggleLike($event, index)">
|
||||
<v-icon color="#FFD600" class="select-on">star</v-icon>
|
||||
<v-icon color="white" class="select-off">star_border</v-icon>
|
||||
</v-btn>
|
||||
|
@ -129,7 +130,7 @@
|
|||
<span v-else>
|
||||
<v-icon>edit</v-icon>
|
||||
</span>
|
||||
<template #input>
|
||||
<template v-if="hasPermission(aclResources.ResourceSubjects, aclActions.ActionUpdate)" #input>
|
||||
<v-text-field
|
||||
v-model="model.Name"
|
||||
:rules="[titleRule]"
|
||||
|
|
94
frontend/src/pages/profile.vue
Normal file
94
frontend/src/pages/profile.vue
Normal file
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<div class="p-page p-page-settings">
|
||||
<v-tabs
|
||||
v-model="active"
|
||||
flat
|
||||
grow
|
||||
touchless
|
||||
color="secondary"
|
||||
slider-color="secondary-dark"
|
||||
:height="$vuetify.breakpoint.smAndDown ? 48 : 64"
|
||||
>
|
||||
<v-tab v-for="(item, index) in tabs" :id="'tab-' + item.name" :key="index" :class="item.class" ripple
|
||||
@click="changePath(item.path)">
|
||||
<v-icon v-if="$vuetify.breakpoint.smAndDown" :title="item.label">{{ item.icon }}</v-icon>
|
||||
<template v-else>
|
||||
<v-icon :size="18" :left="!rtl" :right="rtl">{{ item.icon }}</v-icon> {{ item.label }}
|
||||
</template>
|
||||
</v-tab>
|
||||
|
||||
<v-tabs-items touchless>
|
||||
<v-tab-item v-for="(item, index) in tabs" :key="index" lazy>
|
||||
<component :is="item.component"></component>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</v-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Account from "pages/settings/account.vue";
|
||||
|
||||
function initTabs(flag, tabs) {
|
||||
let i = 0;
|
||||
while(i < tabs.length) {
|
||||
if(!tabs[i][flag]) {
|
||||
tabs.splice(i,1);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'PPageProfile',
|
||||
props: {
|
||||
tab: String,
|
||||
},
|
||||
data() {
|
||||
const isDemo = this.$config.get("demo");
|
||||
const isPublic = this.$config.get("public");
|
||||
const tabs = [
|
||||
{
|
||||
'name': 'settings-account',
|
||||
'component': Account,
|
||||
'label': this.$gettext('Account'),
|
||||
'class': '',
|
||||
'path': '/account',
|
||||
'icon': 'person',
|
||||
'public': false,
|
||||
'admin': true,
|
||||
'demo': true,
|
||||
},
|
||||
];
|
||||
|
||||
if(isDemo) {
|
||||
initTabs("demo", tabs);
|
||||
} else if(isPublic) {
|
||||
initTabs("public", tabs);
|
||||
}
|
||||
|
||||
let active = 0;
|
||||
|
||||
if (typeof this.tab === 'string' && this.tab !== '') {
|
||||
active = tabs.findIndex((t) => t.name === this.tab);
|
||||
}
|
||||
|
||||
return {
|
||||
tabs: tabs,
|
||||
demo: isDemo,
|
||||
public: isPublic,
|
||||
readonly: this.$config.get("readonly"),
|
||||
active: active,
|
||||
rtl: this.$rtl,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changePath: function (path) {
|
||||
if (this.$route.path !== path) {
|
||||
this.$router.replace(path);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -31,7 +31,6 @@ import General from "pages/settings/general.vue";
|
|||
import Library from "pages/settings/library.vue";
|
||||
import Advanced from "pages/settings/advanced.vue";
|
||||
import Sync from "pages/settings/sync.vue";
|
||||
import Account from "pages/settings/account.vue";
|
||||
|
||||
function initTabs(flag, tabs) {
|
||||
let i = 0;
|
||||
|
@ -97,17 +96,6 @@ export default {
|
|||
'admin': true,
|
||||
'demo': true,
|
||||
},
|
||||
{
|
||||
'name': 'settings-account',
|
||||
'component': Account,
|
||||
'label': this.$gettext('Account'),
|
||||
'class': '',
|
||||
'path': '/settings/account',
|
||||
'icon': 'person',
|
||||
'public': false,
|
||||
'admin': true,
|
||||
'demo': true,
|
||||
},
|
||||
];
|
||||
|
||||
if(isDemo) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="p-tab p-settings-account">
|
||||
<v-form ref="form" dense class="form-password" accept-charset="UTF-8">
|
||||
<v-form v-if="!user.ExternalID" ref="form" dense class="form-password" accept-charset="UTF-8">
|
||||
<v-card flat tile class="ma-2 application">
|
||||
<v-card-actions>
|
||||
<v-layout wrap align-top>
|
||||
|
@ -68,6 +68,9 @@
|
|||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-form>
|
||||
<template v-else>
|
||||
<p class="mt-3 px-4">⚠️ Please head to your Identity Provider to manage your account details.</p>
|
||||
</template>
|
||||
|
||||
<p-about-footer></p-about-footer>
|
||||
</div>
|
||||
|
@ -82,6 +85,7 @@ export default {
|
|||
|
||||
return {
|
||||
demo: isDemo,
|
||||
user: this.$session.getUser(),
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
|
@ -89,6 +93,10 @@ export default {
|
|||
rtl: this.$rtl,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
console.log("this.$session.getUser()");
|
||||
console.log(this.$session.getUser());
|
||||
},
|
||||
methods: {
|
||||
disabled() {
|
||||
return (this.demo || this.busy || this.oldPassword === "" || this.newPassword.length < 6 || (this.newPassword !== this.confirmPassword));
|
||||
|
|
|
@ -44,13 +44,26 @@ import About from "pages/about/about.vue";
|
|||
import Feedback from "pages/about/feedback.vue";
|
||||
import License from "pages/about/license.vue";
|
||||
import Help from "pages/help.vue";
|
||||
import Profile from "pages/profile.vue";
|
||||
import { $gettext } from "common/vm";
|
||||
import { config, session } from "./session";
|
||||
import Acl, { Constants } from "./common/acl";
|
||||
|
||||
const c = window.__CONFIG__;
|
||||
const appName = c.name;
|
||||
const siteTitle = c.siteTitle ? c.siteTitle : c.name;
|
||||
|
||||
const aclActions = Constants.actions;
|
||||
const aclResources = Constants.resources;
|
||||
|
||||
const hasPermission = (resource, ...actions) => {
|
||||
const conf = config.values.acl ? config.values : c;
|
||||
if (conf.public) return true;
|
||||
const acl = new Acl(conf.acl);
|
||||
const role = session.getUser().getRole();
|
||||
return acl.accessAllowedAny(role, resource, ...actions);
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
name: "home",
|
||||
|
@ -200,6 +213,13 @@ export default [
|
|||
component: Photos,
|
||||
meta: { title: $gettext("Review"), auth: true },
|
||||
props: { staticFilter: { review: true } },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceReview, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "private",
|
||||
|
@ -207,6 +227,13 @@ export default [
|
|||
component: Photos,
|
||||
meta: { title: $gettext("Private"), auth: true },
|
||||
props: { staticFilter: { private: true } },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourcePrivate, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "archive",
|
||||
|
@ -214,6 +241,13 @@ export default [
|
|||
component: Photos,
|
||||
meta: { title: $gettext("Archive"), auth: true },
|
||||
props: { staticFilter: { archived: true } },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceArchive, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "places",
|
||||
|
@ -245,6 +279,13 @@ export default [
|
|||
path: "/library/files*",
|
||||
component: Files,
|
||||
meta: { title: $gettext("File Browser"), auth: true },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceLibrary, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hidden",
|
||||
|
@ -252,18 +293,41 @@ export default [
|
|||
component: Photos,
|
||||
meta: { title: $gettext("Hidden Files"), auth: true },
|
||||
props: { staticFilter: { hidden: true } },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceLibrary, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "errors",
|
||||
path: "/library/errors",
|
||||
component: Errors,
|
||||
meta: { title: appName, auth: true },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceLibrary, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "labels",
|
||||
path: "/labels",
|
||||
component: Labels,
|
||||
meta: { title: $gettext("Labels"), auth: true },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (
|
||||
hasPermission(aclResources.ResourceLabels, aclActions.ActionSearch, aclActions.ActionRead)
|
||||
) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "people",
|
||||
|
@ -271,12 +335,24 @@ export default [
|
|||
component: People,
|
||||
meta: { title: $gettext("People"), auth: true, background: "application-light" },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (
|
||||
!hasPermission(
|
||||
aclResources.ResourceSubjects,
|
||||
aclActions.ActionRead,
|
||||
aclActions.ActionSearch
|
||||
)
|
||||
) {
|
||||
next({ name: "home" });
|
||||
}
|
||||
if (!config || !from || !from.name || from.name.startsWith("people")) {
|
||||
next();
|
||||
} else {
|
||||
config.load().finally(() => {
|
||||
// Open new faces tab when there are no people.
|
||||
if (config.values.count.people === 0) {
|
||||
if (
|
||||
config.values.count.people === 0 &&
|
||||
hasPermission(aclResources.ResourceSubjects, aclActions.ActionUpdate)
|
||||
) {
|
||||
next({ name: "people_faces" });
|
||||
} else {
|
||||
next();
|
||||
|
@ -290,6 +366,13 @@ export default [
|
|||
path: "/people/new",
|
||||
component: People,
|
||||
meta: { title: $gettext("People"), auth: true, background: "application-light" },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceSubjects, aclActions.ActionUpdate)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "people" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "library",
|
||||
|
@ -297,6 +380,13 @@ export default [
|
|||
component: Library,
|
||||
meta: { title: $gettext("Library"), auth: true, background: "application-light" },
|
||||
props: { tab: "library-index" },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceLibrary, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "library_import",
|
||||
|
@ -304,6 +394,13 @@ export default [
|
|||
component: Library,
|
||||
meta: { title: $gettext("Library"), auth: true, background: "application-light" },
|
||||
props: { tab: "library-import" },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceLibrary, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "library_logs",
|
||||
|
@ -311,6 +408,13 @@ export default [
|
|||
component: Library,
|
||||
meta: { title: $gettext("Library"), auth: true, background: "application-light" },
|
||||
props: { tab: "library-logs" },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceLibrary, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "settings",
|
||||
|
@ -323,6 +427,13 @@ export default [
|
|||
background: "application-light",
|
||||
},
|
||||
props: { tab: "settings-general" },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceSettings, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "settings_library",
|
||||
|
@ -335,6 +446,13 @@ export default [
|
|||
background: "application-light",
|
||||
},
|
||||
props: { tab: "settings-library" },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceSettings, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "settings_sync",
|
||||
|
@ -347,18 +465,31 @@ export default [
|
|||
background: "application-light",
|
||||
},
|
||||
props: { tab: "settings-sync" },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceSettings, aclActions.ActionRead)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "settings_account",
|
||||
path: "/settings/account",
|
||||
component: Settings,
|
||||
name: "profile_account",
|
||||
path: "/account",
|
||||
component: Profile,
|
||||
meta: {
|
||||
title: $gettext("Settings"),
|
||||
auth: true,
|
||||
settings: true,
|
||||
background: "application-light",
|
||||
},
|
||||
props: { tab: "settings-account" },
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (hasPermission(aclResources.ResourceUsers, aclActions.ActionUpdateSelf)) {
|
||||
next();
|
||||
} else {
|
||||
next({ name: "home" });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "settings_advanced",
|
||||
|
|
|
@ -56,6 +56,7 @@ import VueInfiniteScroll from "vue-infinite-scroll";
|
|||
import Hls from "hls.js";
|
||||
import { $gettext, Mount } from "common/vm";
|
||||
import * as options from "./options/options";
|
||||
import { aclMixin } from "./common/acl";
|
||||
|
||||
// Initialize helpers
|
||||
const viewer = new Viewer();
|
||||
|
@ -90,6 +91,8 @@ Vue.prototype.$clipboard = Clipboard;
|
|||
Vue.prototype.$isMobile = isMobile;
|
||||
Vue.prototype.$rtl = rtl;
|
||||
|
||||
Vue.mixin(aclMixin);
|
||||
|
||||
// Register Vuetify
|
||||
Vue.use(Vuetify, { rtl, icons, theme });
|
||||
|
||||
|
|
208
frontend/tests/acceptance-new/albums.js
Normal file
208
frontend/tests/acceptance-new/albums.js
Normal file
|
@ -0,0 +1,208 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Album from "../page-model/album";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import PhotoViewer from "../page-model/photoviewer";
|
||||
import Page from "../page-model/page";
|
||||
import AlbumDialog from "../page-model/dialog-album";
|
||||
|
||||
fixture`Test albums`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const album = new Album();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const photoviewer = new PhotoViewer();
|
||||
const page = new Page();
|
||||
const albumdialog = new AlbumDialog();
|
||||
|
||||
test.meta("testID", "authentication-000")(
|
||||
"Time to start instance (will be marked as unstable)",
|
||||
async (t) => {
|
||||
await t.wait(5000);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "albums-001").meta({ type: "smoke" })(
|
||||
"Create/delete album on /albums",
|
||||
async (t) => {
|
||||
await menu.openPage("albums");
|
||||
const AlbumCount = await album.getAlbumCount("all");
|
||||
await toolbar.triggerToolbarAction("add");
|
||||
const AlbumCountAfterCreate = await album.getAlbumCount("all");
|
||||
const NewAlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
|
||||
await t.expect(AlbumCountAfterCreate).eql(AlbumCount + 1);
|
||||
|
||||
await album.selectAlbumFromUID(NewAlbumUid);
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
const AlbumCountAfterDelete = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterDelete).eql(AlbumCountAfterCreate - 1);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "albums-002").meta({ type: "smoke" })(
|
||||
"Create/delete album during add to album",
|
||||
async (t) => {
|
||||
await menu.openPage("albums");
|
||||
const AlbumCount = await album.getAlbumCount("all");
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("photo:true");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("image", 1);
|
||||
await photo.selectPhotoFromUID(SecondPhotoUid);
|
||||
await photo.selectPhotoFromUID(FirstPhotoUid);
|
||||
await contextmenu.triggerContextMenuAction("album", "NotYetExistingAlbum");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterCreation = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterCreation).eql(AlbumCount + 1);
|
||||
|
||||
await toolbar.search("NotYetExistingAlbum");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.selectAlbumFromUID(AlbumUid);
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterDelete = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterDelete).eql(AlbumCount);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "albums-003").meta({ type: "smoke" })("Update album details", async (t) => {
|
||||
await menu.openPage("albums");
|
||||
await toolbar.search("Holiday");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("Holiday");
|
||||
|
||||
await t.click(page.cardTitle.nth(0)).typeText(albumdialog.title, "Animals", { replace: true });
|
||||
|
||||
await t.expect(albumdialog.description.value).eql("").expect(albumdialog.category.value).eql("");
|
||||
|
||||
await t
|
||||
.typeText(albumdialog.description, "All my animals")
|
||||
.typeText(albumdialog.category, "Pets")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.dialogSave);
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("Animals");
|
||||
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
await toolbar.triggerToolbarAction("edit");
|
||||
await t.typeText(albumdialog.title, "Holiday", { replace: true });
|
||||
|
||||
await t
|
||||
.expect(albumdialog.description.value)
|
||||
.eql("All my animals")
|
||||
.expect(albumdialog.category.value)
|
||||
.eql("Pets");
|
||||
|
||||
await t
|
||||
.click(albumdialog.description)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.category)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.dialogSave);
|
||||
await menu.openPage("albums");
|
||||
|
||||
await t
|
||||
.expect(Selector("div").withText("Holiday").visible)
|
||||
.ok()
|
||||
.expect(Selector("div").withText("Animals").exists)
|
||||
.notOk();
|
||||
});
|
||||
|
||||
test.meta("testID", "albums-004").meta({ type: "smoke" })(
|
||||
"Add/Remove Photos to/from album",
|
||||
async (t) => {
|
||||
await menu.openPage("albums");
|
||||
await toolbar.search("Holiday");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCount = await photo.getPhotoCount("all");
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("photo:true");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("image", 1);
|
||||
await photo.selectPhotoFromUID(SecondPhotoUid);
|
||||
await photoviewer.openPhotoViewer("uid", FirstPhotoUid);
|
||||
await photoviewer.triggerPhotoViewerAction("select");
|
||||
await photoviewer.triggerPhotoViewerAction("close");
|
||||
await contextmenu.triggerContextMenuAction("album", "Holiday");
|
||||
await menu.openPage("albums");
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCountAfterAdd = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterAdd).eql(PhotoCount + 2);
|
||||
|
||||
await photo.selectPhotoFromUID(FirstPhotoUid);
|
||||
await photo.selectPhotoFromUID(SecondPhotoUid);
|
||||
await contextmenu.triggerContextMenuAction("remove", "");
|
||||
const PhotoCountAfterRemove = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterRemove).eql(PhotoCountAfterAdd - 2);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "albums-005")("Use album search and filters", async (t) => {
|
||||
await menu.openPage("albums");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await toolbar.search("category:Family");
|
||||
} else {
|
||||
await toolbar.setFilter("category", "Family");
|
||||
}
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("Christmas");
|
||||
const AlbumCount = await album.getAlbumCount("all");
|
||||
await t.expect(AlbumCount).eql(1);
|
||||
|
||||
if (t.browser.platform === "mobile") {
|
||||
} else {
|
||||
await toolbar.setFilter("category", "All Categories");
|
||||
}
|
||||
|
||||
await toolbar.search("Holiday");
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("Holiday");
|
||||
const AlbumCount2 = await album.getAlbumCount("all");
|
||||
await t.expect(AlbumCount2).eql(1);
|
||||
});
|
||||
|
||||
test.meta("testID", "albums-006")("Test album autocomplete", async (t) => {
|
||||
await toolbar.search("photo:true");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
await photo.selectPhotoFromUID(FirstPhotoUid);
|
||||
await contextmenu.openContextMenu();
|
||||
await t.click(Selector("button.action-album")).click(Selector(".input-album input"));
|
||||
|
||||
await t
|
||||
.expect(page.selectOption.withText("Holiday").visible)
|
||||
.ok()
|
||||
.expect(page.selectOption.withText("Christmas").visible)
|
||||
.ok();
|
||||
|
||||
await t.typeText(Selector(".input-album input"), "C", { replace: true });
|
||||
|
||||
await t
|
||||
.expect(page.selectOption.withText("Holiday").visible)
|
||||
.notOk()
|
||||
.expect(page.selectOption.withText("Christmas").visible)
|
||||
.ok()
|
||||
.expect(page.selectOption.withText("C").visible)
|
||||
.ok();
|
||||
});
|
||||
|
||||
test.meta("testID", "albums-007").meta({ type: "smoke" })(
|
||||
"Create, Edit, delete sharing link",
|
||||
async (t) => {
|
||||
await page.testCreateEditDeleteSharingLink("albums");
|
||||
}
|
||||
);
|
146
frontend/tests/acceptance-new/calendar.js
Normal file
146
frontend/tests/acceptance-new/calendar.js
Normal file
|
@ -0,0 +1,146 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Album from "../page-model/album";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Page from "../page-model/page";
|
||||
import AlbumDialog from "../page-model/dialog-album";
|
||||
|
||||
fixture`Test calendar`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const album = new Album();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const page = new Page();
|
||||
const albumdialog = new AlbumDialog();
|
||||
|
||||
test.meta("testID", "calendar-001").meta({ type: "smoke" })("View calendar", async (t) => {
|
||||
await menu.openPage("calendar");
|
||||
|
||||
await t
|
||||
.expect(Selector("a").withText("May 2019").visible)
|
||||
.ok()
|
||||
.expect(Selector("a").withText("October 2019").visible)
|
||||
.ok();
|
||||
});
|
||||
|
||||
test.meta("testID", "calendar-002")("Update calendar details", async (t) => {
|
||||
await menu.openPage("calendar");
|
||||
await toolbar.search("March 2014");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("March 2014");
|
||||
|
||||
await t.click(page.cardTitle.nth(0)).typeText(albumdialog.location, "Snow", { replace: true });
|
||||
|
||||
await t.expect(albumdialog.description.value).eql("").expect(albumdialog.category.value).eql("");
|
||||
|
||||
await t
|
||||
.typeText(albumdialog.description, "We went to ski")
|
||||
.typeText(albumdialog.category, "Mountains")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.dialogSave);
|
||||
|
||||
await t
|
||||
.expect(page.cardTitle.nth(0).innerText)
|
||||
.contains("March 2014")
|
||||
.expect(page.cardDescription.nth(0).innerText)
|
||||
.contains("We went to ski")
|
||||
.expect(Selector("div.caption").nth(1).innerText)
|
||||
.contains("Mountains")
|
||||
.expect(Selector("div.caption").nth(2).innerText)
|
||||
.contains("Snow");
|
||||
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await t.expect(toolbar.toolbarTitle.innerText).contains("March 2014");
|
||||
await t.expect(toolbar.toolbarDescription.innerText).contains("We went to ski");
|
||||
await menu.openPage("calendar");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await toolbar.search("category:Mountains");
|
||||
} else {
|
||||
await toolbar.setFilter("category", "Mountains");
|
||||
}
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("March 2014");
|
||||
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
await toolbar.triggerToolbarAction("edit");
|
||||
|
||||
await t
|
||||
.expect(albumdialog.description.value)
|
||||
.eql("We went to ski")
|
||||
.expect(albumdialog.category.value)
|
||||
.eql("Mountains")
|
||||
.expect(albumdialog.location.value)
|
||||
.eql("Snow");
|
||||
|
||||
await t
|
||||
.click(albumdialog.category)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.description)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.location)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.dialogSave);
|
||||
await menu.openPage("calendar");
|
||||
await toolbar.search("March 2014");
|
||||
|
||||
await t
|
||||
.expect(page.cardDescription.innerText)
|
||||
.notContains("We went to ski")
|
||||
.expect(Selector("div.caption").nth(0).innerText)
|
||||
.notContains("Snow");
|
||||
});
|
||||
|
||||
test.meta("testID", "calendar-003")("Create, Edit, delete sharing link for calendar", async (t) => {
|
||||
await page.testCreateEditDeleteSharingLink("calendar");
|
||||
});
|
||||
|
||||
test.meta("testID", "calendar-004").meta({ type: "smoke" })(
|
||||
"Create/delete album-clone from calendar",
|
||||
async (t) => {
|
||||
await menu.openPage("albums");
|
||||
const AlbumCount = await album.getAlbumCount("all");
|
||||
await menu.openPage("calendar");
|
||||
const SecondCalendarUid = await album.getNthAlbumUid("all", 1);
|
||||
await album.openAlbumWithUid(SecondCalendarUid);
|
||||
const PhotoCountInCalendar = await photo.getPhotoCount("all");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("image", 1);
|
||||
await menu.openPage("calendar");
|
||||
await album.selectAlbumFromUID(SecondCalendarUid);
|
||||
await contextmenu.triggerContextMenuAction("clone", "NotYetExistingAlbumForCalendar");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterCreation = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterCreation).eql(AlbumCount + 1);
|
||||
|
||||
await toolbar.search("NotYetExistingAlbumForCalendar");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCountInAlbum = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountInAlbum).eql(PhotoCountInCalendar);
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
await menu.openPage("albums");
|
||||
await album.selectAlbumFromUID(AlbumUid);
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterDelete = await album.getAlbumCount("all");
|
||||
await t.expect(AlbumCountAfterDelete).eql(AlbumCount);
|
||||
await menu.openPage("calendar");
|
||||
await album.openAlbumWithUid(SecondCalendarUid);
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
}
|
||||
);
|
61
frontend/tests/acceptance-new/components.js
Normal file
61
frontend/tests/acceptance-new/components.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
|
||||
fixture`Test components`.page`${testcafeconfig.url}`;
|
||||
|
||||
const toolbar = new Toolbar();
|
||||
|
||||
test.meta("testID", "components-001").meta({ type: "smoke" })("Test filter options", async (t) => {
|
||||
await t.expect(Selector("body").withText("object Object").exists).notOk();
|
||||
});
|
||||
|
||||
test.meta("testID", "components-002").meta({ type: "smoke" })("Fullscreen mode", async (t) => {
|
||||
await t.click(Selector("div.v-image__image").nth(0));
|
||||
|
||||
if (await Selector("#photo-viewer").visible) {
|
||||
await t
|
||||
.expect(Selector("#photo-viewer").visible)
|
||||
.ok()
|
||||
.expect(Selector("img.pswp__img").visible)
|
||||
.ok();
|
||||
} else {
|
||||
await t.expect(Selector("div.video-viewer").visible).ok();
|
||||
}
|
||||
});
|
||||
|
||||
test.meta("testID", "components-003").meta({ type: "smoke" })("Mosaic view", async (t) => {
|
||||
await toolbar.setFilter("view", "Mosaic");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.v-image__image").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-photo-mosaic").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.is-photo div.caption").exists)
|
||||
.notOk()
|
||||
.expect(Selector("#photo-viewer").visible)
|
||||
.notOk();
|
||||
});
|
||||
|
||||
test.meta("testID", "components-004")("List view", async (t) => {
|
||||
await toolbar.setFilter("view", "List");
|
||||
|
||||
await t
|
||||
.expect(Selector("table.v-datatable").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.list-view").visible)
|
||||
.ok();
|
||||
});
|
||||
|
||||
test.meta("testID", "components-005").meta({ type: "smoke" })("Card view", async (t) => {
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.v-image__image").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.is-photo div.caption").visible)
|
||||
.ok()
|
||||
.expect(Selector("#photo-viewer").visible)
|
||||
.notOk();
|
||||
});
|
160
frontend/tests/acceptance-new/folders.js
Normal file
160
frontend/tests/acceptance-new/folders.js
Normal file
|
@ -0,0 +1,160 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Album from "../page-model/album";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Page from "../page-model/page";
|
||||
import AlbumDialog from "../page-model/dialog-album";
|
||||
|
||||
fixture`Test folders`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const album = new Album();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const page = new Page();
|
||||
const albumdialog = new AlbumDialog();
|
||||
|
||||
test.meta("testID", "folders-001").meta({ type: "smoke" })("View folders", async (t) => {
|
||||
await menu.openPage("folders");
|
||||
|
||||
await t
|
||||
.expect(Selector("a").withText("BotanicalGarden").visible)
|
||||
.ok()
|
||||
.expect(Selector("a").withText("Kanada").visible)
|
||||
.ok()
|
||||
.expect(Selector("a").withText("KorsikaAdventure").visible)
|
||||
.ok();
|
||||
});
|
||||
|
||||
test.meta("testID", "folders-002")("Update folder details", async (t) => {
|
||||
await menu.openPage("folders");
|
||||
await toolbar.search("Kanada");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("Kanada");
|
||||
|
||||
await t.click(page.cardTitle.nth(0));
|
||||
|
||||
await t
|
||||
.expect(albumdialog.title.value)
|
||||
.eql("Kanada")
|
||||
.expect(albumdialog.location.value)
|
||||
.eql("")
|
||||
.expect(albumdialog.description.value)
|
||||
.eql("")
|
||||
.expect(albumdialog.category.value)
|
||||
.eql("");
|
||||
|
||||
await t
|
||||
.typeText(albumdialog.title, "MyFolder", { replace: true })
|
||||
.typeText(albumdialog.location, "USA", { replace: true })
|
||||
.typeText(albumdialog.description, "Last holiday")
|
||||
.typeText(albumdialog.category, "Mountains")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.dialogSave);
|
||||
|
||||
await t
|
||||
.expect(page.cardTitle.nth(0).innerText)
|
||||
.contains("MyFolder")
|
||||
.expect(page.cardDescription.nth(0).innerText)
|
||||
.contains("Last holiday")
|
||||
.expect(Selector("div.caption").nth(1).innerText)
|
||||
.contains("Mountains")
|
||||
.expect(Selector("div.caption").nth(2).innerText)
|
||||
.contains("USA");
|
||||
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await t
|
||||
.expect(toolbar.toolbarDescription.nth(0).innerText)
|
||||
.contains("Last holiday")
|
||||
.expect(toolbar.toolbarTitle.nth(0).innerText)
|
||||
.contains("MyFolder");
|
||||
|
||||
await menu.openPage("folders");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await toolbar.search("category:Mountains");
|
||||
} else {
|
||||
await toolbar.setFilter("category", "Mountains");
|
||||
}
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("MyFolder");
|
||||
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
await toolbar.triggerToolbarAction("edit");
|
||||
|
||||
await t
|
||||
.expect(albumdialog.description.value)
|
||||
.eql("Last holiday")
|
||||
.expect(albumdialog.category.value)
|
||||
.eql("Mountains")
|
||||
.expect(albumdialog.location.value)
|
||||
.eql("USA");
|
||||
|
||||
await t
|
||||
.typeText(albumdialog.title, "Kanada", { replace: true })
|
||||
.click(albumdialog.category)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.description)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.location)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.dialogSave);
|
||||
await menu.openPage("folders");
|
||||
await toolbar.search("Kanada");
|
||||
|
||||
await t
|
||||
.expect(page.cardTitle.nth(0).innerText)
|
||||
.contains("Kanada")
|
||||
.expect(page.cardDescription.nth(0).innerText)
|
||||
.notContains("We went to ski")
|
||||
.expect(Selector("div.caption").nth(0).innerText)
|
||||
.notContains("USA");
|
||||
});
|
||||
|
||||
test.meta("testID", "folders-003")("Create, Edit, delete sharing link", async (t) => {
|
||||
await page.testCreateEditDeleteSharingLink("folders");
|
||||
});
|
||||
|
||||
test.meta("testID", "folders-004")("Create/delete album-clone from folder", async (t) => {
|
||||
await menu.openPage("albums");
|
||||
const AlbumCount = await album.getAlbumCount("all");
|
||||
await menu.openPage("folders");
|
||||
const ThirdFolderUid = await album.getNthAlbumUid("all", 2);
|
||||
await album.openAlbumWithUid(ThirdFolderUid);
|
||||
const PhotoCountInFolder = await photo.getPhotoCount("all");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
await menu.openPage("folders");
|
||||
await album.selectAlbumFromUID(ThirdFolderUid);
|
||||
await contextmenu.triggerContextMenuAction("clone", "NotYetExistingAlbumForFolder");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterCreation = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterCreation).eql(AlbumCount + 1);
|
||||
|
||||
await toolbar.search("NotYetExistingAlbumForFolder");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCountInAlbum = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountInAlbum).eql(PhotoCountInFolder);
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await menu.openPage("albums");
|
||||
await album.selectAlbumFromUID(AlbumUid);
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterDelete = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterDelete).eql(AlbumCount);
|
||||
|
||||
await menu.openPage("folders");
|
||||
await album.openAlbumWithUid(ThirdFolderUid);
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
});
|
209
frontend/tests/acceptance-new/labels.js
Normal file
209
frontend/tests/acceptance-new/labels.js
Normal file
|
@ -0,0 +1,209 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Album from "../page-model/album";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Page from "../page-model/page";
|
||||
import Label from "../page-model/label";
|
||||
import PhotoEdit from "../page-model/photo-edit";
|
||||
|
||||
fixture`Test labels`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const album = new Album();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const page = new Page();
|
||||
const label = new Label();
|
||||
const photoedit = new PhotoEdit();
|
||||
|
||||
test.meta("testID", "labels-001").meta({ type: "smoke" })(
|
||||
"Remove/Activate Add/Delete Label from photo",
|
||||
async (t) => {
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("beacon");
|
||||
const LabelBeaconUid = await label.getNthLabeltUid(0);
|
||||
await label.openLabelWithUid(LabelBeaconUid);
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
const PhotoBeaconUid = await photo.getNthPhotoUid("all", 0);
|
||||
await t.click(page.cardTitle.withAttribute("data-uid", PhotoBeaconUid));
|
||||
const PhotoKeywords = await photoedit.keywords.value;
|
||||
|
||||
await t.expect(PhotoKeywords).contains("beacon");
|
||||
|
||||
await t
|
||||
.click(photoedit.labelsTab)
|
||||
.click(photoedit.removeLabel)
|
||||
.typeText(photoedit.inputLabelName, "Test")
|
||||
.click(Selector(photoedit.addLabel))
|
||||
.click(photoedit.detailsTab);
|
||||
const PhotoKeywordsAfterEdit = await photoedit.keywords.value;
|
||||
|
||||
await t
|
||||
.expect(PhotoKeywordsAfterEdit)
|
||||
.contains("test")
|
||||
.expect(PhotoKeywordsAfterEdit)
|
||||
.notContains("beacon");
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("beacon");
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await toolbar.search("test");
|
||||
const LabelTest = await label.getNthLabeltUid(0);
|
||||
await label.openLabelWithUid(LabelTest);
|
||||
await t
|
||||
.click(page.cardTitle.withAttribute("data-uid", PhotoBeaconUid))
|
||||
.click(photoedit.labelsTab)
|
||||
.click(photoedit.deleteLabel)
|
||||
.click(photoedit.activateLabel)
|
||||
.click(photoedit.detailsTab);
|
||||
const PhotoKeywordsAfterUndo = await photoedit.keywords.value;
|
||||
|
||||
await t
|
||||
.expect(PhotoKeywordsAfterUndo)
|
||||
.contains("beacon")
|
||||
.expect(PhotoKeywordsAfterUndo)
|
||||
.notContains("test");
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("test");
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await toolbar.search("beacon");
|
||||
await album.checkAlbumVisibility(LabelBeaconUid, true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "labels-002")("Toggle between important and all labels", async (t) => {
|
||||
await menu.openPage("labels");
|
||||
const ImportantLabelsCount = await label.getLabelCount();
|
||||
await toolbar.triggerToolbarAction("show-all");
|
||||
const AllLabelsCount = await label.getLabelCount();
|
||||
|
||||
await t.expect(AllLabelsCount).gt(ImportantLabelsCount);
|
||||
|
||||
await toolbar.triggerToolbarAction("show-important");
|
||||
const ImportantLabelsCount2 = await label.getLabelCount();
|
||||
|
||||
await t.expect(ImportantLabelsCount).eql(ImportantLabelsCount2);
|
||||
});
|
||||
|
||||
test.meta("testID", "labels-003")("Rename Label", async (t) => {
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("zebra");
|
||||
const LabelZebraUid = await label.getNthLabeltUid(0);
|
||||
await label.openNthLabel(0);
|
||||
const FirstPhotoZebraUid = await photo.getNthPhotoUid("all", 0);
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
await t.click(page.cardTitle.withAttribute("data-uid", FirstPhotoZebraUid));
|
||||
const FirstPhotoTitle = await photoedit.title.value;
|
||||
const FirstPhotoKeywords = await photoedit.keywords.value;
|
||||
|
||||
await t.expect(FirstPhotoTitle).contains("Zebra").expect(FirstPhotoKeywords).contains("zebra");
|
||||
|
||||
await t
|
||||
.click(photoedit.labelsTab)
|
||||
.click(photoedit.openInlineEdit)
|
||||
.typeText(photoedit.inputLabelRename, "Horse", { replace: true })
|
||||
.pressKey("enter")
|
||||
.click(photoedit.detailsTab);
|
||||
const FirstPhotoTitleAfterEdit = await photoedit.title.value;
|
||||
const FirstPhotoKeywordsAfterEdit = await photoedit.keywords.value;
|
||||
|
||||
await t
|
||||
.expect(FirstPhotoTitleAfterEdit)
|
||||
.contains("Horse")
|
||||
.expect(FirstPhotoKeywordsAfterEdit)
|
||||
.contains("horse")
|
||||
.expect(FirstPhotoTitleAfterEdit)
|
||||
.notContains("Zebra");
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("horse");
|
||||
await album.checkAlbumVisibility(LabelZebraUid, true);
|
||||
await label.openLabelWithUid(LabelZebraUid);
|
||||
await photo.checkPhotoVisibility(FirstPhotoZebraUid, true);
|
||||
await t
|
||||
.click(page.cardTitle.withAttribute("data-uid", FirstPhotoZebraUid))
|
||||
.click(photoedit.labelsTab)
|
||||
.click(photoedit.openInlineEdit)
|
||||
.typeText(photoedit.inputLabelRename, "Zebra", { replace: true })
|
||||
.pressKey("enter")
|
||||
.click(photoedit.dialogClose);
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("horse");
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
});
|
||||
|
||||
test.meta("testID", "labels-003")("Add label to album", async (t) => {
|
||||
await menu.openPage("albums");
|
||||
await toolbar.search("Christmas");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCount = await photo.getPhotoCount("all");
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("landscape");
|
||||
const LabelLandscape = await label.getNthLabeltUid(1);
|
||||
await label.openLabelWithUid(LabelLandscape);
|
||||
const FirstPhotoLandscape = await photo.getNthPhotoUid("all", 0);
|
||||
const SecondPhotoLandscape = await photo.getNthPhotoUid("all", 1);
|
||||
const ThirdPhotoLandscape = await photo.getNthPhotoUid("all", 2);
|
||||
const FourthPhotoLandscape = await photo.getNthPhotoUid("all", 3);
|
||||
const FifthPhotoLandscape = await photo.getNthPhotoUid("all", 4);
|
||||
const SixthPhotoLandscape = await photo.getNthPhotoUid("all", 5);
|
||||
await menu.openPage("labels");
|
||||
await label.triggerHoverAction("uid", LabelLandscape, "select");
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.triggerContextMenuAction("album", "Christmas");
|
||||
await menu.openPage("albums");
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCountAfterAdd = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterAdd).eql(PhotoCount + 6);
|
||||
|
||||
await photo.triggerHoverAction("uid", FirstPhotoLandscape, "select");
|
||||
await photo.triggerHoverAction("uid", SecondPhotoLandscape, "select");
|
||||
await photo.triggerHoverAction("uid", ThirdPhotoLandscape, "select");
|
||||
await photo.triggerHoverAction("uid", FourthPhotoLandscape, "select");
|
||||
await photo.triggerHoverAction("uid", FifthPhotoLandscape, "select");
|
||||
await photo.triggerHoverAction("uid", SixthPhotoLandscape, "select");
|
||||
await contextmenu.triggerContextMenuAction("remove", "");
|
||||
const PhotoCountAfterDelete = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterDelete).eql(PhotoCountAfterAdd - 6);
|
||||
});
|
||||
|
||||
test.meta("testID", "labels-004")("Delete label", async (t) => {
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("dome");
|
||||
const LabelDomeUid = await label.getNthLabeltUid(0);
|
||||
await label.openLabelWithUid(LabelDomeUid);
|
||||
const FirstPhotoDomeUid = await photo.getNthPhotoUid("all", 0);
|
||||
await menu.openPage("labels");
|
||||
await label.triggerHoverAction("uid", LabelDomeUid, "select");
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await toolbar.search("dome");
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await menu.openPage("browse");
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
await t
|
||||
.click(page.cardTitle.withAttribute("data-uid", FirstPhotoDomeUid))
|
||||
.click(photoedit.labelsTab);
|
||||
|
||||
await t.expect(Selector("td").withText("No labels found").visible).ok();
|
||||
|
||||
await t.typeText(photoedit.inputLabelName, "Dome").click(photoedit.addLabel);
|
||||
});
|
37
frontend/tests/acceptance-new/library/import.js
Normal file
37
frontend/tests/acceptance-new/library/import.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "../testcafeconfig";
|
||||
import Menu from "../../page-model/menu";
|
||||
import Toolbar from "../../page-model/toolbar";
|
||||
import Page from "../../page-model/page";
|
||||
import Library from "../../page-model/library";
|
||||
|
||||
fixture`Import file from folder`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const toolbar = new Toolbar();
|
||||
const page = new Page();
|
||||
const library = new Library();
|
||||
|
||||
test.meta("testID", "library-import-001").meta({ type: "smoke" })(
|
||||
"Import files from folder using copy",
|
||||
async (t) => {
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("bakery");
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await menu.openPage("library");
|
||||
await t
|
||||
.click(library.importTab)
|
||||
.typeText(library.openImportFolderSelect, "/B", { replace: true })
|
||||
.click(page.selectOption.nth(0))
|
||||
.click(library.import)
|
||||
//TODO replace wait
|
||||
.wait(60000);
|
||||
await menu.openPage("labels");
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
await toolbar.search("bakery");
|
||||
|
||||
await t.expect(Selector(".is-label").visible).ok();
|
||||
}
|
||||
);
|
132
frontend/tests/acceptance-new/library/index.js
Normal file
132
frontend/tests/acceptance-new/library/index.js
Normal file
|
@ -0,0 +1,132 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "../testcafeconfig";
|
||||
import Menu from "../../page-model/menu";
|
||||
import Toolbar from "../../page-model/toolbar";
|
||||
import Photo from "../../page-model/photo";
|
||||
import Page from "../../page-model/page";
|
||||
import Library from "../../page-model/library";
|
||||
import Album from "../../page-model/album";
|
||||
|
||||
fixture`Test index`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const toolbar = new Toolbar();
|
||||
const photo = new Photo();
|
||||
const page = new Page();
|
||||
const library = new Library();
|
||||
const album = new Album();
|
||||
|
||||
test.meta("testID", "library-index-001").meta({ type: "smoke" })(
|
||||
"Index files from folder",
|
||||
async (t) => {
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("cheetah");
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await menu.openPage("moments");
|
||||
const MomentCount = await album.getAlbumCount("all");
|
||||
await menu.openPage("calendar");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.navigateTo("/calendar?q=December%202013");
|
||||
} else {
|
||||
await toolbar.search("December 2013");
|
||||
}
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await menu.openPage("folders");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.navigateTo("/folders?q=moment");
|
||||
} else {
|
||||
await toolbar.search("Moment");
|
||||
}
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await menu.openPage("states");
|
||||
if (t.browser.platform === "mobile") {
|
||||
console.log(t.browser.platform);
|
||||
await t.navigateTo("/states?q=KwaZulu");
|
||||
} else {
|
||||
await toolbar.search("KwaZulu");
|
||||
}
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await menu.openPage("originals");
|
||||
await t.click(Selector(".is-folder").withText("moment"));
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await menu.openPage("monochrome");
|
||||
const MonochromeCount = await photo.getPhotoCount("all");
|
||||
await menu.openPage("library");
|
||||
await t
|
||||
.click(library.indexTab)
|
||||
.click(library.indexFolderSelect)
|
||||
.click(page.selectOption.withText("/moment"))
|
||||
.click(library.index)
|
||||
//TODO replace wait
|
||||
.wait(50000);
|
||||
|
||||
await t.expect(Selector("span").withText("Done.").visible, { timeout: 60000 }).ok();
|
||||
|
||||
await menu.openPage("labels");
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
await toolbar.search("cheetah");
|
||||
|
||||
await t.expect(Selector(".is-label").visible).ok();
|
||||
|
||||
await menu.openPage("moments");
|
||||
const MomentCountAfterIndex = await album.getAlbumCount("all");
|
||||
|
||||
await t
|
||||
.expect(MomentCountAfterIndex)
|
||||
.gt(MomentCount)
|
||||
.click(Selector("a").withText("South Africa 2013"))
|
||||
.expect(Selector(".is-photo").visible)
|
||||
.ok();
|
||||
|
||||
await menu.openPage("calendar");
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
if (t.browser.platform === "mobile") {
|
||||
console.log(t.browser.platform);
|
||||
await t.navigateTo("/calendar?q=December%202013");
|
||||
} else {
|
||||
await toolbar.search("December 2013");
|
||||
}
|
||||
|
||||
await t.expect(Selector(".is-album").visible).ok();
|
||||
|
||||
await menu.openPage("folders");
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
if (t.browser.platform === "mobile") {
|
||||
console.log(t.browser.platform);
|
||||
await t.navigateTo("/folders?q=moment");
|
||||
} else {
|
||||
await toolbar.search("Moment");
|
||||
}
|
||||
|
||||
await t.expect(Selector(".is-album", { timeout: 15000 }).visible).ok();
|
||||
|
||||
await menu.openPage("states");
|
||||
if (t.browser.platform === "mobile") {
|
||||
console.log(t.browser.platform);
|
||||
await t.navigateTo("/states?q=KwaZulu");
|
||||
} else {
|
||||
await toolbar.search("KwaZulu");
|
||||
}
|
||||
|
||||
await t.expect(Selector(".is-album").visible).ok();
|
||||
|
||||
await menu.openPage("originals");
|
||||
|
||||
await t.expect(Selector(".is-folder").withText("moment").visible, { timeout: 60000 }).ok();
|
||||
|
||||
await menu.openPage("monochrome");
|
||||
const MonochromeCountAfterIndex = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(MonochromeCountAfterIndex).gt(MonochromeCount);
|
||||
}
|
||||
);
|
149
frontend/tests/acceptance-new/moment.js
Normal file
149
frontend/tests/acceptance-new/moment.js
Normal file
|
@ -0,0 +1,149 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Album from "../page-model/album";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Page from "../page-model/page";
|
||||
import AlbumDialog from "../page-model/dialog-album";
|
||||
|
||||
fixture`Test moments`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const album = new Album();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const page = new Page();
|
||||
const albumdialog = new AlbumDialog();
|
||||
|
||||
test.meta("testID", "moments-001")("Update moment details", async (t) => {
|
||||
await menu.openPage("moments");
|
||||
await toolbar.search("Nature");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("Nature");
|
||||
|
||||
await t.click(page.cardTitle.nth(0));
|
||||
|
||||
await t
|
||||
.expect(albumdialog.title.value)
|
||||
.eql("Nature & Landscape")
|
||||
.expect(albumdialog.location.value)
|
||||
.eql("")
|
||||
.expect(albumdialog.description.value)
|
||||
.eql("")
|
||||
.expect(albumdialog.category.value)
|
||||
.eql("");
|
||||
|
||||
await t
|
||||
.typeText(albumdialog.title, "Winter", { replace: true })
|
||||
.typeText(albumdialog.location, "Snow-Land", { replace: true })
|
||||
.typeText(albumdialog.description, "We went to ski")
|
||||
.typeText(albumdialog.category, "Mountains")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.dialogSave);
|
||||
|
||||
await t
|
||||
.expect(page.cardTitle.nth(0).innerText)
|
||||
.contains("Winter")
|
||||
.expect(page.cardDescription.nth(0).innerText)
|
||||
.contains("We went to ski")
|
||||
.expect(Selector("div.caption").nth(1).innerText)
|
||||
.contains("Mountains")
|
||||
.expect(Selector("div.caption").nth(2).innerText)
|
||||
.contains("Snow-Land");
|
||||
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await t.expect(toolbar.toolbarTitle.innerText).contains("Winter");
|
||||
await t.expect(toolbar.toolbarDescription.innerText).contains("We went to ski");
|
||||
|
||||
await menu.openPage("moments");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await toolbar.search("category:Mountains");
|
||||
} else {
|
||||
await toolbar.setFilter("category", "Mountains");
|
||||
}
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("Winter");
|
||||
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
await toolbar.triggerToolbarAction("edit");
|
||||
|
||||
await t
|
||||
.expect(albumdialog.description.value)
|
||||
.eql("We went to ski")
|
||||
.expect(albumdialog.category.value)
|
||||
.eql("Mountains")
|
||||
.expect(albumdialog.location.value)
|
||||
.eql("Snow-Land");
|
||||
|
||||
await t
|
||||
.typeText(albumdialog.title, "Nature & Landscape", { replace: true })
|
||||
.click(albumdialog.category)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.description)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.location)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.dialogSave);
|
||||
await menu.openPage("moments");
|
||||
await toolbar.search("Nature");
|
||||
|
||||
await t
|
||||
.expect(page.cardTitle.nth(0).innerText)
|
||||
.contains("Nature & Landscape")
|
||||
.expect(page.cardDescription.innerText)
|
||||
.notContains("We went to ski")
|
||||
.expect(Selector("div.caption").nth(0).innerText)
|
||||
.notContains("Snow-Land");
|
||||
});
|
||||
|
||||
test.meta("testID", "moments-002")("Create, Edit, delete sharing link for moment", async (t) => {
|
||||
await page.testCreateEditDeleteSharingLink("moments");
|
||||
});
|
||||
|
||||
test.meta("testID", "moments-003")("Create/delete album-clone from moment", async (t) => {
|
||||
await menu.openPage("albums");
|
||||
const AlbumCount = await album.getAlbumCount("all");
|
||||
await menu.openPage("moments");
|
||||
const FirstMomentUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(FirstMomentUid);
|
||||
const PhotoCountInMoment = await photo.getPhotoCount("all");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("image", 1);
|
||||
await menu.openPage("moments");
|
||||
await album.selectAlbumFromUID(FirstMomentUid);
|
||||
await contextmenu.triggerContextMenuAction("clone", "NotYetExistingAlbumForMoment");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterCreation = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterCreation).eql(AlbumCount + 1);
|
||||
|
||||
await toolbar.search("NotYetExistingAlbumForMoment");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCountInAlbum = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountInAlbum).eql(PhotoCountInMoment);
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
await menu.openPage("albums");
|
||||
await album.selectAlbumFromUID(AlbumUid);
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterDelete = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterDelete).eql(AlbumCount);
|
||||
|
||||
await menu.openPage("moments");
|
||||
await album.openAlbumWithUid(FirstMomentUid);
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
});
|
99
frontend/tests/acceptance-new/originals.js
Normal file
99
frontend/tests/acceptance-new/originals.js
Normal file
|
@ -0,0 +1,99 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Album from "../page-model/album";
|
||||
import Originals from "../page-model/originals";
|
||||
|
||||
fixture`Test files`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const photo = new Photo();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const album = new Album();
|
||||
const originals = new Originals();
|
||||
|
||||
test.meta("testID", "originals-001").meta({ type: "smoke" })("Navigate in originals", async (t) => {
|
||||
await menu.openPage("originals");
|
||||
await t.click(Selector("button").withText("Vacation"));
|
||||
const FirstItemInVacationName = await Selector("div.result", { timeout: 15000 }).nth(0).innerText;
|
||||
const KanadaFolderUid = await originals.getNthFolderUid(0);
|
||||
const SecondItemInVacationName = await Selector("div.result").nth(1).innerText;
|
||||
|
||||
await t
|
||||
.expect(FirstItemInVacationName)
|
||||
.contains("Kanada")
|
||||
.expect(SecondItemInVacationName)
|
||||
.contains("Korsika");
|
||||
|
||||
await originals.openFolderWithUid(KanadaFolderUid);
|
||||
|
||||
const FirstItemInKanadaName = await Selector("div.result").nth(0).innerText;
|
||||
const SecondItemInKanadaName = await Selector("div.result").nth(1).innerText;
|
||||
|
||||
await t
|
||||
.expect(FirstItemInKanadaName)
|
||||
.contains("BotanicalGarden")
|
||||
.expect(SecondItemInKanadaName)
|
||||
.contains("originals-001_2.jpg");
|
||||
|
||||
await t.click(Selector("button").withText("BotanicalGarden"));
|
||||
const FirstItemInBotanicalGardenName = await Selector("div.result", { timeout: 15000 }).nth(0)
|
||||
.innerText;
|
||||
await t.expect(FirstItemInBotanicalGardenName).contains("originals-001_1.jpg");
|
||||
await t.click(Selector('a[href="/library/files/Vacation"]'));
|
||||
const FolderCount = await originals.getFolderCount();
|
||||
|
||||
await t.expect(FolderCount).eql(2);
|
||||
});
|
||||
|
||||
test.meta("testID", "originals-002").meta({ type: "smoke" })(
|
||||
"Add original files to album",
|
||||
async (t) => {
|
||||
await menu.openPage("albums");
|
||||
await toolbar.search("KanadaVacation");
|
||||
|
||||
await t.expect(Selector("div.no-results").visible).ok();
|
||||
|
||||
await menu.openPage("originals");
|
||||
await t.click(Selector("button").withText("Vacation"));
|
||||
const KanadaFolderUid = await originals.getNthFolderUid(0);
|
||||
await originals.openFolderWithUid(KanadaFolderUid);
|
||||
const FilesCountInKanada = await originals.getFileCount();
|
||||
await t.click(Selector("button").withText("BotanicalGarden"));
|
||||
const FilesCountInKanadaSubfolder = await originals.getFileCount();
|
||||
await t.navigateTo("/library/files/Vacation");
|
||||
await originals.triggerHoverAction("is-folder", "uid", KanadaFolderUid, "select");
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.triggerContextMenuAction("album", "KanadaVacation");
|
||||
await menu.openPage("albums");
|
||||
await toolbar.search("KanadaVacation");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCountAfterAdd = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterAdd).eql(FilesCountInKanada + FilesCountInKanadaSubfolder);
|
||||
|
||||
await menu.openPage("albums");
|
||||
await album.triggerHoverAction("uid", AlbumUid, "select");
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "originals-003")("Download available in originals", async (t) => {
|
||||
await menu.openPage("originals");
|
||||
const FirstFile = await originals.getNthFileUid(0);
|
||||
await originals.triggerHoverAction("is-file", "uid", FirstFile, "select");
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.clearSelection();
|
||||
const FirstFolder = await originals.getNthFolderUid(0);
|
||||
await originals.triggerHoverAction("is-folder", "uid", FirstFolder, "select");
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.clearSelection();
|
||||
});
|
280
frontend/tests/acceptance-new/people.js
Normal file
280
frontend/tests/acceptance-new/people.js
Normal file
|
@ -0,0 +1,280 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Subject from "../page-model/subject";
|
||||
import PhotoEdit from "../page-model/photo-edit";
|
||||
|
||||
fixture`Test people`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const subject = new Subject();
|
||||
const photoedit = new PhotoEdit();
|
||||
|
||||
test.meta("testID", "people-001").meta({ type: "smoke" })(
|
||||
"Add name to new face and rename subject",
|
||||
async (t) => {
|
||||
await menu.openPage("people");
|
||||
await t.click(subject.newTab);
|
||||
await subject.triggerToolbarAction("reload", "");
|
||||
const FaceCount = await subject.getFaceCount();
|
||||
|
||||
await t.click(subject.recognizedTab);
|
||||
|
||||
const SubjectCount = await subject.getSubjectCount();
|
||||
await t.click(subject.newTab);
|
||||
const FirstFaceID = await subject.getNthFaceUid(0);
|
||||
await subject.openFaceWithUid(FirstFaceID);
|
||||
const PhotosInFaceCount = await photo.getPhotoCount("all");
|
||||
await menu.openPage("people");
|
||||
await t.click(subject.newTab);
|
||||
await subject.addNameToFace(FirstFaceID, "Jane Doe");
|
||||
await subject.triggerToolbarAction("reload");
|
||||
const FaceCountAfterAdd = await subject.getFaceCount();
|
||||
|
||||
await t.expect(FaceCountAfterAdd).eql(FaceCount - 1);
|
||||
|
||||
await t.click(subject.recognizedTab);
|
||||
await subject.checkFaceVisibility(FirstFaceID, false);
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(6000);
|
||||
const SubjectCountAfterAdd = await subject.getSubjectCount();
|
||||
|
||||
await t.expect(SubjectCountAfterAdd).eql(SubjectCount + 1);
|
||||
|
||||
await toolbar.search("Jane");
|
||||
const JaneUID = await subject.getNthSubjectUid(0);
|
||||
|
||||
await t
|
||||
.expect(Selector("a[data-uid=" + JaneUID + "] div.caption").innerText)
|
||||
.contains(PhotosInFaceCount.toString());
|
||||
|
||||
await subject.openSubjectWithUid(JaneUID);
|
||||
const PhotosInSubjectCount = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotosInFaceCount).eql(PhotosInSubjectCount);
|
||||
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
await photo.triggerHoverAction("nth", 1, "select");
|
||||
await photo.triggerHoverAction("nth", 2, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.peopleTab);
|
||||
|
||||
await t.expect(photoedit.inputName.nth(0).value).contains("Jane Doe");
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await menu.openPage("people");
|
||||
await subject.renameSubject(JaneUID, "Max Mu");
|
||||
|
||||
await t
|
||||
.expect(Selector("a[data-uid=" + JaneUID + "] div.v-card__title").innerText)
|
||||
.contains("Max Mu");
|
||||
|
||||
await subject.openSubjectWithUid(JaneUID);
|
||||
await t.eval(() => location.reload());
|
||||
await contextmenu.checkContextMenuCount("3");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.peopleTab);
|
||||
|
||||
await t.expect(photoedit.inputName.nth(0).value).contains("Max Mu");
|
||||
|
||||
await t.click(photoedit.dialogNext);
|
||||
|
||||
await t.expect(photoedit.inputName.nth(0).value).contains("Max Mu").click(photoedit.dialogNext);
|
||||
await t
|
||||
.expect(photoedit.inputName.nth(0).value)
|
||||
.contains("Max Mu")
|
||||
.click(photoedit.dialogClose);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await toolbar.search("person:max-mu");
|
||||
const PhotosInSubjectAfterRenameCount = await photo.getPhotoCount("all");
|
||||
await t.expect(PhotosInSubjectAfterRenameCount).eql(PhotosInSubjectCount);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "people-002").meta({ type: "smoke" })(
|
||||
"Add + Reject name on people tab",
|
||||
async (t) => {
|
||||
await menu.openPage("people");
|
||||
await t.click(subject.newTab);
|
||||
await subject.triggerToolbarAction("reload");
|
||||
const FirstFaceID = await subject.getNthFaceUid(0);
|
||||
await subject.addNameToFace(FirstFaceID, "Andrea Doe");
|
||||
await t.click(subject.recognizedTab);
|
||||
await toolbar.search("Andrea");
|
||||
const AndreaUID = await subject.getNthSubjectUid(0);
|
||||
await subject.openSubjectWithUid(AndreaUID);
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(5000);
|
||||
const PhotosInAndreaCount = await photo.getPhotoCount("all");
|
||||
await photo.triggerHoverAction("nth", 1, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t
|
||||
.click(photoedit.peopleTab)
|
||||
.expect(photoedit.inputName.nth(0).value)
|
||||
.eql("Andrea Doe")
|
||||
.click(photoedit.rejectName.nth(0));
|
||||
|
||||
await t.expect(photoedit.inputName.nth(0).value).eql("");
|
||||
|
||||
await t
|
||||
.typeText(photoedit.inputName.nth(0), "Nicole", { replace: true })
|
||||
.pressKey("enter")
|
||||
.click(photoedit.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(5000);
|
||||
const PhotosInAndreaAfterRejectCount = await photo.getPhotoCount("all");
|
||||
const Diff = PhotosInAndreaCount - PhotosInAndreaAfterRejectCount;
|
||||
await toolbar.search("person:nicole");
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(5000);
|
||||
const PhotosInNicoleCount = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(Diff).gte(PhotosInNicoleCount);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "people-003")("Test mark subject as favorite", async (t) => {
|
||||
await menu.openPage("people");
|
||||
const FirstSubjectUid = await subject.getNthSubjectUid(0);
|
||||
const SecondSubjectUid = await subject.getNthSubjectUid(1);
|
||||
await subject.triggerHoverAction("uid", SecondSubjectUid, "favorite");
|
||||
await subject.triggerToolbarAction("reload");
|
||||
const FirstSubjectUidAfterFavorite = await subject.getNthSubjectUid(0);
|
||||
|
||||
await t.expect(FirstSubjectUid).notEql(FirstSubjectUidAfterFavorite);
|
||||
await t.expect(SecondSubjectUid).eql(FirstSubjectUidAfterFavorite);
|
||||
|
||||
await subject.checkHoverActionState("uid", SecondSubjectUid, "favorite", true);
|
||||
await subject.triggerHoverAction("uid", SecondSubjectUid, "favorite");
|
||||
await subject.checkHoverActionState("uid", SecondSubjectUid, "favorite", false);
|
||||
});
|
||||
|
||||
test.meta("testID", "people-004")("Test new face autocomplete", async (t) => {
|
||||
await menu.openPage("people");
|
||||
await t.click(subject.newTab);
|
||||
await subject.triggerToolbarAction("reload");
|
||||
const FirstFaceID = await subject.getNthFaceUid(0);
|
||||
await t
|
||||
.expect(Selector("div.menuable__content__active").nth(0).visible)
|
||||
.notOk()
|
||||
.click(Selector("div[data-id=" + FirstFaceID + "] div.input-name input"))
|
||||
.typeText(Selector("div[data-id=" + FirstFaceID + "] div.input-name input"), "Otto");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.menuable__content__active").nth(0).withText("Otto Visible").visible)
|
||||
.ok();
|
||||
});
|
||||
|
||||
test.meta("testID", "people-005")("Remove face", async (t) => {
|
||||
await toolbar.search("face:new");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.peopleTab);
|
||||
const MarkerCount = await subject.getMarkerCount();
|
||||
|
||||
if ((await photoedit.inputName.nth(0).value) == "") {
|
||||
await t
|
||||
.expect(photoedit.undoRemoveMarker.nth(0).visible)
|
||||
.notOk()
|
||||
.expect(photoedit.inputName.nth(0).value)
|
||||
.eql("")
|
||||
.click(photoedit.removeMarker)
|
||||
.expect(photoedit.undoRemoveMarker.nth(0).visible)
|
||||
.ok()
|
||||
.click(photoedit.undoRemoveMarker);
|
||||
} else if ((await photoedit.inputName.nth(0).value) != "") {
|
||||
await t
|
||||
.expect(photoedit.inputName.nth(1).value)
|
||||
.eql("")
|
||||
.click(photoedit.removeMarker)
|
||||
.expect(photoedit.undoRemoveMarker.nth(0).visible)
|
||||
.ok()
|
||||
.click(photoedit.undoRemoveMarker);
|
||||
}
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(5000);
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.peopleTab);
|
||||
|
||||
if ((await photoedit.inputName.nth(0).value) == "") {
|
||||
await t
|
||||
.expect(photoedit.undoRemoveMarker.nth(0).visible)
|
||||
.notOk()
|
||||
.expect(photoedit.inputName.nth(0).value)
|
||||
.eql("")
|
||||
.click(photoedit.removeMarker)
|
||||
.expect(photoedit.undoRemoveMarker.nth(0).visible)
|
||||
.ok();
|
||||
} else if ((await photoedit.inputName.nth(0).value) != "") {
|
||||
await t
|
||||
.expect(photoedit.undoRemoveMarker.nth(0).visible)
|
||||
.notOk()
|
||||
.expect(photoedit.inputName.nth(1).value)
|
||||
.eql("")
|
||||
.click(photoedit.removeMarker)
|
||||
.expect(photoedit.undoRemoveMarker.nth(0).visible)
|
||||
.ok();
|
||||
}
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await t.eval(() => location.reload());
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.peopleTab);
|
||||
const MarkerCountAfterRemove = await subject.getMarkerCount();
|
||||
|
||||
await t.expect(MarkerCountAfterRemove).eql(MarkerCount - 1);
|
||||
});
|
||||
|
||||
test.meta("testID", "people-006")("Hide face", async (t) => {
|
||||
await menu.openPage("people");
|
||||
await t.click(subject.newTab);
|
||||
await subject.triggerToolbarAction("reload");
|
||||
const FirstFaceID = await subject.getNthFaceUid(0);
|
||||
await subject.checkFaceVisibility(FirstFaceID, true);
|
||||
await subject.triggerHoverAction("id", FirstFaceID, "hidden");
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(5000);
|
||||
await subject.checkFaceVisibility(FirstFaceID, false);
|
||||
await subject.triggerToolbarAction("show-hidden");
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(6000);
|
||||
await subject.checkFaceVisibility(FirstFaceID, true);
|
||||
await subject.triggerHoverAction("id", FirstFaceID, "hidden");
|
||||
await subject.triggerToolbarAction("exclude-hidden");
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(6000);
|
||||
await subject.checkFaceVisibility(FirstFaceID, true);
|
||||
});
|
||||
|
||||
test.meta("testID", "people-007")("Hide person", async (t) => {
|
||||
await menu.openPage("people");
|
||||
await t.click(subject.recognizedTab);
|
||||
const FirstPersonUid = await subject.getNthSubjectUid(0);
|
||||
await subject.checkSubjectVisibility("uid", FirstPersonUid, true);
|
||||
await subject.triggerHoverAction("uid", FirstPersonUid, "hidden");
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(6000);
|
||||
await subject.checkSubjectVisibility("uid", FirstPersonUid, false);
|
||||
await subject.triggerToolbarAction("show-hidden");
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(6000);
|
||||
await subject.checkSubjectVisibility("uid", FirstPersonUid, true);
|
||||
await subject.triggerHoverAction("uid", FirstPersonUid, "hidden");
|
||||
await subject.triggerToolbarAction("exclude-hidden");
|
||||
await t.eval(() => location.reload());
|
||||
await t.wait(5000);
|
||||
await subject.checkSubjectVisibility("uid", FirstPersonUid, true);
|
||||
});
|
494
frontend/tests/acceptance-new/photos-archive-private.js
Normal file
494
frontend/tests/acceptance-new/photos-archive-private.js
Normal file
|
@ -0,0 +1,494 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import PhotoEdit from "../page-model/photo-edit";
|
||||
import Album from "../page-model/album";
|
||||
import Subject from "../page-model/subject";
|
||||
import Label from "../page-model/label";
|
||||
|
||||
fixture`Test photos archive and private functionalities`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const photoedit = new PhotoEdit();
|
||||
const album = new Album();
|
||||
const label = new Label();
|
||||
const subject = new Subject();
|
||||
|
||||
test.meta("testID", "photos-archive-private-001").meta({ type: "smoke" })(
|
||||
"Private/unprivate photo/video using clipboard and list",
|
||||
async (t) => {
|
||||
await toolbar.setFilter("view", "Mosaic");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("image", 1);
|
||||
const ThirdPhotoUid = await photo.getNthPhotoUid("image", 2);
|
||||
const FirstVideoUid = await photo.getNthPhotoUid("video", 0);
|
||||
const SecondVideoUid = await photo.getNthPhotoUid("video", 1);
|
||||
const ThirdVideoUid = await photo.getNthPhotoUid("video", 2);
|
||||
await menu.openPage("private");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(ThirdPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondVideoUid, false);
|
||||
await photo.checkPhotoVisibility(ThirdVideoUid, false);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", FirstVideoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("private", "");
|
||||
await toolbar.setFilter("view", "List");
|
||||
await photo.triggerListViewActions("uid", SecondPhotoUid, "private");
|
||||
await photo.triggerListViewActions("uid", SecondVideoUid, "private");
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
await photo.triggerHoverAction("uid", ThirdPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", ThirdVideoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await photoedit.turnSwitchOn("private");
|
||||
await t.click(photoedit.dialogNext);
|
||||
await photoedit.turnSwitchOn("private");
|
||||
await t.click(photoedit.dialogClose);
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(ThirdPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondVideoUid, false);
|
||||
await photo.checkPhotoVisibility(ThirdVideoUid, false);
|
||||
|
||||
await menu.openPage("video");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondVideoUid, false);
|
||||
await photo.checkPhotoVisibility(ThirdVideoUid, false);
|
||||
|
||||
await menu.openPage("private");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(ThirdPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondVideoUid, true);
|
||||
await photo.checkPhotoVisibility(ThirdVideoUid, true);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", SecondPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", ThirdPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", FirstVideoUid, "select");
|
||||
await photo.triggerHoverAction("uid", SecondVideoUid, "select");
|
||||
await photo.triggerHoverAction("uid", ThirdVideoUid, "select");
|
||||
await contextmenu.checkContextMenuCount("6");
|
||||
await contextmenu.triggerContextMenuAction("private", "");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(ThirdPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondVideoUid, false);
|
||||
await photo.checkPhotoVisibility(ThirdVideoUid, false);
|
||||
|
||||
await menu.openPage("browse");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(ThirdPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondVideoUid, true);
|
||||
await photo.checkPhotoVisibility(ThirdVideoUid, true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-archive-private-002").meta({ type: "smoke" })(
|
||||
"Archive/restore video, photos, private photos and review photos using clipboard",
|
||||
async (t) => {
|
||||
await toolbar.setFilter("view", "Mosaic");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("image", 1);
|
||||
const FirstVideoUid = await photo.getNthPhotoUid("video", 0);
|
||||
await menu.openPage("private");
|
||||
const FirstPrivatePhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await menu.openPage("review");
|
||||
const FirstReviewPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await menu.openPage("archive");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstPrivatePhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstReviewPhotoUid, false);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", SecondPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", FirstVideoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("archive", "");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstPrivatePhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstReviewPhotoUid, false);
|
||||
|
||||
await menu.openPage("review");
|
||||
await photo.triggerHoverAction("uid", FirstReviewPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("archive", "");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
|
||||
await photo.checkPhotoVisibility(FirstReviewPhotoUid, false);
|
||||
|
||||
await menu.openPage("private");
|
||||
await photo.triggerHoverAction("uid", FirstPrivatePhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("archive", "");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPrivatePhotoUid, false);
|
||||
|
||||
await menu.openPage("archive");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstPrivatePhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstReviewPhotoUid, true);
|
||||
|
||||
await photo.triggerHoverAction("uid", FirstPrivatePhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", FirstReviewPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", SecondPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", FirstVideoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("restore", "");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstPrivatePhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstReviewPhotoUid, false);
|
||||
|
||||
await menu.openPage("browse");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstPrivatePhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstReviewPhotoUid, false);
|
||||
|
||||
await menu.openPage("private");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPrivatePhotoUid, true);
|
||||
|
||||
await menu.openPage("review");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstReviewPhotoUid, true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-archive-private-003")(
|
||||
"Check that archived files are not shown in monochrome/panoramas/stacks/scans/review/albums/favorites/private/videos/calendar/moments/states/labels/folders/originals",
|
||||
async (t) => {
|
||||
await menu.openPage("archive");
|
||||
const InitialPhotoCountInArchive = await photo.getPhotoCount("all");
|
||||
await menu.openPage("monochrome");
|
||||
const MonochromePhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", MonochromePhoto, "select");
|
||||
await menu.openPage("panoramas");
|
||||
const PanoramaPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", PanoramaPhoto, "select");
|
||||
await menu.openPage("stacks");
|
||||
const StackedPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", StackedPhoto, "select");
|
||||
await menu.openPage("scans");
|
||||
const ScannedPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", ScannedPhoto, "select");
|
||||
await menu.openPage("review");
|
||||
const ReviewPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", ReviewPhoto, "select");
|
||||
await menu.openPage("favorites");
|
||||
const FavoritesPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", FavoritesPhoto, "select");
|
||||
await menu.openPage("private");
|
||||
const PrivatePhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", PrivatePhoto, "select");
|
||||
await menu.openPage("video");
|
||||
const Video = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", Video, "select");
|
||||
await menu.openPage("calendar");
|
||||
await toolbar.search("January 2017");
|
||||
await album.openNthAlbum(0);
|
||||
const CalendarPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", CalendarPhoto, "select");
|
||||
await menu.openPage("moments");
|
||||
await album.openNthAlbum(0);
|
||||
const MomentPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", MomentPhoto, "select");
|
||||
await menu.openPage("states");
|
||||
await toolbar.search("Western Cape");
|
||||
await album.openNthAlbum(0);
|
||||
const StatesPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", StatesPhoto, "select");
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("Seashore");
|
||||
await label.openNthLabel(0);
|
||||
const LabelPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", LabelPhoto, "select");
|
||||
await menu.openPage("people");
|
||||
await subject.openNthSubject(1);
|
||||
const SubjectPhoto = await photo.getNthPhotoUid("all", 1);
|
||||
await photo.triggerHoverAction("uid", SubjectPhoto, "select");
|
||||
await menu.openPage("folders");
|
||||
await toolbar.search("archive");
|
||||
await album.openNthAlbum(0);
|
||||
const FolderPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", FolderPhoto, "select");
|
||||
await contextmenu.checkContextMenuCount("14");
|
||||
await contextmenu.triggerContextMenuAction("archive", "");
|
||||
await menu.openPage("archive");
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
const PhotoCountInArchiveAfterArchive = await photo.getPhotoCount("all");
|
||||
await t.expect(PhotoCountInArchiveAfterArchive).eql(InitialPhotoCountInArchive + 14);
|
||||
|
||||
await menu.openPage("monochrome");
|
||||
await photo.checkPhotoVisibility(MonochromePhoto, false);
|
||||
await menu.openPage("panoramas");
|
||||
await photo.checkPhotoVisibility(PanoramaPhoto, false);
|
||||
await menu.openPage("stacks");
|
||||
await photo.checkPhotoVisibility(StackedPhoto, false);
|
||||
await menu.openPage("scans");
|
||||
await photo.checkPhotoVisibility(ScannedPhoto, false);
|
||||
await menu.openPage("review");
|
||||
await photo.checkPhotoVisibility(ReviewPhoto, false);
|
||||
await menu.openPage("favorites");
|
||||
await photo.checkPhotoVisibility(FavoritesPhoto, false);
|
||||
await menu.openPage("private");
|
||||
await photo.checkPhotoVisibility(PrivatePhoto, false);
|
||||
await menu.openPage("video");
|
||||
await photo.checkPhotoVisibility(Video, false);
|
||||
await t.navigateTo("/calendar/aqmxlr71p6zo22dk/january-2017");
|
||||
await photo.checkPhotoVisibility(CalendarPhoto, false);
|
||||
await menu.openPage("moments");
|
||||
await album.openNthAlbum(0);
|
||||
await photo.checkPhotoVisibility(MomentPhoto, false);
|
||||
await t.navigateTo("/states/aqmxlr71tebcohrw/western-cape-south-africa");
|
||||
await photo.checkPhotoVisibility(StatesPhoto, false);
|
||||
|
||||
await t.navigateTo("/all?q=label%3Aseashore");
|
||||
await photo.checkPhotoVisibility(LabelPhoto, false);
|
||||
await menu.openPage("people");
|
||||
await subject.openNthSubject(1);
|
||||
await photo.checkPhotoVisibility(SubjectPhoto, false);
|
||||
await t.navigateTo("/folders/aqnah1321mgkt1w2/archive");
|
||||
await photo.checkPhotoVisibility(FolderPhoto, false);
|
||||
|
||||
await menu.openPage("archive");
|
||||
await photo.triggerHoverAction("uid", MonochromePhoto, "select");
|
||||
await photo.triggerHoverAction("uid", PanoramaPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", StackedPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", ScannedPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", FavoritesPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", ReviewPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", PrivatePhoto, "select");
|
||||
await photo.triggerHoverAction("uid", Video, "select");
|
||||
await photo.triggerHoverAction("uid", CalendarPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", MomentPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", StatesPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", LabelPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", SubjectPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", FolderPhoto, "select");
|
||||
await contextmenu.checkContextMenuCount("14");
|
||||
await contextmenu.triggerContextMenuAction("restore", "");
|
||||
|
||||
const PhotoCountInArchiveAfterRestore = await photo.getPhotoCount("all");
|
||||
await t.expect(PhotoCountInArchiveAfterRestore).eql(InitialPhotoCountInArchive);
|
||||
await menu.openPage("private");
|
||||
await photo.checkPhotoVisibility(PrivatePhoto, true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-archive-private-004").meta({ type: "smoke" })(
|
||||
"Check that private files are not shown in monochrome/panoramas/stacks/scans/review/albums/favorites/archive/videos/calendar/moments/states/labels/folders/originals",
|
||||
async (t) => {
|
||||
await menu.openPage("private");
|
||||
const InitialPhotoCountInPrivate = await photo.getPhotoCount("all");
|
||||
await menu.openPage("monochrome");
|
||||
const MonochromePhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", MonochromePhoto, "select");
|
||||
await menu.openPage("panoramas");
|
||||
const PanoramaPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", PanoramaPhoto, "select");
|
||||
await menu.openPage("stacks");
|
||||
const StackedPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", StackedPhoto, "select");
|
||||
await menu.openPage("scans");
|
||||
const ScannedPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", ScannedPhoto, "select");
|
||||
await menu.openPage("review");
|
||||
const ReviewPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", ReviewPhoto, "select");
|
||||
await menu.openPage("favorites");
|
||||
const FavoritesPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", FavoritesPhoto, "select");
|
||||
await menu.openPage("video");
|
||||
const Video = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", Video, "select");
|
||||
await menu.openPage("albums");
|
||||
await toolbar.search("Holiday");
|
||||
await album.openNthAlbum(0);
|
||||
const AlbumPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", AlbumPhoto, "select");
|
||||
await menu.openPage("calendar");
|
||||
await toolbar.search("January 2017");
|
||||
await album.openNthAlbum(0);
|
||||
const CalendarPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", CalendarPhoto, "select");
|
||||
await menu.openPage("moments");
|
||||
await album.openNthAlbum(0);
|
||||
const MomentPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", MomentPhoto, "select");
|
||||
await menu.openPage("states");
|
||||
await toolbar.search("Western Cape");
|
||||
await album.openNthAlbum(0);
|
||||
const StatesPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", StatesPhoto, "select");
|
||||
await menu.openPage("labels");
|
||||
await toolbar.search("Seashore");
|
||||
await label.openNthLabel(0);
|
||||
const LabelPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", LabelPhoto, "select");
|
||||
await menu.openPage("people");
|
||||
await subject.openNthSubject(1);
|
||||
const SubjectPhoto = await photo.getNthPhotoUid("all", 1);
|
||||
await photo.triggerHoverAction("uid", SubjectPhoto, "select");
|
||||
await menu.openPage("folders");
|
||||
await toolbar.search("archive");
|
||||
await album.openNthAlbum(0);
|
||||
const FolderPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", FolderPhoto, "select");
|
||||
await contextmenu.checkContextMenuCount("14");
|
||||
await contextmenu.triggerContextMenuAction("private", "");
|
||||
await menu.openPage("private");
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
const PhotoCountInPrivateAfterArchive = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountInPrivateAfterArchive).eql(InitialPhotoCountInPrivate + 14);
|
||||
await menu.openPage("monochrome");
|
||||
await photo.checkPhotoVisibility(MonochromePhoto, false);
|
||||
await menu.openPage("panoramas");
|
||||
await photo.checkPhotoVisibility(PanoramaPhoto, false);
|
||||
await menu.openPage("stacks");
|
||||
await photo.checkPhotoVisibility(StackedPhoto, false);
|
||||
await menu.openPage("scans");
|
||||
await photo.checkPhotoVisibility(ScannedPhoto, false);
|
||||
await menu.openPage("review");
|
||||
await photo.checkPhotoVisibility(ReviewPhoto, false);
|
||||
await menu.openPage("favorites");
|
||||
await photo.checkPhotoVisibility(FavoritesPhoto, false);
|
||||
await menu.openPage("video");
|
||||
await photo.checkPhotoVisibility(Video, false);
|
||||
await t.navigateTo("/albums?q=Holiday");
|
||||
await album.openNthAlbum(0);
|
||||
await photo.checkPhotoVisibility(AlbumPhoto, true);
|
||||
await t.navigateTo("/calendar/aqmxlr71p6zo22dk/january-2017");
|
||||
await photo.checkPhotoVisibility(CalendarPhoto, false);
|
||||
await menu.openPage("moments");
|
||||
await album.openNthAlbum(0);
|
||||
await photo.checkPhotoVisibility(MomentPhoto, false);
|
||||
await t.navigateTo("/states/aqmxlr71tebcohrw/western-cape-south-africa");
|
||||
await photo.checkPhotoVisibility(StatesPhoto, false);
|
||||
|
||||
await t.navigateTo("/all?q=label%3Aseashore");
|
||||
await photo.checkPhotoVisibility(LabelPhoto, false);
|
||||
await menu.openPage("people");
|
||||
await subject.openNthSubject(1);
|
||||
await photo.checkPhotoVisibility(SubjectPhoto, false);
|
||||
await t.navigateTo("/folders/aqnah1321mgkt1w2/archive");
|
||||
await photo.checkPhotoVisibility(FolderPhoto, false);
|
||||
|
||||
await menu.openPage("private");
|
||||
await photo.triggerHoverAction("uid", MonochromePhoto, "select");
|
||||
await photo.triggerHoverAction("uid", PanoramaPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", StackedPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", ScannedPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", FavoritesPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", ReviewPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", Video, "select");
|
||||
await photo.triggerHoverAction("uid", CalendarPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", AlbumPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", MomentPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", StatesPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", LabelPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", SubjectPhoto, "select");
|
||||
await photo.triggerHoverAction("uid", FolderPhoto, "select");
|
||||
await contextmenu.checkContextMenuCount("14");
|
||||
await contextmenu.triggerContextMenuAction("private", "");
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
const PhotoCountInPrivateAfterRestore = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountInPrivateAfterRestore).eql(InitialPhotoCountInPrivate);
|
||||
|
||||
await menu.openPage("monochrome");
|
||||
await photo.checkPhotoVisibility(MonochromePhoto, true);
|
||||
await menu.openPage("panoramas");
|
||||
await photo.checkPhotoVisibility(PanoramaPhoto, true);
|
||||
await menu.openPage("stacks");
|
||||
await photo.checkPhotoVisibility(StackedPhoto, true);
|
||||
await menu.openPage("scans");
|
||||
await photo.checkPhotoVisibility(ScannedPhoto, true);
|
||||
await menu.openPage("review");
|
||||
await photo.checkPhotoVisibility(ReviewPhoto, true);
|
||||
await menu.openPage("favorites");
|
||||
await photo.checkPhotoVisibility(FavoritesPhoto, true);
|
||||
await menu.openPage("video");
|
||||
await photo.checkPhotoVisibility(Video, true);
|
||||
await t.navigateTo("/albums?q=Holiday");
|
||||
await album.openNthAlbum(0);
|
||||
await photo.checkPhotoVisibility(AlbumPhoto, true);
|
||||
await t.navigateTo("/calendar/aqmxlr71p6zo22dk/january-2017");
|
||||
await photo.checkPhotoVisibility(CalendarPhoto, true);
|
||||
await menu.openPage("moments");
|
||||
await album.openNthAlbum(0);
|
||||
await photo.checkPhotoVisibility(MomentPhoto, true);
|
||||
await t.navigateTo("/states/aqmxlr71tebcohrw/western-cape-south-africa");
|
||||
await photo.checkPhotoVisibility(StatesPhoto, true);
|
||||
|
||||
await t.navigateTo("/all?q=label%3Aseashore");
|
||||
await photo.checkPhotoVisibility(LabelPhoto, true);
|
||||
await menu.openPage("people");
|
||||
await subject.openNthSubject(1);
|
||||
await photo.checkPhotoVisibility(SubjectPhoto, true);
|
||||
await t.navigateTo("/folders/aqnah1321mgkt1w2/archive");
|
||||
await photo.checkPhotoVisibility(FolderPhoto, true);
|
||||
}
|
||||
);
|
119
frontend/tests/acceptance-new/photos-download.js
Normal file
119
frontend/tests/acceptance-new/photos-download.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import { RequestLogger } from "testcafe";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import PhotoViewer from "../page-model/photoviewer";
|
||||
import Page from "../page-model/page";
|
||||
|
||||
const logger = RequestLogger(/http:\/\/localhost:2343\/api\/v1\/*/, {
|
||||
logResponseHeaders: true,
|
||||
logResponseBody: true,
|
||||
});
|
||||
|
||||
fixture`Test photos download`.page`${testcafeconfig.url}`
|
||||
.requestHooks(logger)
|
||||
.skip("Does not work in container and we have no content-disposition header anymore");
|
||||
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const photoviewer = new PhotoViewer();
|
||||
const page = new Page();
|
||||
|
||||
test.meta("testID", "photos-download-001").meta({ type: "smoke" })(
|
||||
"Test download jpg file from context menu and fullscreen",
|
||||
async (t) => {
|
||||
await toolbar.search("name:monochrome-2.jpg");
|
||||
const PhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", PhotoUid, "select");
|
||||
await logger.clear();
|
||||
await contextmenu.triggerContextMenuAction("download", "");
|
||||
const requestInfo = await logger.requests[1].response;
|
||||
console.log(requestInfo);
|
||||
const requestInfo0 = await logger.requests[0].response;
|
||||
console.log(requestInfo0);
|
||||
|
||||
await page.validateDownloadRequest(requestInfo, "monochrome-2", ".jpg");
|
||||
|
||||
await logger.clear();
|
||||
await contextmenu.clearSelection();
|
||||
await toolbar.search("name:IMG_20200711_174006.jpg");
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await t.click(Selector("div").withAttribute("data-uid", SecondPhotoUid));
|
||||
await photoviewer.openPhotoViewer("uid", SecondPhotoUid);
|
||||
await logger.clear();
|
||||
await photoviewer.triggerPhotoViewerAction("download");
|
||||
await logger.clear();
|
||||
await photoviewer.triggerPhotoViewerAction("close");
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-download-002").meta({ type: "smoke" })(
|
||||
"Test download video from context menu",
|
||||
async (t) => {
|
||||
await toolbar.search("name:Mohn.mp4");
|
||||
const PhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", PhotoUid, "select");
|
||||
await logger.clear();
|
||||
await contextmenu.triggerContextMenuAction("download", "");
|
||||
const requestInfo = await logger.requests[0].response;
|
||||
console.log(requestInfo);
|
||||
const requestInfo2 = await logger.requests[1].response;
|
||||
|
||||
await page.validateDownloadRequest(requestInfo, "Mohn", ".mp4.jpg");
|
||||
await page.validateDownloadRequest(requestInfo2, "Mohn", ".mp4");
|
||||
|
||||
await logger.clear();
|
||||
await contextmenu.clearSelection();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-download-003")(
|
||||
"Test download multiple jpg files from context menu",
|
||||
async (t) => {
|
||||
await toolbar.search("name:panorama_2.jpg");
|
||||
const PhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", PhotoUid, "select");
|
||||
await toolbar.search("name:IMG_6478.JPG");
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", SecondPhotoUid, "select");
|
||||
await logger.clear();
|
||||
await contextmenu.triggerContextMenuAction("download", "");
|
||||
const requestInfo = await logger.requests[1].response;
|
||||
console.log(requestInfo);
|
||||
|
||||
await page.validateDownloadRequest(requestInfo, "photoprism-download", ".zip");
|
||||
|
||||
await logger.clear();
|
||||
await contextmenu.clearSelection();
|
||||
}
|
||||
);
|
||||
|
||||
//TODO Check RAW files as well
|
||||
test.meta("testID", "photos-download-004")(
|
||||
"Test raw file from context menu and fullscreen mode",
|
||||
async (t) => {
|
||||
await toolbar.search("name:elephantRAW");
|
||||
const PhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", PhotoUid, "select");
|
||||
await logger.clear();
|
||||
await contextmenu.triggerContextMenuAction("download", "");
|
||||
const requestInfo = await logger.requests[1].response;
|
||||
|
||||
await page.validateDownloadRequest(requestInfo, "elephantRAW", ".JPG");
|
||||
|
||||
await logger.clear();
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(Selector("div").withAttribute("data-uid", Photo));
|
||||
await t.expect(Selector("#photo-viewer").visible).ok().hover(Selector(".action-download"));
|
||||
await logger.clear();
|
||||
await t.click(Selector(".action-download"));
|
||||
const requestInfo3 = await logger.requests[1].response;
|
||||
//const requestInfo4 = await logger.requests[2].response;
|
||||
await page.validateDownloadRequest(requestInfo3, "elephantRAW", ".JPG");
|
||||
//await page.validateDownloadRequest(requestInfo4, "elephantRAW", ".mp4");
|
||||
await logger.clear();
|
||||
}
|
||||
);
|
266
frontend/tests/acceptance-new/photos-upload-delete.js
Normal file
266
frontend/tests/acceptance-new/photos-upload-delete.js
Normal file
|
@ -0,0 +1,266 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import fs from "fs";
|
||||
import Menu from "../page-model/menu";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Page from "../page-model/page";
|
||||
import PhotoEdit from "../page-model/photo-edit";
|
||||
import Originals from "../page-model/originals";
|
||||
import Album from "../page-model/album";
|
||||
import Library from "../page-model/library";
|
||||
|
||||
fixture`Test photos upload and delete`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const album = new Album();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const page = new Page();
|
||||
const photoedit = new PhotoEdit();
|
||||
const originals = new Originals();
|
||||
const library = new Library();
|
||||
|
||||
test.meta("testID", "photos-upload-delete-001").meta({ type: "smoke" })(
|
||||
"Upload + Delete jpg/json",
|
||||
async (t) => {
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/10")).notOk();
|
||||
await toolbar.search("digikam");
|
||||
const PhotoCount = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCount).eql(0);
|
||||
|
||||
await toolbar.triggerToolbarAction("upload");
|
||||
await t
|
||||
.setFilesToUpload(Selector(".input-upload"), [
|
||||
"./upload-files/digikam.jpg",
|
||||
"./upload-files/digikam.json",
|
||||
])
|
||||
.wait(15000);
|
||||
const PhotoCountAfterUpload = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterUpload).eql(1);
|
||||
|
||||
const UploadedPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await t.navigateTo("/library/files/2020/10");
|
||||
const FileCount = await originals.getFileCount();
|
||||
|
||||
await t.expect(FileCount).eql(2);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("digikam");
|
||||
await photo.triggerHoverAction("uid", UploadedPhoto, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.filesTab);
|
||||
|
||||
await t
|
||||
.expect(Selector("div.caption").withText(".json").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.caption").withText(".jpg").visible)
|
||||
.ok();
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
|
||||
if (t.browser.platform !== "mobile") {
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/10")).ok();
|
||||
const originalsLength = fs.readdirSync("../storage/acceptance/originals/2020/10").length;
|
||||
await t.expect(originalsLength).eql(2);
|
||||
}
|
||||
|
||||
await contextmenu.triggerContextMenuAction("archive", "");
|
||||
await menu.openPage("archive");
|
||||
await photo.triggerHoverAction("uid", UploadedPhoto, "select");
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("digikam");
|
||||
await photo.checkPhotoVisibility(UploadedPhoto, false);
|
||||
await t.navigateTo("/library/files/2020/10");
|
||||
const FileCountAfterDelete = await originals.getFileCount();
|
||||
|
||||
await t.expect(FileCountAfterDelete).eql(0);
|
||||
if (t.browser.platform !== "mobile") {
|
||||
const originalsLengthAfterDelete = fs.readdirSync(
|
||||
"../storage/acceptance/originals/2020/10"
|
||||
).length;
|
||||
await t.expect(originalsLengthAfterDelete).eql(0);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-upload-delete-002")("Upload + Delete video", async (t) => {
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/06")).notOk();
|
||||
await toolbar.search("korn");
|
||||
const PhotoCount = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCount).eql(0);
|
||||
|
||||
await toolbar.triggerToolbarAction("upload");
|
||||
await t.setFilesToUpload(Selector(".input-upload"), ["./upload-files/korn.mp4"]).wait(15000);
|
||||
const PhotoCountAfterUpload = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterUpload).eql(1);
|
||||
|
||||
const UploadedPhoto = await photo.getNthPhotoUid("all", 0);
|
||||
await t.navigateTo("/library/files/2020/06");
|
||||
const FileCount = await originals.getFileCount();
|
||||
|
||||
await t.expect(FileCount).eql(1);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("korn");
|
||||
await photo.triggerHoverAction("uid", UploadedPhoto, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.filesTab);
|
||||
|
||||
await t
|
||||
.expect(Selector("div.caption").withText(".mp4").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.caption").withText(".jpg").visible)
|
||||
.ok();
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
|
||||
if (t.browser.platform !== "mobile") {
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/06")).ok();
|
||||
const originalsLength = fs.readdirSync("../storage/acceptance/originals/2020/06").length;
|
||||
await t.expect(originalsLength).eql(1);
|
||||
const sidecarLength = fs.readdirSync("../storage/acceptance/originals/2020/06").length;
|
||||
await t.expect(sidecarLength).eql(1);
|
||||
}
|
||||
|
||||
await contextmenu.triggerContextMenuAction("archive", "");
|
||||
await menu.openPage("archive");
|
||||
await photo.triggerHoverAction("uid", UploadedPhoto, "select");
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("korn");
|
||||
await photo.checkPhotoVisibility(UploadedPhoto, false);
|
||||
await t.navigateTo("/library/files/2020/06");
|
||||
const FileCountAfterDelete = await originals.getFileCount();
|
||||
|
||||
await t.expect(FileCountAfterDelete).eql(0);
|
||||
if (t.browser.platform !== "mobile") {
|
||||
const originalsLengthAfterDelete = fs.readdirSync(
|
||||
"../storage/acceptance/originals/2020/06"
|
||||
).length;
|
||||
await t.expect(originalsLengthAfterDelete).eql(0);
|
||||
const sidecarLengthAfterDelete = fs.readdirSync(
|
||||
"../storage/acceptance/originals/2020/06"
|
||||
).length;
|
||||
await t.expect(sidecarLengthAfterDelete).eql(0);
|
||||
}
|
||||
});
|
||||
|
||||
test.meta("testID", "photos-upload-delete-003")("Upload to existing Album + Delete", async (t) => {
|
||||
await menu.openPage("albums");
|
||||
await toolbar.search("Christmas");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCount = await photo.getPhotoCount("all");
|
||||
await toolbar.triggerToolbarAction("upload");
|
||||
await t
|
||||
.click(Selector(".input-albums"))
|
||||
.click(page.selectOption.withText("Christmas"))
|
||||
.setFilesToUpload(Selector(".input-upload"), ["./upload-files/ladybug.jpg"])
|
||||
.wait(15000);
|
||||
const PhotoCountAfterUpload = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterUpload).eql(PhotoCount + 1);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("ladybug");
|
||||
const UploadedPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", UploadedPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("archive", "");
|
||||
await menu.openPage("archive");
|
||||
await photo.triggerHoverAction("uid", UploadedPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("ladybug");
|
||||
await photo.checkPhotoVisibility(UploadedPhotoUid, false);
|
||||
await menu.openPage("albums");
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
await photo.checkPhotoVisibility(UploadedPhotoUid, false);
|
||||
const PhotoCountAfterDelete = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterDelete).eql(PhotoCount);
|
||||
});
|
||||
|
||||
test.meta("testID", "photos-upload-delete-004")("Upload jpg to new Album + Delete", async (t) => {
|
||||
await menu.openPage("albums");
|
||||
const AlbumCount = await album.getAlbumCount("all");
|
||||
await toolbar.triggerToolbarAction("upload");
|
||||
await t
|
||||
.click(Selector(".input-albums"))
|
||||
.typeText(Selector(".input-albums input"), "NewCreatedAlbum")
|
||||
.pressKey("enter")
|
||||
.setFilesToUpload(Selector(".input-upload"), ["./upload-files/digikam.jpg"])
|
||||
.wait(15000);
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
const AlbumCountAfterUpload = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterUpload).eql(AlbumCount + 1);
|
||||
|
||||
await toolbar.search("NewCreatedAlbum");
|
||||
await album.openNthAlbum(0);
|
||||
const PhotoCount = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCount).eql(1);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("digikam");
|
||||
const UploadedPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.triggerHoverAction("uid", UploadedPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("archive", "");
|
||||
await menu.openPage("archive");
|
||||
await photo.triggerHoverAction("uid", UploadedPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("digikam");
|
||||
await photo.checkPhotoVisibility(UploadedPhotoUid, false);
|
||||
await menu.openPage("albums");
|
||||
await toolbar.search("NewCreatedAlbum");
|
||||
await album.openNthAlbum(0);
|
||||
await photo.checkPhotoVisibility(UploadedPhotoUid, false);
|
||||
const PhotoCountAfterDelete = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterDelete).eql(0);
|
||||
|
||||
await menu.openPage("albums");
|
||||
await toolbar.search("NewCreatedAlbum");
|
||||
await album.triggerHoverAction("nth", 0, "select");
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
});
|
||||
|
||||
test.meta("testID", "photos-upload-delete-005").meta({ type: "smoke" })(
|
||||
"Try uploading nsfw file",
|
||||
async (t) => {
|
||||
await toolbar.triggerToolbarAction("upload");
|
||||
await t
|
||||
.setFilesToUpload(Selector(".input-upload"), ["./upload-files/hentai_2.jpg"])
|
||||
.wait(15000);
|
||||
await menu.openPage("library");
|
||||
await t.click(library.logsTab);
|
||||
|
||||
await t.expect(Selector("p").withText("hentai_2.jpg might be offensive").visible).ok();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-upload-delete-006").meta({ type: "smoke" })(
|
||||
"Try uploading txt file",
|
||||
async (t) => {
|
||||
await toolbar.triggerToolbarAction("upload");
|
||||
await t.setFilesToUpload(Selector(".input-upload"), ["./upload-files/foo.txt"]).wait(15000);
|
||||
await menu.openPage("library");
|
||||
await t.click(library.logsTab);
|
||||
|
||||
await t.expect(Selector("p").withText(" foo.txt is not a jpeg file").visible).ok();
|
||||
}
|
||||
);
|
377
frontend/tests/acceptance-new/photos.js
Normal file
377
frontend/tests/acceptance-new/photos.js
Normal file
|
@ -0,0 +1,377 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import { ClientFunction } from "testcafe";
|
||||
import Menu from "../page-model/menu";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import PhotoViewer from "../page-model/photoviewer";
|
||||
import Page from "../page-model/page";
|
||||
import PhotoEdit from "../page-model/photo-edit";
|
||||
|
||||
const scroll = ClientFunction((x, y) => window.scrollTo(x, y));
|
||||
const getcurrentPosition = ClientFunction(() => window.pageYOffset);
|
||||
|
||||
fixture`Test photos`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const photoviewer = new PhotoViewer();
|
||||
const page = new Page();
|
||||
const photoedit = new PhotoEdit();
|
||||
|
||||
test.meta("testID", "photos-001")("Scroll to top", async (t) => {
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
|
||||
await t
|
||||
.expect(Selector("button.is-photo-scroll-top").exists)
|
||||
.notOk()
|
||||
.expect(getcurrentPosition())
|
||||
.eql(0)
|
||||
.expect(Selector('div[class="v-image__image v-image__image--cover"]').nth(0).visible)
|
||||
.ok();
|
||||
|
||||
await scroll(0, 1400);
|
||||
await scroll(0, 900);
|
||||
|
||||
await t.click(Selector("button.p-scroll-top")).expect(getcurrentPosition()).eql(0);
|
||||
});
|
||||
|
||||
//TODO Covered by admin role test
|
||||
test.meta("testID", "photos-002")(
|
||||
"Download single photo/video using clipboard and fullscreen mode",
|
||||
async (t) => {
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("image", 1);
|
||||
const FirstVideoUid = await photo.getNthPhotoUid("video", 0);
|
||||
await photoviewer.openPhotoViewer("uid", SecondPhotoUid);
|
||||
|
||||
await photoviewer.checkPhotoViewerActionAvailability("download", true);
|
||||
|
||||
await photoviewer.triggerPhotoViewerAction("close");
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", FirstVideoUid, "select");
|
||||
await contextmenu.checkContextMenuCount("2");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-003").meta({ type: "smoke" })(
|
||||
"Approve photo using approve and by adding location",
|
||||
async (t) => {
|
||||
await menu.openPage("review");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("all", 1);
|
||||
const ThirdPhotoUid = await photo.getNthPhotoUid("all", 2);
|
||||
await menu.openPage("browse");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
|
||||
await menu.openPage("review");
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.detailsClose);
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.detailsApprove);
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.click(photoedit.detailsApply).click(photoedit.detailsClose);
|
||||
} else {
|
||||
await t.click(photoedit.detailsDone);
|
||||
}
|
||||
await photo.triggerHoverAction("uid", SecondPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t
|
||||
.typeText(photoedit.latitude, "9.999", { replace: true })
|
||||
.typeText(photoedit.longitude, "9.999", { replace: true });
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.click(photoedit.detailsApply).click(photoedit.detailsClose);
|
||||
} else {
|
||||
await t.click(photoedit.detailsDone);
|
||||
}
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
const ApproveButtonThirdPhoto =
|
||||
'div.is-photo[data-uid="' + ThirdPhotoUid + '"] button.action-approve';
|
||||
await t.click(Selector(ApproveButtonThirdPhoto));
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(ThirdPhotoUid, false);
|
||||
await menu.openPage("browse");
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(ThirdPhotoUid, true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-004").meta({ type: "smoke" })("Like/dislike photo/video", async (t) => {
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("image", 1);
|
||||
const FirstVideoUid = await photo.getNthPhotoUid("video", 0);
|
||||
await menu.openPage("favorites");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "favorite");
|
||||
await photo.triggerHoverAction("uid", FirstVideoUid, "favorite");
|
||||
await photo.triggerHoverAction("uid", SecondPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await photoedit.turnSwitchOn("favorite");
|
||||
await t.click(photoedit.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
|
||||
await menu.openPage("favorites");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
|
||||
await photo.triggerHoverAction("uid", SecondPhotoUid, "favorite");
|
||||
await photo.triggerHoverAction("uid", FirstVideoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await photoedit.turnSwitchOff("favorite");
|
||||
await t.click(photoedit.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
await photoviewer.openPhotoViewer("uid", FirstPhotoUid);
|
||||
await photoviewer.triggerPhotoViewerAction("like");
|
||||
await photoviewer.triggerPhotoViewerAction("close");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
});
|
||||
|
||||
test.meta("testID", "photos-005").meta({ type: "smoke" })("Edit photo/video", async (t) => {
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
await t.click(page.cardTitle.withAttribute("data-uid", FirstPhotoUid));
|
||||
|
||||
await t.expect(photoedit.latitude.visible).ok();
|
||||
|
||||
await t.click(photoedit.dialogNext);
|
||||
|
||||
await t.expect(photoedit.dialogPrevious.getAttribute("disabled")).notEql("disabled");
|
||||
|
||||
await t.click(photoedit.dialogPrevious).click(photoedit.dialogClose);
|
||||
await photoviewer.openPhotoViewer("uid", FirstPhotoUid);
|
||||
await photoviewer.triggerPhotoViewerAction("edit");
|
||||
const FirstPhotoTitle = await photoedit.title.value;
|
||||
const FirstPhotoLocalTime = await photoedit.localTime.value;
|
||||
const FirstPhotoDay = await photoedit.day.value;
|
||||
const FirstPhotoMonth = await photoedit.month.value;
|
||||
const FirstPhotoYear = await photoedit.year.value;
|
||||
const FirstPhotoTimezone = await photoedit.timezone.value;
|
||||
const FirstPhotoLatitude = await photoedit.latitude.value;
|
||||
const FirstPhotoLongitude = await photoedit.longitude.value;
|
||||
const FirstPhotoAltitude = await photoedit.altitude.value;
|
||||
const FirstPhotoCountry = await photoedit.country.value;
|
||||
const FirstPhotoCamera = await photoedit.camera.innerText;
|
||||
const FirstPhotoIso = await photoedit.iso.value;
|
||||
const FirstPhotoExposure = await photoedit.exposure.value;
|
||||
const FirstPhotoLens = await photoedit.lens.innerText;
|
||||
const FirstPhotoFnumber = await photoedit.fnumber.value;
|
||||
const FirstPhotoFocalLength = await photoedit.focallength.value;
|
||||
const FirstPhotoSubject = await photoedit.subject.value;
|
||||
const FirstPhotoArtist = await photoedit.artist.value;
|
||||
const FirstPhotoCopyright = await photoedit.copyright.value;
|
||||
const FirstPhotoLicense = await photoedit.license.value;
|
||||
const FirstPhotoDescription = await photoedit.description.value;
|
||||
const FirstPhotoKeywords = await photoedit.keywords.value;
|
||||
const FirstPhotoNotes = await photoedit.notes.value;
|
||||
|
||||
await t
|
||||
.typeText(photoedit.title, "Not saved photo title", { replace: true })
|
||||
.click(photoedit.detailsClose)
|
||||
.click(Selector("button.action-date-edit").withAttribute("data-uid", FirstPhotoUid));
|
||||
|
||||
await t.expect(photoedit.title.value).eql(FirstPhotoTitle);
|
||||
|
||||
await photoedit.editPhoto(
|
||||
"New Photo Title",
|
||||
"Europe/Moscow",
|
||||
"15",
|
||||
"07",
|
||||
"2019",
|
||||
"04:30:30",
|
||||
"-1",
|
||||
"41.15333",
|
||||
"20.168331",
|
||||
"32",
|
||||
"1/32",
|
||||
"29",
|
||||
"33",
|
||||
"Super nice edited photo",
|
||||
"Happy",
|
||||
"Happy2020",
|
||||
"Super nice cat license",
|
||||
"Description of a nice image :)",
|
||||
", cat, love",
|
||||
"Some notes"
|
||||
);
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
|
||||
await t
|
||||
.expect(page.cardTitle.withAttribute("data-uid", FirstPhotoUid).innerText)
|
||||
.eql("New Photo Title");
|
||||
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
|
||||
//const expectedValues = [{ FirstPhotoTitle: photoedit.title }, { "bluh bla": photoedit.day }];
|
||||
/*const expectedValues = [
|
||||
[FirstPhotoTitle, photoedit.title],
|
||||
["blah", photoedit.day],
|
||||
];
|
||||
await photoedit.checkEditFormValuesNewNew(expectedValues);*/
|
||||
|
||||
await photoedit.checkEditFormValues(
|
||||
"New Photo Title",
|
||||
"15",
|
||||
"07",
|
||||
"2019",
|
||||
"04:30:30",
|
||||
"Europe/Moscow",
|
||||
"Albania",
|
||||
"-1",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"32",
|
||||
"1/32",
|
||||
"",
|
||||
"29",
|
||||
"33",
|
||||
"Super nice edited photo",
|
||||
"Happy",
|
||||
"Happy2020",
|
||||
"Super nice cat license",
|
||||
"Description of a nice image :)",
|
||||
"cat",
|
||||
"Some notes"
|
||||
);
|
||||
|
||||
await photoedit.undoPhotoEdit(
|
||||
FirstPhotoTitle,
|
||||
FirstPhotoTimezone,
|
||||
FirstPhotoDay,
|
||||
FirstPhotoMonth,
|
||||
FirstPhotoYear,
|
||||
FirstPhotoLocalTime,
|
||||
FirstPhotoAltitude,
|
||||
FirstPhotoLatitude,
|
||||
FirstPhotoLongitude,
|
||||
FirstPhotoCountry,
|
||||
FirstPhotoIso,
|
||||
FirstPhotoExposure,
|
||||
FirstPhotoFnumber,
|
||||
FirstPhotoFocalLength,
|
||||
FirstPhotoSubject,
|
||||
FirstPhotoArtist,
|
||||
FirstPhotoCopyright,
|
||||
FirstPhotoLicense,
|
||||
FirstPhotoDescription,
|
||||
FirstPhotoKeywords,
|
||||
FirstPhotoNotes
|
||||
);
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.clearSelection();
|
||||
});
|
||||
|
||||
test.meta("testID", "photos-006")("Navigate from card view to place", async (t) => {
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
await t.click(page.cardLocation.nth(0));
|
||||
|
||||
await t
|
||||
.expect(Selector("#map").exists, { timeout: 15000 })
|
||||
.ok()
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").value)
|
||||
.notEql("");
|
||||
});
|
||||
|
||||
test.meta("testID", "photos-007")("Mark photos/videos as panorama/scan", async (t) => {
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const FirstVideoUid = await photo.getNthPhotoUid("video", 1);
|
||||
await menu.openPage("scans");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
|
||||
await menu.openPage("panoramas");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", FirstVideoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await photoedit.turnSwitchOn("scan");
|
||||
await photoedit.turnSwitchOn("panorama");
|
||||
await t.click(photoedit.dialogNext);
|
||||
await photoedit.turnSwitchOn("scan");
|
||||
await photoedit.turnSwitchOn("panorama");
|
||||
await t.click(photoedit.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, true);
|
||||
|
||||
await menu.openPage("scans");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
|
||||
await menu.openPage("panoramas");
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, true);
|
||||
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "select");
|
||||
await photo.triggerHoverAction("uid", FirstVideoUid, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await photoedit.turnSwitchOff("scan");
|
||||
await photoedit.turnSwitchOff("panorama");
|
||||
await t.click(photoedit.dialogNext);
|
||||
await photoedit.turnSwitchOff("scan");
|
||||
await photoedit.turnSwitchOff("panorama");
|
||||
await t.click(photoedit.dialogClose);
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
await contextmenu.clearSelection();
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(FirstVideoUid, false);
|
||||
});
|
53
frontend/tests/acceptance-new/places.js
Normal file
53
frontend/tests/acceptance-new/places.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { Selector } from "testcafe";
|
||||
import { ClientFunction } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig.json";
|
||||
import Menu from "../page-model/menu";
|
||||
|
||||
const getLocation = ClientFunction(() => document.location.href);
|
||||
|
||||
fixture`Search and open photo from places`.page`${testcafeconfig.url}`.skip(
|
||||
"Places don't loadin chrome from within the container"
|
||||
);
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
test.meta("testID", "places-001")("Test places", async (t) => {
|
||||
await menu.openPage("places");
|
||||
|
||||
await t
|
||||
.expect(Selector("#map").exists, { timeout: 15000 })
|
||||
.ok()
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok();
|
||||
|
||||
await t.typeText(Selector('input[aria-label="Search"]'), "Berlin").pressKey("enter");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok()
|
||||
.expect(getLocation())
|
||||
.contains("Berlin");
|
||||
|
||||
await menu.openPage("browse");
|
||||
|
||||
await t.expect(Selector("div.is-photo").exists).ok();
|
||||
|
||||
await menu.openPage("places");
|
||||
|
||||
await t
|
||||
.expect(Selector("#map").exists, { timeout: 15000 })
|
||||
.ok()
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok();
|
||||
|
||||
await t
|
||||
.typeText(Selector('input[aria-label="Search"]'), "canada", { replace: true })
|
||||
.pressKey("enter")
|
||||
.wait(8000);
|
||||
|
||||
await t.expect(Selector('div[title="Cape / Bowen Island / 2019"]').visible).ok();
|
||||
|
||||
await t.click(Selector('div[title="Cape / Bowen Island / 2019"]'));
|
||||
|
||||
await t.expect(Selector("#photo-viewer").visible).ok();
|
||||
});
|
37
frontend/tests/acceptance-new/settings/about.js
Normal file
37
frontend/tests/acceptance-new/settings/about.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "../testcafeconfig";
|
||||
import Menu from "../../page-model/menu";
|
||||
|
||||
fixture`Test about`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
test.meta("testID", "about-001")("About page is displayed with all links", async (t) => {
|
||||
await menu.openPage("about");
|
||||
await t
|
||||
.expect(Selector("h2").withText("Trademarks").visible)
|
||||
.ok()
|
||||
.expect(Selector('a[href="https://photoprism.app/"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('a[href="https://www.patreon.com/photoprism"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('a[href="https://github.com/photoprism/photoprism/projects/5"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('a[href="https://docs.photoprism.app/"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('a[href="/about/license"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('a[href="https://gitter.im/browseyourlife/community"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('a[href="https://twitter.com/photoprism_app"]').visible)
|
||||
.ok();
|
||||
});
|
||||
|
||||
test.meta("testID", "about-002")("License page is displayed with all links", async (t) => {
|
||||
await menu.openPage("license");
|
||||
await t
|
||||
.expect(Selector("h3").withText("GNU AFFERO GENERAL PUBLIC LICENSE").visible)
|
||||
.ok()
|
||||
.expect(Selector('a[href="https://www.gnu.org/licenses/agpl-3.0.en.html"]').visible)
|
||||
.ok();
|
||||
});
|
416
frontend/tests/acceptance-new/settings/general.js
Normal file
416
frontend/tests/acceptance-new/settings/general.js
Normal file
|
@ -0,0 +1,416 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "../testcafeconfig";
|
||||
import Menu from "../../page-model/menu";
|
||||
import Toolbar from "../../page-model/toolbar";
|
||||
import ContextMenu from "../../page-model/context-menu";
|
||||
import PhotoViewer from "../../page-model/photoviewer";
|
||||
import Page from "../../page-model/page";
|
||||
import Photo from "../../page-model/photo";
|
||||
import PhotoEdit from "../../page-model/photo-edit";
|
||||
import Album from "../../page-model/album";
|
||||
import Settings from "../../page-model/settings";
|
||||
import Library from "../../page-model/library";
|
||||
|
||||
fixture`Test settings`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photoviewer = new PhotoViewer();
|
||||
const page = new Page();
|
||||
const photo = new Photo();
|
||||
const photoedit = new PhotoEdit();
|
||||
const album = new Album();
|
||||
const settings = new Settings();
|
||||
const library = new Library();
|
||||
|
||||
test.meta("testID", "settings-general-001").meta({ type: "smoke" })("Disable delete", async (t) => {
|
||||
await menu.openPage("archive");
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", true);
|
||||
await contextmenu.clearSelection();
|
||||
await menu.openPage("settings");
|
||||
await t.click(settings.deleteCheckbox);
|
||||
await menu.openPage("archive");
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("restore", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
await contextmenu.clearSelection();
|
||||
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("stack:true");
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.filesTab);
|
||||
await t.click(photoedit.toggleExpandFile.nth(1));
|
||||
|
||||
await t.expect(photoedit.deleteFile.visible).notOk();
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
await menu.openPage("settings");
|
||||
await t.click(settings.deleteCheckbox);
|
||||
});
|
||||
|
||||
test.meta("testID", "settings-general-002").meta({ type: "smoke" })(
|
||||
"Change language",
|
||||
async (t) => {
|
||||
await t.expect(Selector(".nav-browse").innerText).contains("Search");
|
||||
|
||||
await menu.openPage("settings");
|
||||
await t
|
||||
.click(settings.languageInput)
|
||||
.hover(Selector("div").withText("Deutsch").parent('div[role="listitem"]'))
|
||||
.click(Selector("div").withText("Deutsch").parent('div[role="listitem"]'));
|
||||
await t.eval(() => location.reload());
|
||||
|
||||
await t.expect(Selector(".nav-browse").innerText).contains("Suche");
|
||||
|
||||
await t
|
||||
.click(settings.languageInput)
|
||||
.hover(Selector("div").withText("English").parent('div[role="listitem"]'))
|
||||
.click(Selector("div").withText("English").parent('div[role="listitem"]'));
|
||||
|
||||
await t.expect(Selector(".nav-browse").innerText).contains("Search");
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "settings-general-003").meta({ type: "smoke" })(
|
||||
"Disable pages: import, originals, logs, moments, places, library",
|
||||
async (t) => {
|
||||
await t.expect(page.cardLocation.exists).ok();
|
||||
|
||||
await menu.openPage("library");
|
||||
|
||||
await t
|
||||
.expect(library.importTab.visible)
|
||||
.ok()
|
||||
.expect(library.logsTab.visible)
|
||||
.ok()
|
||||
.expect(library.indexTab.visible)
|
||||
.ok();
|
||||
await menu.checkMenuItemAvailability("originals", true);
|
||||
await menu.checkMenuItemAvailability("folders", true);
|
||||
await menu.checkMenuItemAvailability("moments", true);
|
||||
await menu.checkMenuItemAvailability("places", true);
|
||||
await menu.checkMenuItemAvailability("library", true);
|
||||
|
||||
await menu.openPage("settings");
|
||||
await t
|
||||
.click(settings.importCheckbox)
|
||||
.click(settings.filesCheckbox)
|
||||
.click(settings.momentsCheckbox)
|
||||
.click(settings.logsCheckbox)
|
||||
.click(settings.placesCheckbox);
|
||||
await t.eval(() => location.reload());
|
||||
|
||||
await menu.openPage("browse");
|
||||
|
||||
await t.expect(page.cardLocation.exists).notOk();
|
||||
|
||||
await menu.openPage("library");
|
||||
|
||||
await t
|
||||
.expect(library.importTab.visible)
|
||||
.notOk()
|
||||
.expect(library.logsTab.visible)
|
||||
.notOk()
|
||||
.expect(library.indexTab.visible)
|
||||
.ok();
|
||||
await menu.checkMenuItemAvailability("originals", false);
|
||||
await menu.checkMenuItemAvailability("folders", true);
|
||||
await menu.checkMenuItemAvailability("moments", false);
|
||||
await menu.checkMenuItemAvailability("places", false);
|
||||
await menu.checkMenuItemAvailability("library", true);
|
||||
|
||||
await menu.openPage("settings");
|
||||
await t
|
||||
.click(settings.importCheckbox)
|
||||
.click(settings.filesCheckbox)
|
||||
.click(settings.momentsCheckbox)
|
||||
.click(settings.logsCheckbox)
|
||||
.click(settings.placesCheckbox)
|
||||
.click(settings.libraryCheckbox);
|
||||
|
||||
await menu.checkMenuItemAvailability("originals", false);
|
||||
await menu.checkMenuItemAvailability("folders", true);
|
||||
await menu.checkMenuItemAvailability("moments", true);
|
||||
await menu.checkMenuItemAvailability("places", true);
|
||||
await menu.checkMenuItemAvailability("library", false);
|
||||
|
||||
await t.click(settings.libraryCheckbox);
|
||||
|
||||
await menu.checkMenuItemAvailability("originals", true);
|
||||
await menu.checkMenuItemAvailability("library", true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "settings-general-004").meta({ type: "smoke" })(
|
||||
"Disable people and labels",
|
||||
async (t) => {
|
||||
await t.click(page.cardTitle.nth(0));
|
||||
await t.click(photoedit.labelsTab);
|
||||
|
||||
await t.expect(photoedit.addLabel.visible).ok();
|
||||
|
||||
await t.click(photoedit.peopleTab);
|
||||
|
||||
await t.expect(Selector("div.p-faces").visible).ok();
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await menu.checkMenuItemAvailability("people", true);
|
||||
await menu.checkMenuItemAvailability("labels", true);
|
||||
await menu.openPage("settings");
|
||||
await t.click(settings.peopleCheckbox).click(settings.labelsCheckbox);
|
||||
await t.eval(() => location.reload());
|
||||
await menu.openPage("browse");
|
||||
await t.click(page.cardTitle.nth(0));
|
||||
await t.click(photoedit.labelsTab);
|
||||
|
||||
await t.expect(photoedit.addLabel.exists).notOk();
|
||||
|
||||
await t.click(photoedit.peopleTab);
|
||||
|
||||
await t.expect(Selector("div.p-faces ").exists).notOk();
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
|
||||
await menu.checkMenuItemAvailability("people", false);
|
||||
await menu.checkMenuItemAvailability("labels", false);
|
||||
|
||||
await menu.openPage("settings");
|
||||
await t.click(settings.peopleCheckbox).click(settings.labelsCheckbox);
|
||||
|
||||
await menu.checkMenuItemAvailability("people", true);
|
||||
await menu.checkMenuItemAvailability("labels", true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "settings-general-005").meta({ type: "smoke" })(
|
||||
"Disable private, archive and quality filter",
|
||||
async (t) => {
|
||||
await menu.checkMenuItemAvailability("private", true);
|
||||
await menu.checkMenuItemAvailability("archive", true);
|
||||
await menu.checkMenuItemAvailability("review", true);
|
||||
|
||||
await toolbar.search("photo:true stack:true");
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("archive", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", true);
|
||||
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.infoTab);
|
||||
|
||||
await photoedit.checkFieldDisabled(photoedit.privateInput, false);
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
await toolbar.search("Viewpoint / Mexico / 2017");
|
||||
|
||||
await photo.checkPhotoVisibility("pqmxlr7188hz4bih", false);
|
||||
|
||||
await toolbar.search("Truck / Vancouver / 2019");
|
||||
|
||||
await photo.checkPhotoVisibility("pqmxlr0kg161o9ek", false);
|
||||
|
||||
await toolbar.search("Archive / 2020");
|
||||
|
||||
await photo.checkPhotoVisibility("pqnah1k2frui6p63", false);
|
||||
|
||||
await menu.openPage("settings");
|
||||
await t
|
||||
.click(settings.archiveCheckbox)
|
||||
.click(settings.privateCheckbox)
|
||||
.click(Selector(settings.libraryTab))
|
||||
.click(settings.reviewCheckbox);
|
||||
await t.eval(() => location.reload());
|
||||
|
||||
await menu.checkMenuItemAvailability("private", false);
|
||||
await menu.checkMenuItemAvailability("archive", false);
|
||||
await menu.checkMenuItemAvailability("review", false);
|
||||
|
||||
await menu.openPage("browse");
|
||||
|
||||
await toolbar.search("photo:true");
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("archive", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", false);
|
||||
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.infoTab);
|
||||
|
||||
//await photoedit.checkFieldDisabled(photoedit.privateInput, true);
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
await toolbar.search("Viewpoint / Mexico / 2017");
|
||||
|
||||
await photo.checkPhotoVisibility("pqmxlr7188hz4bih", true);
|
||||
|
||||
await toolbar.search("Truck / Vancouver / 2019");
|
||||
|
||||
await photo.checkPhotoVisibility("pqmxlr0kg161o9ek", false);
|
||||
|
||||
await toolbar.search("Archive / 2020");
|
||||
|
||||
await photo.checkPhotoVisibility("pqnah1k2frui6p63", true);
|
||||
|
||||
await menu.openPage("settings");
|
||||
await t
|
||||
.click(settings.privateCheckbox)
|
||||
.click(settings.archiveCheckbox)
|
||||
.click(Selector(settings.libraryTab))
|
||||
.click(settings.reviewCheckbox);
|
||||
|
||||
await menu.checkMenuItemAvailability("archive", true);
|
||||
await menu.checkMenuItemAvailability("private", true);
|
||||
await menu.checkMenuItemAvailability("review", true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "settings-general-006").meta({ type: "smoke" })(
|
||||
"Disable upload, download, edit and share",
|
||||
async (t) => {
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
|
||||
await toolbar.search("photo:true stack:true");
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
|
||||
await photoedit.checkAllDetailsFieldsDisabled(false);
|
||||
await t.expect(photoedit.infoTab.visible).ok();
|
||||
await t.click(photoedit.filesTab);
|
||||
|
||||
await t
|
||||
.expect(photoedit.downloadFile.nth(0).visible)
|
||||
.ok()
|
||||
.click(photoedit.toggleExpandFile.nth(1))
|
||||
.expect(photoedit.downloadFile.nth(1).visible)
|
||||
.ok()
|
||||
.expect(photoedit.deleteFile.visible)
|
||||
.ok()
|
||||
.click(photoedit.dialogClose);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await photoviewer.openPhotoViewer("nth", 0);
|
||||
|
||||
await photoviewer.checkPhotoViewerActionAvailability("download", true);
|
||||
|
||||
await photoviewer.triggerPhotoViewerAction("close");
|
||||
await menu.openPage("settings");
|
||||
|
||||
await t
|
||||
.click(settings.uploadCheckbox)
|
||||
.click(settings.downloadCheckbox)
|
||||
.click(settings.editCheckbox)
|
||||
.click(settings.shareCheckbox);
|
||||
await t.eval(() => location.reload());
|
||||
await t.navigateTo("/calendar");
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await album.checkHoverActionAvailability("nth", 2, "share", false);
|
||||
|
||||
await album.triggerHoverAction("nth", 0, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await toolbar.checkToolbarActionAvailability("download", false);
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
await toolbar.checkToolbarActionAvailability("edit", true);
|
||||
|
||||
await t.navigateTo("/folders");
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", false);
|
||||
|
||||
await album.triggerHoverAction("nth", 0, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await toolbar.checkToolbarActionAvailability("download", false);
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
await toolbar.checkToolbarActionAvailability("edit", true);
|
||||
|
||||
await t.navigateTo("/albums");
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", false);
|
||||
|
||||
await album.triggerHoverAction("nth", 0, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await toolbar.checkToolbarActionAvailability("download", false);
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
await toolbar.checkToolbarActionAvailability("edit", true);
|
||||
|
||||
await t.navigateTo("/browse");
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
|
||||
await toolbar.search("photo:true stack:true");
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("download", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle.nth(0));
|
||||
|
||||
await photoedit.checkAllDetailsFieldsDisabled(true);
|
||||
await t.expect(photoedit.infoTab.visible).notOk();
|
||||
|
||||
await t.click(photoedit.filesTab);
|
||||
|
||||
await t
|
||||
.expect(photoedit.downloadFile.nth(0).visible)
|
||||
.notOk()
|
||||
.click(photoedit.toggleExpandFile.nth(1))
|
||||
.expect(photoedit.downloadFile.nth(1).visible)
|
||||
.notOk()
|
||||
.expect(photoedit.deleteFile.visible)
|
||||
.ok();
|
||||
|
||||
await t.click(photoedit.dialogClose);
|
||||
await toolbar.search("photo:true");
|
||||
await photoviewer.openPhotoViewer("nth", 0);
|
||||
await photoviewer.checkPhotoViewerActionAvailability("download", false);
|
||||
await photoviewer.checkPhotoViewerActionAvailability("edit", true);
|
||||
await photoviewer.triggerPhotoViewerAction("close");
|
||||
|
||||
await menu.openPage("settings");
|
||||
await t
|
||||
.click(settings.uploadCheckbox)
|
||||
.click(settings.downloadCheckbox)
|
||||
.click(settings.editCheckbox)
|
||||
.click(settings.shareCheckbox);
|
||||
}
|
||||
);
|
123
frontend/tests/acceptance-new/stacks.js
Normal file
123
frontend/tests/acceptance-new/stacks.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import Photo from "../page-model/photo";
|
||||
import PhotoViewer from "../page-model/photoviewer";
|
||||
import Page from "../page-model/page";
|
||||
import PhotoEdit from "../page-model/photo-edit";
|
||||
import Library from "../page-model/library";
|
||||
|
||||
fixture`Test stacks`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const toolbar = new Toolbar();
|
||||
const photo = new Photo();
|
||||
const photoviewer = new PhotoViewer();
|
||||
const page = new Page();
|
||||
const photoedit = new PhotoEdit();
|
||||
const library = new Library();
|
||||
|
||||
test.meta("testID", "stacks-001").meta({ type: "smoke" })(
|
||||
"View all files of a stack",
|
||||
async (t) => {
|
||||
await toolbar.search("ski");
|
||||
const SequentialPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await photo.checkHoverActionAvailability("uid", SequentialPhotoUid, "open", true);
|
||||
if (t.browser.platform === "desktop") {
|
||||
console.log(t.browser.platform);
|
||||
await photo.triggerHoverAction("nth", 0, "open");
|
||||
await photoviewer.triggerPhotoViewerAction("next");
|
||||
await photoviewer.triggerPhotoViewerAction("previous");
|
||||
await photoviewer.triggerPhotoViewerAction("close");
|
||||
}
|
||||
await photo.checkHoverActionAvailability("uid", SequentialPhotoUid, "open", true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "stacks-002").meta({ type: "smoke" })("Change primary file", async (t) => {
|
||||
await toolbar.search("ski");
|
||||
const SequentialPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
await t
|
||||
.click(page.cardTitle.withAttribute("data-uid", SequentialPhotoUid))
|
||||
.click(photoedit.filesTab);
|
||||
const FirstFileName = await Selector("div.caption").nth(0).innerText;
|
||||
|
||||
await t.expect(FirstFileName).contains("photos8_1_ski.jpg");
|
||||
|
||||
await t
|
||||
.click(photoedit.toggleExpandFile.nth(1))
|
||||
.click(photoedit.makeFilePrimary)
|
||||
.click(photoedit.dialogClose)
|
||||
.click(page.cardTitle.withAttribute("data-uid", SequentialPhotoUid));
|
||||
const FirstFileNameAfterChange = await Selector("div.caption").nth(0).innerText;
|
||||
|
||||
await t
|
||||
.expect(FirstFileNameAfterChange)
|
||||
.notContains("photos8_1_ski.jpg")
|
||||
.expect(FirstFileNameAfterChange)
|
||||
.contains("photos8_2_ski.jpg");
|
||||
});
|
||||
|
||||
test.meta("testID", "stacks-003").meta({ type: "smoke" })("Ungroup files", async (t) => {
|
||||
await toolbar.search("group");
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
const PhotoCount = await photo.getPhotoCount("all");
|
||||
const SequentialPhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
|
||||
await t.expect(PhotoCount).eql(1);
|
||||
|
||||
await menu.openPage("stacks");
|
||||
await photo.checkHoverActionAvailability("uid", SequentialPhotoUid, "open", true);
|
||||
await t
|
||||
.click(page.cardTitle.withAttribute("data-uid", SequentialPhotoUid))
|
||||
.click(photoedit.filesTab)
|
||||
.click(photoedit.toggleExpandFile.nth(0))
|
||||
.click(photoedit.toggleExpandFile.nth(1))
|
||||
.click(photoedit.unstackFile)
|
||||
.wait(12000)
|
||||
.click(photoedit.dialogClose);
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("group");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.eval(() => location.reload());
|
||||
} else {
|
||||
await toolbar.triggerToolbarAction("reload");
|
||||
}
|
||||
const PhotoCountAfterUngroup = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountAfterUngroup).eql(2);
|
||||
await photo.checkHoverActionAvailability("uid", SequentialPhotoUid, "open", false);
|
||||
});
|
||||
|
||||
test.meta("testID", "stacks-004")("Delete non primary file", async (t) => {
|
||||
await menu.openPage("library");
|
||||
await t
|
||||
.click(library.importTab)
|
||||
.click(library.openImportFolderSelect, { timeout: 5000 })
|
||||
.click(page.selectOption.withText("/pizza"))
|
||||
.click(library.import)
|
||||
.wait(10000);
|
||||
await menu.openPage("browse");
|
||||
await toolbar.search("pizza");
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
const PhotoCount = await photo.getPhotoCount("all");
|
||||
const PhotoUid = await photo.getNthPhotoUid("all", 0);
|
||||
|
||||
await t.expect(PhotoCount).eql(1);
|
||||
|
||||
await t.click(page.cardTitle.withAttribute("data-uid", PhotoUid)).click(photoedit.filesTab);
|
||||
const FileCount = await photoedit.getFileCount();
|
||||
|
||||
await t.expect(FileCount).eql(2);
|
||||
|
||||
await t
|
||||
.click(photoedit.toggleExpandFile.nth(1))
|
||||
.click(Selector(photoedit.deleteFile))
|
||||
.click(Selector(".action-confirm"))
|
||||
.wait(10000);
|
||||
const FileCountAfterDeletion = await photoedit.getFileCount();
|
||||
|
||||
await t.expect(FileCountAfterDeletion).eql(1);
|
||||
});
|
148
frontend/tests/acceptance-new/states.js
Normal file
148
frontend/tests/acceptance-new/states.js
Normal file
|
@ -0,0 +1,148 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "./testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Album from "../page-model/album";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Page from "../page-model/page";
|
||||
import AlbumDialog from "../page-model/dialog-album";
|
||||
|
||||
fixture`Test states`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const album = new Album();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const photo = new Photo();
|
||||
const page = new Page();
|
||||
const albumdialog = new AlbumDialog();
|
||||
|
||||
test.meta("testID", "states-001")("Update state details", async (t) => {
|
||||
await menu.openPage("states");
|
||||
await toolbar.search("Canada");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("British Columbia");
|
||||
|
||||
await t.click(page.cardTitle.nth(0));
|
||||
|
||||
await t
|
||||
.expect(albumdialog.title.value)
|
||||
.eql("British Columbia")
|
||||
.expect(albumdialog.location.value)
|
||||
.eql("Canada")
|
||||
.expect(albumdialog.description.value)
|
||||
.eql("")
|
||||
.expect(albumdialog.category.value)
|
||||
.eql("");
|
||||
|
||||
await t
|
||||
.typeText(albumdialog.title, "Wonderland", { replace: true })
|
||||
.typeText(albumdialog.location, "Earth", { replace: true })
|
||||
.typeText(albumdialog.description, "We love earth")
|
||||
.typeText(albumdialog.category, "Mountains")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.dialogSave);
|
||||
|
||||
await t
|
||||
.expect(page.cardTitle.nth(0).innerText)
|
||||
.contains("Wonderland")
|
||||
.expect(page.cardDescription.nth(0).innerText)
|
||||
.contains("We love earth")
|
||||
.expect(Selector("div.caption").nth(1).innerText)
|
||||
.contains("Mountains")
|
||||
.expect(Selector("div.caption").nth(2).innerText)
|
||||
.contains("Earth");
|
||||
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await t.expect(toolbar.toolbarTitle.innerText).contains("Wonderland");
|
||||
await t.expect(toolbar.toolbarDescription.innerText).contains("We love earth");
|
||||
|
||||
await menu.openPage("states");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await toolbar.search("category:Mountains");
|
||||
} else {
|
||||
await toolbar.setFilter("category", "Mountains");
|
||||
}
|
||||
|
||||
await t.expect(page.cardTitle.nth(0).innerText).contains("Wonderland");
|
||||
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
await toolbar.triggerToolbarAction("edit");
|
||||
|
||||
await t
|
||||
.expect(albumdialog.description.value)
|
||||
.eql("We love earth")
|
||||
.expect(albumdialog.category.value)
|
||||
.eql("Mountains")
|
||||
.expect(albumdialog.location.value)
|
||||
.eql("Earth");
|
||||
|
||||
await t
|
||||
.typeText(albumdialog.title, "British Columbia / Canada", { replace: true })
|
||||
.click(albumdialog.category)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.click(albumdialog.description)
|
||||
.pressKey("ctrl+a delete")
|
||||
.pressKey("enter")
|
||||
.typeText(albumdialog.location, "Canada", { replace: true })
|
||||
.click(albumdialog.dialogSave);
|
||||
await menu.openPage("states");
|
||||
await toolbar.search("Canada");
|
||||
|
||||
await t
|
||||
.expect(page.cardTitle.nth(0).innerText)
|
||||
.contains("British Columbia / Canada")
|
||||
.expect(page.cardDescription.innerText)
|
||||
.notContains("We love earth")
|
||||
.expect(Selector("div.caption").nth(0).innerText)
|
||||
.notContains("Earth");
|
||||
});
|
||||
|
||||
test.meta("testID", "states-002")("Create, Edit, delete sharing link for state", async (t) => {
|
||||
await page.testCreateEditDeleteSharingLink("states");
|
||||
});
|
||||
|
||||
test.meta("testID", "states-003")("Create/delete album-clone from state", async (t) => {
|
||||
await menu.openPage("albums");
|
||||
const AlbumCount = await album.getAlbumCount("all");
|
||||
await menu.openPage("states");
|
||||
await toolbar.search("Canada");
|
||||
const FirstStateUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(FirstStateUid);
|
||||
const PhotoCountInState = await photo.getPhotoCount("all");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("image", 1);
|
||||
await menu.openPage("states");
|
||||
await album.selectAlbumFromUID(FirstStateUid);
|
||||
await contextmenu.triggerContextMenuAction("clone", "NotYetExistingAlbumForState");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterCreation = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterCreation).eql(AlbumCount + 1);
|
||||
|
||||
await toolbar.search("NotYetExistingAlbumForState");
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.openAlbumWithUid(AlbumUid);
|
||||
const PhotoCountInAlbum = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountInAlbum).eql(PhotoCountInState);
|
||||
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
await menu.openPage("albums");
|
||||
await album.selectAlbumFromUID(AlbumUid);
|
||||
await contextmenu.triggerContextMenuAction("delete", "");
|
||||
await menu.openPage("albums");
|
||||
const AlbumCountAfterDelete = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAfterDelete).eql(AlbumCount);
|
||||
|
||||
await menu.openPage("states");
|
||||
await album.openAlbumWithUid(FirstStateUid);
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, true);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, true);
|
||||
});
|
4
frontend/tests/acceptance-new/testcafeconfig.json
Normal file
4
frontend/tests/acceptance-new/testcafeconfig.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
|
||||
"url": "localhost:2343/browse"
|
||||
}
|
BIN
frontend/tests/acceptance-new/upload-files/digikam.jpg
Normal file
BIN
frontend/tests/acceptance-new/upload-files/digikam.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 195 KiB |
120
frontend/tests/acceptance-new/upload-files/digikam.json
Normal file
120
frontend/tests/acceptance-new/upload-files/digikam.json
Normal file
|
@ -0,0 +1,120 @@
|
|||
[{
|
||||
"SourceFile": "digikam.jpg",
|
||||
"ExifToolVersion": 11.88,
|
||||
"FileName": "digikam.jpg",
|
||||
"Directory": ".",
|
||||
"FileSize": "195 kB",
|
||||
"FileModifyDate": "2020:10:19 10:29:43+02:00",
|
||||
"FileAccessDate": "2020:10:19 10:29:43+02:00",
|
||||
"FileInodeChangeDate": "2020:10:19 10:29:43+02:00",
|
||||
"FilePermissions": "rw-rw-r--",
|
||||
"FileType": "JPEG",
|
||||
"FileTypeExtension": "jpg",
|
||||
"MIMEType": "image/jpeg",
|
||||
"JFIFVersion": 1.01,
|
||||
"CurrentIPTCDigest": "92f4f2920e4cd46aa5c45f9d2ce7affa",
|
||||
"CodedCharacterSet": "UTF8",
|
||||
"Keywords": ["Berlin","Shop"],
|
||||
"ExifByteOrder": "Big-endian (Motorola, MM)",
|
||||
"Make": "HUAWEI",
|
||||
"Model": "ELE-L29",
|
||||
"Orientation": "Unknown (0)",
|
||||
"XResolution": 72,
|
||||
"YResolution": 72,
|
||||
"ResolutionUnit": "inches",
|
||||
"Software": "ELE-L29 10.1.0.150(C431E22R2P5)",
|
||||
"ModifyDate": "2020:10:17 17:48:24",
|
||||
"YCbCrPositioning": "Centered",
|
||||
"DocumentName": "",
|
||||
"ExposureTime": "1/50",
|
||||
"FNumber": 1.8,
|
||||
"ExposureProgram": "Program AE",
|
||||
"ISO": 100,
|
||||
"ExifVersion": "0210",
|
||||
"DateTimeOriginal": "2020:10:17 17:48:24",
|
||||
"CreateDate": "2020:10:17 17:48:24",
|
||||
"ComponentsConfiguration": "Y, Cb, Cr, -",
|
||||
"CompressedBitsPerPixel": 0.95,
|
||||
"ShutterSpeedValue": "1/999963365",
|
||||
"ApertureValue": 1.8,
|
||||
"BrightnessValue": 0,
|
||||
"ExposureCompensation": 0,
|
||||
"MaxApertureValue": 1.8,
|
||||
"MeteringMode": "Multi-segment",
|
||||
"LightSource": "Daylight",
|
||||
"Flash": "No Flash",
|
||||
"FocalLength": "5.6 mm",
|
||||
"MakerNoteUnknownText": "Auto",
|
||||
"SubSecTime": 950488,
|
||||
"SubSecTimeOriginal": 950488,
|
||||
"SubSecTimeDigitized": 950488,
|
||||
"FlashpixVersion": "0100",
|
||||
"ColorSpace": "sRGB",
|
||||
"ExifImageWidth": 3648,
|
||||
"ExifImageHeight": 2736,
|
||||
"InteropIndex": "R98 - DCF basic file (sRGB)",
|
||||
"InteropVersion": "0100",
|
||||
"SensingMethod": "One-chip color area",
|
||||
"FileSource": "Digital Camera",
|
||||
"SceneType": "Directly photographed",
|
||||
"CustomRendered": "Custom",
|
||||
"ExposureMode": "Auto",
|
||||
"WhiteBalance": "Auto",
|
||||
"DigitalZoomRatio": 1,
|
||||
"FocalLengthIn35mmFormat": "27 mm",
|
||||
"SceneCaptureType": "Standard",
|
||||
"GainControl": "None",
|
||||
"Contrast": "Normal",
|
||||
"Saturation": "Normal",
|
||||
"Sharpness": "Normal",
|
||||
"SubjectDistanceRange": "Unknown",
|
||||
"GPSVersionID": "2.2.0.0",
|
||||
"GPSLatitudeRef": "North",
|
||||
"GPSLongitudeRef": "East",
|
||||
"GPSAltitudeRef": "Above Sea Level",
|
||||
"GPSTimeStamp": "15:48:23",
|
||||
"GPSProcessingMethod": "GPS",
|
||||
"GPSDateStamp": "2020:10:17",
|
||||
"DeviceSettingDescription": "(Binary data 4 bytes, use -b option to extract)",
|
||||
"Compression": "JPEG (old-style)",
|
||||
"ThumbnailOffset": 83310,
|
||||
"ThumbnailLength": 27920,
|
||||
"XMPToolkit": "XMP Core 4.4.0-Exiv2",
|
||||
"Warning": "[minor] Fixed incorrect URI for xmlns:MicrosoftPhoto",
|
||||
"Rating": 4,
|
||||
"Categories": "<Categories><Category Assigned=\"1\">Berlin</Category><Category Assigned=\"1\">Shop</Category></Categories>",
|
||||
"RatingPercent": 75,
|
||||
"ColorLabel": 9,
|
||||
"PickLabel": 3,
|
||||
"Urgency": "9 (user-defined priority)",
|
||||
"LastKeywordXMP": ["Berlin","Shop"],
|
||||
"TagsList": ["Berlin","Shop"],
|
||||
"HierarchicalSubject": ["Berlin","Shop"],
|
||||
"CatalogSets": ["Berlin","Shop"],
|
||||
"Subject": ["Berlin","Shop"],
|
||||
"ImageWidth": 500,
|
||||
"ImageHeight": 375,
|
||||
"EncodingProcess": "Baseline DCT, Huffman coding",
|
||||
"BitsPerSample": 8,
|
||||
"ColorComponents": 3,
|
||||
"YCbCrSubSampling": "YCbCr4:2:0 (2 2)",
|
||||
"Aperture": 1.8,
|
||||
"ImageSize": "500x375",
|
||||
"Megapixels": 0.188,
|
||||
"ScaleFactor35efl": 4.8,
|
||||
"ShutterSpeed": "1/50",
|
||||
"SubSecCreateDate": "2020:10:17 17:48:24.950488",
|
||||
"SubSecDateTimeOriginal": "2020:10:17 17:48:24.950488",
|
||||
"SubSecModifyDate": "2020:10:17 17:48:24.950488",
|
||||
"ThumbnailImage": "(Binary data 27920 bytes, use -b option to extract)",
|
||||
"GPSAltitude": "84.4 m Above Sea Level",
|
||||
"GPSDateTime": "2020:10:17 15:48:23Z",
|
||||
"GPSLatitude": "52 deg 27' 37.88\" N",
|
||||
"GPSLongitude": "13 deg 19' 53.05\" E",
|
||||
"CircleOfConfusion": "0.006 mm",
|
||||
"FOV": "67.4 deg",
|
||||
"FocalLength35efl": "5.6 mm (35 mm equivalent: 27.0 mm)",
|
||||
"GPSPosition": "52 deg 27' 37.88\" N, 13 deg 19' 53.05\" E",
|
||||
"HyperfocalDistance": "2.79 m",
|
||||
"LightValue": 7.3
|
||||
}]
|
1
frontend/tests/acceptance-new/upload-files/foo.txt
Normal file
1
frontend/tests/acceptance-new/upload-files/foo.txt
Normal file
|
@ -0,0 +1 @@
|
|||
example text file, you can ignore this
|
BIN
frontend/tests/acceptance-new/upload-files/hentai_2.jpg
Normal file
BIN
frontend/tests/acceptance-new/upload-files/hentai_2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
BIN
frontend/tests/acceptance-new/upload-files/korn.mp4
Normal file
BIN
frontend/tests/acceptance-new/upload-files/korn.mp4
Normal file
Binary file not shown.
BIN
frontend/tests/acceptance-new/upload-files/ladybug.jpg
Normal file
BIN
frontend/tests/acceptance-new/upload-files/ladybug.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 93 KiB |
123
frontend/tests/acceptance-openid/authentication.js
Normal file
123
frontend/tests/acceptance-openid/authentication.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "../acceptance/testcafeconfig";
|
||||
import Page from "../acceptance/page-model";
|
||||
|
||||
fixture`Test authentication with openid`.page`https://photoprism.traefik.net`;
|
||||
|
||||
const page = new Page();
|
||||
test.meta("testID", "authentication-000")(
|
||||
"Time to start instance (will be marked as unstable)",
|
||||
async (t) => {
|
||||
await t.wait(5000);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "authentication-001")("Login and Logout as admin", async (t) => {
|
||||
await t
|
||||
.expect(Selector("#username").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk()
|
||||
.typeText(Selector("#username"), "admin", { replace: true })
|
||||
.typeText(Selector("#password"), "photoprism", { replace: true })
|
||||
.click(Selector("#kc-login"))
|
||||
.expect(Selector(".input-search input", { timeout: 7000 }).visible)
|
||||
.ok();
|
||||
await page.openNav();
|
||||
await t
|
||||
.click(Selector('div[title="Logout"]'))
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
await t.navigateTo("/settings");
|
||||
await t
|
||||
.expect(Selector("#username").visible)
|
||||
.notOk()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.ok();
|
||||
await t
|
||||
/* TODO
|
||||
await t.navigateTo("/library");
|
||||
.expect(Selector(".input-index-folder input").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk()*/
|
||||
.click(Selector('div[title="Logout"]'))
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.navigateTo("https://keycloak.traefik.net")
|
||||
.click(Selector('a[href="https://keycloak.traefik.net/auth/admin/"]'))
|
||||
.click(Selector("a.dropdown-toggle"))
|
||||
.click(Selector("a").withText("Sign Out"));
|
||||
await t.navigateTo("https://photoprism.traefik.net/settings");
|
||||
await t
|
||||
.expect(Selector("#username").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
});
|
||||
|
||||
test.meta("testID", "authentication-001")("Login and Logout as user", async (t) => {
|
||||
await t
|
||||
.expect(Selector("#username").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk()
|
||||
.typeText(Selector("#username"), "user", { replace: true })
|
||||
.typeText(Selector("#password"), "photoprism", { replace: true })
|
||||
.click(Selector("#kc-login"))
|
||||
.expect(Selector(".input-search input", { timeout: 7000 }).visible)
|
||||
.ok();
|
||||
await page.openNav();
|
||||
await t
|
||||
.click(Selector('div[title="Logout"]'))
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
await t.navigateTo("/settings");
|
||||
await t
|
||||
.expect(Selector("#username").visible)
|
||||
.notOk()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.ok();
|
||||
await t.navigateTo("/library");
|
||||
await t
|
||||
.expect(Selector(".input-index-folder input").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok()
|
||||
.click(Selector('div[title="Logout"]'))
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.navigateTo("https://keycloak.traefik.net")
|
||||
.click(Selector('a[href="https://keycloak.traefik.net/auth/admin/"]'))
|
||||
.click(Selector("a.dropdown-toggle"))
|
||||
.click(Selector("a").withText("Sign Out"));
|
||||
await t.navigateTo("https://photoprism.traefik.net/settings");
|
||||
await t
|
||||
.expect(Selector("#username").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
});
|
||||
|
||||
test.meta("testID", "authentication-002")("Login with wrong credentials", async (t) => {
|
||||
await t
|
||||
.expect(Selector("#username").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk()
|
||||
.typeText(Selector("#username"), "admin", { replace: true })
|
||||
.typeText(Selector("#password"), "wrong", { replace: true })
|
||||
.click(Selector("#kc-login"))
|
||||
.expect(Selector(".input-search input", { timeout: 7000 }).visible)
|
||||
.notOk();
|
||||
await t
|
||||
.navigateTo("https://photoprism.traefik.net/favorites")
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
});
|
691
frontend/tests/acceptance-private/admin-role.js
Normal file
691
frontend/tests/acceptance-private/admin-role.js
Normal file
|
@ -0,0 +1,691 @@
|
|||
import { Selector } from "testcafe";
|
||||
import { ClientFunction } from "testcafe";
|
||||
import testcafeconfig from "../acceptance/testcafeconfig";
|
||||
import Menu from "../page-model/menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Label from "../page-model/label";
|
||||
import Album from "../page-model/album";
|
||||
import Subject from "../page-model/subject";
|
||||
import Page from "../page-model/page";
|
||||
import Settings from "../page-model/settings";
|
||||
import Library from "../page-model/library";
|
||||
import PhotoEdit from "../page-model/photo-edit";
|
||||
import AlbumDialog from "../page-model/dialog-album";
|
||||
|
||||
fixture`Test admin role`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const photo = new Photo();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const label = new Label();
|
||||
const album = new Album();
|
||||
const subject = new Subject();
|
||||
const page = new Page();
|
||||
const settings = new Settings();
|
||||
const library = new Library();
|
||||
const photoedit = new PhotoEdit();
|
||||
const albumdialog = new AlbumDialog();
|
||||
|
||||
const getLocation = ClientFunction(() => document.location.href);
|
||||
|
||||
test.meta("testID", "authentication-000")(
|
||||
"Time to start instance (will be marked as unstable)",
|
||||
async (t) => {
|
||||
await t.wait(5000);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "admin-role-001").meta({ type: "smoke" })("Access to settings", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
|
||||
await menu.checkMenuItemAvailability("settings", true);
|
||||
|
||||
await t.navigateTo("/settings");
|
||||
await t
|
||||
.expect(settings.languageInput.visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk();
|
||||
|
||||
await t.navigateTo("/settings/library");
|
||||
|
||||
await t
|
||||
.expect(Selector("form.p-form-settings").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk();
|
||||
|
||||
await t.navigateTo("/settings/advanced");
|
||||
|
||||
await t
|
||||
.expect(Selector("label").withText("Read-Only Mode").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk();
|
||||
|
||||
await t.navigateTo("/settings/sync");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-accounts-list").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk();
|
||||
});
|
||||
|
||||
test.meta("testID", "admin-role-002").meta({ type: "smoke" })("Access to archive", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
|
||||
await menu.checkMenuItemAvailability("archive", true);
|
||||
|
||||
await t.navigateTo("/archive");
|
||||
|
||||
await photo.checkPhotoVisibility("pqnahct2mvee8sr4", true);
|
||||
|
||||
const PhotoCountArchive = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountBrowse).gte(PhotoCountArchive);
|
||||
});
|
||||
|
||||
test.meta("testID", "admin-role-003").meta({ type: "smoke" })("Access to review", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
|
||||
await menu.checkMenuItemAvailability("review", true);
|
||||
|
||||
await t.navigateTo("/review");
|
||||
|
||||
await photo.checkPhotoVisibility("pqzuein2pdcg1kc7", true);
|
||||
|
||||
const PhotoCountReview = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountBrowse).gte(PhotoCountReview);
|
||||
});
|
||||
|
||||
test.meta("testID", "admin-role-004").meta({ type: "smoke" })("Access to private", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
|
||||
await menu.checkMenuItemAvailability("private", true);
|
||||
|
||||
await t.navigateTo("/private");
|
||||
|
||||
await photo.checkPhotoVisibility("pqmxlquf9tbc8mk2", true);
|
||||
|
||||
const PhotoCountPrivate = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountBrowse).gte(PhotoCountPrivate);
|
||||
});
|
||||
|
||||
test.meta("testID", "admin-role-005").meta({ type: "smoke" })("Access to library", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
|
||||
await menu.checkMenuItemAvailability("library", true);
|
||||
|
||||
await t.navigateTo("/library");
|
||||
|
||||
await t
|
||||
.expect(library.indexFolderSelect.visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk();
|
||||
|
||||
await t.navigateTo("/library/import");
|
||||
|
||||
await t
|
||||
.expect(library.openImportFolderSelect.visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk();
|
||||
|
||||
await t.navigateTo("/library/logs");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.terminal").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk();
|
||||
await menu.checkMenuItemAvailability("originals", true);
|
||||
|
||||
await t.navigateTo("/library/files");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-page-files").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk();
|
||||
await menu.checkMenuItemAvailability("hidden", true);
|
||||
|
||||
await t.navigateTo("/library/hidden");
|
||||
const PhotoCountHidden = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountBrowse).gte(PhotoCountHidden);
|
||||
await menu.checkMenuItemAvailability("errors", true);
|
||||
|
||||
await t.navigateTo("/library/errors");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-page-errors").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.notOk();
|
||||
});
|
||||
|
||||
test.meta("testID", "admin-role-006").meta({ type: "smoke" })(
|
||||
"private/archived photos in search results",
|
||||
async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
await toolbar.search("private:true");
|
||||
const PhotoCountPrivate = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountPrivate).eql(2);
|
||||
await photo.checkPhotoVisibility("pqmxlquf9tbc8mk2", true);
|
||||
|
||||
await toolbar.search("archived:true");
|
||||
const PhotoCountArchive = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountArchive).eql(3);
|
||||
await photo.checkPhotoVisibility("pqnahct2mvee8sr4", true);
|
||||
|
||||
await toolbar.search("quality:0");
|
||||
const PhotoCountReview = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountReview).gte(PhotoCountBrowse);
|
||||
await photo.checkPhotoVisibility("pqzuein2pdcg1kc7", true);
|
||||
|
||||
await menu.openPage("places");
|
||||
|
||||
await t
|
||||
.expect(Selector("#map").exists, { timeout: 15000 })
|
||||
.ok()
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok()
|
||||
.wait(5000);
|
||||
|
||||
await t
|
||||
.typeText(Selector('input[aria-label="Search"]'), "oaxaca", { replace: true })
|
||||
.pressKey("enter");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok()
|
||||
.expect(getLocation())
|
||||
.contains("oaxaca")
|
||||
.wait(5000)
|
||||
.expect(Selector('div[title="Viewpoint / Mexico / 2017"]').visible)
|
||||
.notOk()
|
||||
.expect(Selector('div[title="Viewpoint / Mexico / 2018"]').visible)
|
||||
.notOk();
|
||||
|
||||
await t
|
||||
.typeText(Selector('input[aria-label="Search"]'), "canada", { replace: true })
|
||||
.pressKey("enter");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok()
|
||||
.expect(getLocation())
|
||||
.contains("canada")
|
||||
.wait(8000)
|
||||
.expect(Selector('div[title="Cape / Bowen Island / 2019"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('div[title="Truck / Vancouver / 2019"]').visible)
|
||||
.notOk();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "admin-role-007").meta({ type: "smoke" })("Upload functionality", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await menu.openPage("albums");
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await menu.openPage("video");
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await menu.openPage("favorites");
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await menu.openPage("moments");
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await menu.openPage("calendar");
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await menu.openPage("states");
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await menu.openPage("folders");
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", true);
|
||||
});
|
||||
|
||||
test.meta("testID", "admin-role-008").meta({ type: "smoke" })(
|
||||
"Admin can private, archive, share, add/remove to album",
|
||||
async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
await photo.selectPhotoFromUID(FirstPhotoUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("private", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("archive", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("album", true);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await toolbar.setFilter("view", "List");
|
||||
|
||||
await photo.checkListViewActionAvailability("private", false);
|
||||
|
||||
await menu.openPage("albums");
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", true);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("private", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("album", true);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await toolbar.triggerToolbarAction("list-view");
|
||||
|
||||
await photo.checkListViewActionAvailability("private", false);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "admin-role-009").meta({ type: "smoke" })(
|
||||
"Admin can approve low quality photos",
|
||||
async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await toolbar.search('quality:0 name:"photos-013_1"');
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
|
||||
await t.expect(photoedit.detailsApprove.visible).ok();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "admin-role-010").meta({ type: "smoke" })(
|
||||
"Edit dialog is not read only for admin",
|
||||
async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await toolbar.search("faces:new");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
await photo.selectPhotoFromUID(FirstPhotoUid);
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
|
||||
await photoedit.checkAllDetailsFieldsDisabled(false);
|
||||
await t.expect(photoedit.detailsApply.visible).ok();
|
||||
if (t.browser.platform !== "mobile") {
|
||||
await t.expect(photoedit.detailsDone.visible).ok();
|
||||
}
|
||||
|
||||
await t.click(photoedit.labelsTab);
|
||||
|
||||
await photoedit.checkFieldDisabled(photoedit.removeLabel, false);
|
||||
await t.expect(photoedit.inputLabelName.exists).ok().expect(photoedit.addLabel.exists).ok();
|
||||
|
||||
await t.click(photoedit.openInlineEdit);
|
||||
|
||||
await t.expect(photoedit.inputLabelRename.exists).ok();
|
||||
|
||||
await t.click(photoedit.peopleTab);
|
||||
|
||||
await photoedit.checkFieldDisabled(photoedit.inputName, false);
|
||||
await t.expect(photoedit.removeMarker.exists).ok();
|
||||
|
||||
await t.click(photoedit.infoTab);
|
||||
|
||||
await photoedit.checkAllInfoFieldsDisabled(false);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "admin-role-011").meta({ type: "smoke" })(
|
||||
"Edit labels functionality",
|
||||
async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await menu.openPage("labels");
|
||||
const FirstLabelUid = await label.getNthLabeltUid(0);
|
||||
|
||||
await label.checkHoverActionState("uid", FirstLabelUid, "favorite", false);
|
||||
|
||||
await label.triggerHoverAction("uid", FirstLabelUid, "favorite");
|
||||
|
||||
await label.checkHoverActionState("uid", FirstLabelUid, "favorite", true);
|
||||
|
||||
await label.triggerHoverAction("uid", FirstLabelUid, "favorite");
|
||||
|
||||
await label.checkHoverActionState("uid", FirstLabelUid, "favorite", false);
|
||||
|
||||
await t.click(Selector(`a.uid-${FirstLabelUid} div.inline-edit`));
|
||||
|
||||
await t.expect(photoedit.inputLabelRename.visible).ok();
|
||||
|
||||
await label.selectLabelFromUID(FirstLabelUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("album", true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "admin-role-012").meta({ type: "smoke" })(
|
||||
"Edit album functionality",
|
||||
async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await menu.openPage("albums");
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("add", true);
|
||||
await album.checkHoverActionAvailability("nth", 1, "share", true);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", true);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).ok();
|
||||
|
||||
await t.click(albumdialog.dialogCancel);
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", true);
|
||||
await toolbar.checkToolbarActionAvailability("edit", true);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "admin-role-013")("Edit moment functionality", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await menu.openPage("moments");
|
||||
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", true);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("moment", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", true);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).ok();
|
||||
|
||||
await t.click(albumdialog.dialogCancel);
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", true);
|
||||
await toolbar.checkToolbarActionAvailability("edit", true);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("archive", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", false);
|
||||
});
|
||||
|
||||
test.meta("testID", "admin-role-014").meta({ type: "smoke" })(
|
||||
"Edit state functionality",
|
||||
async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await menu.openPage("states");
|
||||
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", true);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("state", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", true);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).ok();
|
||||
|
||||
await t.click(albumdialog.dialogCancel);
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", true);
|
||||
await toolbar.checkToolbarActionAvailability("edit", true);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("archive", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", false);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "admin-role-015")("Edit calendar functionality", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await menu.openPage("calendar");
|
||||
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", true);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("month", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).ok();
|
||||
|
||||
await t.click(albumdialog.dialogCancel);
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", true);
|
||||
await toolbar.checkToolbarActionAvailability("edit", true);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("archive", true);
|
||||
});
|
||||
|
||||
test.meta("testID", "admin-role-016")("Edit folder functionality", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await menu.openPage("folders");
|
||||
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", true);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("folder", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).ok();
|
||||
|
||||
await t.click(albumdialog.dialogCancel);
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", true);
|
||||
await toolbar.checkToolbarActionAvailability("edit", true);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("archive", true);
|
||||
});
|
||||
|
||||
test.meta("testID", "admin-role-017").meta({ type: "smoke" })(
|
||||
"Edit people functionality",
|
||||
async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await menu.openPage("people");
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("show-hidden", true);
|
||||
await t.expect(subject.newTab.exists).ok();
|
||||
await subject.checkSubjectVisibility("name", "Otto Visible", true);
|
||||
await subject.checkSubjectVisibility("name", "Monika Hide", false);
|
||||
|
||||
await toolbar.triggerToolbarAction("show-hidden");
|
||||
|
||||
await subject.checkSubjectVisibility("name", "Otto Visible", true);
|
||||
await subject.checkSubjectVisibility("name", "Monika Hide", true);
|
||||
|
||||
await t.click(Selector("a div.v-card__title").nth(0));
|
||||
|
||||
await t.expect(Selector("div.input-rename input").visible).ok();
|
||||
await subject.checkHoverActionAvailability("nth", 0, "hidden", true);
|
||||
|
||||
await subject.toggleSelectNthSubject(0);
|
||||
await contextmenu.checkContextMenuActionAvailability("album", "true");
|
||||
await contextmenu.clearSelection();
|
||||
|
||||
const FirstSubjectUid = await subject.getNthSubjectUid(0);
|
||||
|
||||
if (await Selector(`a.uid-${FirstSubjectUid}`).hasClass("is-favorite")) {
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", true);
|
||||
await subject.triggerHoverAction("uid", FirstSubjectUid, "favorite");
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", false);
|
||||
await subject.triggerHoverAction("uid", FirstSubjectUid, "favorite");
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", true);
|
||||
} else {
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", false);
|
||||
await subject.triggerHoverAction("uid", FirstSubjectUid, "favorite");
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", true);
|
||||
await subject.triggerHoverAction("uid", FirstSubjectUid, "favorite");
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", false);
|
||||
}
|
||||
|
||||
await subject.openNthSubject(0);
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
|
||||
await t.click(photoedit.peopleTab);
|
||||
|
||||
await photoedit.checkFieldDisabled(photoedit.inputName, false);
|
||||
await t.expect(photoedit.rejectName.hasClass("v-icon--disabled")).notOk();
|
||||
|
||||
await t.navigateTo("/people/new");
|
||||
|
||||
await t.expect(Selector("div.is-face").visible).ok();
|
||||
|
||||
await t.navigateTo("/people?hidden=yes&order=relevance");
|
||||
await t.expect(Selector("a.is-subject").visible).ok();
|
||||
}
|
||||
);
|
|
@ -1,120 +1,135 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "../acceptance/testcafeconfig";
|
||||
import Page from "../acceptance/page-model";
|
||||
import Page from "../page-model/page";
|
||||
import Account from "../page-model/account";
|
||||
import Menu from "../page-model/menu";
|
||||
|
||||
fixture`Test authentication`.page`${testcafeconfig.url}`;
|
||||
|
||||
const page = new Page();
|
||||
test.meta("testID", "authentication-000")(
|
||||
"Time to start instance (will be marked as unstable)",
|
||||
const account = new Account();
|
||||
const menu = new Menu();
|
||||
|
||||
test.meta("testID", "authentication-001").meta({ type: "smoke" })("Login and Logout", async (t) => {
|
||||
await t.navigateTo("/browse");
|
||||
|
||||
await t
|
||||
.expect(page.nameInput.visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
|
||||
await t.typeText(page.nameInput, "admin", { replace: true });
|
||||
|
||||
await t.expect(page.loginAction.hasAttribute("disabled", "disabled")).ok();
|
||||
|
||||
await t.typeText(page.passwordInput, "photoprism", { replace: true });
|
||||
|
||||
await t.expect(page.passwordInput.hasAttribute("type", "password")).ok();
|
||||
|
||||
await t.click(page.togglePasswordMode);
|
||||
|
||||
await t.expect(page.passwordInput.hasAttribute("type", "text")).ok();
|
||||
|
||||
await t.click(page.togglePasswordMode);
|
||||
|
||||
await t.expect(page.passwordInput.hasAttribute("type", "password")).ok();
|
||||
|
||||
await t.click(page.loginAction);
|
||||
|
||||
await t.expect(Selector(".input-search input", { timeout: 7000 }).visible).ok();
|
||||
|
||||
await page.logout();
|
||||
|
||||
await t
|
||||
.expect(page.nameInput.visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
|
||||
await t.navigateTo("/settings");
|
||||
await t
|
||||
.expect(page.nameInput.visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
});
|
||||
|
||||
test.meta("testID", "authentication-002").meta({ type: "smoke" })(
|
||||
"Login with wrong credentials",
|
||||
async (t) => {
|
||||
await t.wait(5000);
|
||||
await page.login("wrong", "photoprism");
|
||||
await t.navigateTo("/favorites");
|
||||
|
||||
await t
|
||||
.expect(page.nameInput.visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
|
||||
await page.login("admin", "abcdefg");
|
||||
await t.navigateTo("/archive");
|
||||
|
||||
await t
|
||||
.expect(page.nameInput.visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "authentication-001")("Login and Logout", async (t) => {
|
||||
//await t.wait(800000);
|
||||
test.meta("testID", "authentication-003").meta({ type: "smoke" })("Change password", async (t) => {
|
||||
await t.navigateTo("/browse");
|
||||
await t
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk()
|
||||
.typeText(Selector(".input-name input"), "admin", { replace: true })
|
||||
.expect(Selector(".action-confirm").hasAttribute("disabled", "disabled"))
|
||||
.ok()
|
||||
.typeText(Selector(".input-password input"), "photoprism", { replace: true })
|
||||
.expect(Selector(".input-password input").hasAttribute("type", "password"))
|
||||
.ok()
|
||||
.click(Selector(".v-input__icon--append"))
|
||||
.expect(Selector(".input-password input").hasAttribute("type", "text"))
|
||||
.ok()
|
||||
.click(Selector(".v-input__icon--append"))
|
||||
.expect(Selector(".input-password input").hasAttribute("type", "password"))
|
||||
.ok()
|
||||
.click(Selector(".action-confirm"))
|
||||
.expect(Selector(".input-search input", { timeout: 7000 }).visible)
|
||||
.ok();
|
||||
await page.openNav();
|
||||
await t
|
||||
.click(Selector('div[title="Logout"]'))
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
await t.navigateTo("/settings");
|
||||
await t
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
});
|
||||
|
||||
//TODO test all pages not accessible while logged out
|
||||
|
||||
test.meta("testID", "authentication-002")("Login with wrong credentials", async (t) => {
|
||||
await page.login("wrong", "photoprism");
|
||||
await t
|
||||
.navigateTo("/favorites")
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
await page.login("admin", "abcdefg");
|
||||
await t
|
||||
.navigateTo("/archive")
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
});
|
||||
|
||||
test.meta("testID", "authentication-003")("Change password", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await t.expect(Selector(".input-search input").visible).ok();
|
||||
await page.openNav();
|
||||
await t.expect(Selector(".input-search input", { timeout: 15000 }).visible).ok();
|
||||
|
||||
await menu.openPage("profile");
|
||||
await t
|
||||
.click(Selector(".nav-settings"))
|
||||
.click(Selector("#tab-settings-account"))
|
||||
.typeText(Selector(".input-current-password input"), "wrong", { replace: true })
|
||||
.typeText(Selector(".input-new-password input"), "photoprism", { replace: true })
|
||||
.expect(Selector(".action-confirm").hasAttribute("disabled", "disabled"))
|
||||
.ok()
|
||||
.typeText(Selector(".input-retype-password input"), "photoprism", { replace: true })
|
||||
.expect(Selector(".action-confirm").hasAttribute("disabled", "disabled"))
|
||||
.notOk()
|
||||
.click(".action-confirm")
|
||||
.typeText(Selector(".input-current-password input"), "photoprism", { replace: true })
|
||||
.typeText(Selector(".input-new-password input"), "photoprism123", { replace: true })
|
||||
.expect(Selector(".action-confirm").hasAttribute("disabled", "disabled"))
|
||||
.ok()
|
||||
.typeText(Selector(".input-retype-password input"), "photoprism123", { replace: true })
|
||||
.expect(Selector(".action-confirm").hasAttribute("disabled", "disabled"))
|
||||
.notOk()
|
||||
.click(".action-confirm");
|
||||
await page.openNav();
|
||||
await t.click(Selector('div[title="Logout"]'));
|
||||
.typeText(account.currentPassword, "wrong", { replace: true })
|
||||
.typeText(account.newPassword, "photoprism", { replace: true });
|
||||
|
||||
await t.expect(account.confirm.hasAttribute("disabled", "disabled")).ok();
|
||||
|
||||
await t.typeText(account.retypePassword, "photoprism", { replace: true });
|
||||
|
||||
await t.expect(account.confirm.hasAttribute("disabled", "disabled")).notOk();
|
||||
|
||||
await t
|
||||
.click(account.confirm)
|
||||
.typeText(account.currentPassword, "photoprism", { replace: true })
|
||||
.typeText(account.newPassword, "photoprism123", { replace: true });
|
||||
|
||||
await t.expect(account.confirm.hasAttribute("disabled", "disabled")).ok();
|
||||
|
||||
await t.typeText(account.retypePassword, "photoprism123", { replace: true });
|
||||
|
||||
await t.expect(account.confirm.hasAttribute("disabled", "disabled")).notOk();
|
||||
|
||||
await t.click(account.confirm);
|
||||
await page.logout();
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(7000);
|
||||
}
|
||||
await page.login("admin", "photoprism");
|
||||
await t.navigateTo("/archive");
|
||||
|
||||
await t
|
||||
.navigateTo("/archive")
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.expect(page.nameInput.visible)
|
||||
.ok()
|
||||
.expect(Selector(".input-search input").visible)
|
||||
.notOk();
|
||||
|
||||
await page.login("admin", "photoprism123");
|
||||
await t.expect(Selector(".input-search input").visible).ok();
|
||||
await page.openNav();
|
||||
await menu.openPage("profile");
|
||||
await t
|
||||
.click(Selector(".nav-settings", { timeout: 7000 }))
|
||||
.click(Selector("#tab-settings-account"))
|
||||
.typeText(Selector(".input-current-password input"), "photoprism123", { replace: true })
|
||||
.typeText(Selector(".input-new-password input"), "photoprism", { replace: true })
|
||||
.typeText(Selector(".input-retype-password input"), "photoprism", { replace: true })
|
||||
.click(".action-confirm");
|
||||
await page.openNav();
|
||||
await t.click(Selector('div[title="Logout"]'));
|
||||
.typeText(account.currentPassword, "photoprism123", { replace: true })
|
||||
.typeText(account.newPassword, "photoprism", { replace: true })
|
||||
.typeText(account.retypePassword, "photoprism", { replace: true })
|
||||
.click(account.confirm);
|
||||
await page.logout();
|
||||
await page.login("admin", "photoprism");
|
||||
|
||||
await t.expect(Selector(".input-search input").visible).ok();
|
||||
await page.openNav();
|
||||
await t.click(Selector('div[title="Logout"]'));
|
||||
await page.logout();
|
||||
});
|
||||
|
|
803
frontend/tests/acceptance-private/member-role.js
Normal file
803
frontend/tests/acceptance-private/member-role.js
Normal file
|
@ -0,0 +1,803 @@
|
|||
import { Selector } from "testcafe";
|
||||
import testcafeconfig from "../acceptance/testcafeconfig";
|
||||
import { ClientFunction } from "testcafe";
|
||||
import Menu from "../page-model/menu";
|
||||
import Photo from "../page-model/photo";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Label from "../page-model/label";
|
||||
import Album from "../page-model/album";
|
||||
import Subject from "../page-model/subject";
|
||||
import PhotoViewer from "../page-model/photoviewer";
|
||||
import PhotoEdit from "../page-model/photo-edit";
|
||||
import Page from "../page-model/page";
|
||||
import Settings from "../page-model/settings";
|
||||
import Library from "../page-model/library";
|
||||
import AlbumDialog from "../page-model/dialog-album";
|
||||
|
||||
fixture`Test member role`.page`${testcafeconfig.url}`;
|
||||
|
||||
const menu = new Menu();
|
||||
const photo = new Photo();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const label = new Label();
|
||||
const album = new Album();
|
||||
const subject = new Subject();
|
||||
const photoviewer = new PhotoViewer();
|
||||
const photoedit = new PhotoEdit();
|
||||
const page = new Page();
|
||||
const settings = new Settings();
|
||||
const library = new Library();
|
||||
const albumdialog = new AlbumDialog();
|
||||
|
||||
const getLocation = ClientFunction(() => document.location.href);
|
||||
|
||||
test.meta("testID", "member-role-001").meta({ type: "smoke" })(
|
||||
"No access to settings",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
|
||||
await menu.checkMenuItemAvailability("settings", false);
|
||||
|
||||
await t.navigateTo("/settings");
|
||||
|
||||
await t
|
||||
.expect(settings.languageInput.visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok();
|
||||
|
||||
await t.navigateTo("/settings/library");
|
||||
|
||||
await t
|
||||
.expect(Selector("form.p-form-settings").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok();
|
||||
|
||||
await t.navigateTo("/settings/advanced");
|
||||
|
||||
await t
|
||||
.expect(Selector("label").withText("Read-Only Mode").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok();
|
||||
|
||||
await t.navigateTo("/settings/sync");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-accounts-list").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-002").meta({ type: "smoke" })(
|
||||
"No access to archive",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
|
||||
await menu.checkMenuItemAvailability("archive", false);
|
||||
|
||||
await t.navigateTo("/archive");
|
||||
|
||||
await photo.checkPhotoVisibility("pqnahct2mvee8sr4", false);
|
||||
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
|
||||
const PhotoCountArchive = await photo.getPhotoCount("all");
|
||||
await t.expect(PhotoCountBrowse).eql(PhotoCountArchive);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-003").meta({ type: "smoke" })("No access to review", async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
|
||||
await menu.checkMenuItemAvailability("review", false);
|
||||
|
||||
await t.navigateTo("/review");
|
||||
|
||||
await photo.checkPhotoVisibility("pqzuein2pdcg1kc7", false);
|
||||
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
const PhotoCountReview = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountBrowse).eql(PhotoCountReview);
|
||||
});
|
||||
|
||||
test.meta("testID", "member-role-004").meta({ type: "smoke" })(
|
||||
"No access to private",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
|
||||
await menu.checkMenuItemAvailability("private", false);
|
||||
|
||||
await t.navigateTo("/private");
|
||||
|
||||
await photo.checkPhotoVisibility("pqmxlquf9tbc8mk2", false);
|
||||
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
const PhotoCountPrivate = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountBrowse).eql(PhotoCountPrivate);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-005").meta({ type: "smoke" })(
|
||||
"No access to library",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
|
||||
await menu.checkMenuItemAvailability("library", false);
|
||||
|
||||
await t.navigateTo("/library");
|
||||
|
||||
await t
|
||||
.expect(library.indexFolderSelect.visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok();
|
||||
|
||||
await t.navigateTo("/library/import");
|
||||
|
||||
await t
|
||||
.expect(library.openImportFolderSelect.visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok();
|
||||
|
||||
await t.navigateTo("/library/logs");
|
||||
|
||||
await t
|
||||
.expect(Selector("p.p-log-message").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok();
|
||||
await menu.checkMenuItemAvailability("originals", false);
|
||||
|
||||
await t.navigateTo("/library/files");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-page-files").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok();
|
||||
await menu.checkMenuItemAvailability("hidden", false);
|
||||
|
||||
await t.navigateTo("/library/hidden");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
const PhotoCountHidden = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountBrowse).eql(PhotoCountHidden);
|
||||
await menu.checkMenuItemAvailability("errors", false);
|
||||
|
||||
await t.navigateTo("/library/errors");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-page-errors").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.p-page-photos").visible)
|
||||
.ok();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-006").meta({ type: "smoke" })(
|
||||
"No private/archived photos in search results",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
const PhotoCountBrowse = await photo.getPhotoCount("all");
|
||||
await toolbar.search("private:true");
|
||||
const PhotoCountPrivate = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountPrivate).eql(0);
|
||||
await photo.checkPhotoVisibility("pqmxlquf9tbc8mk2", false);
|
||||
|
||||
await toolbar.search("archived:true");
|
||||
const PhotoCountArchive = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountArchive).eql(0);
|
||||
await photo.checkPhotoVisibility("pqnahct2mvee8sr4", false);
|
||||
|
||||
await toolbar.search("quality:0");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await t.wait(5000);
|
||||
}
|
||||
const PhotoCountReview = await photo.getPhotoCount("all");
|
||||
|
||||
await t.expect(PhotoCountReview).gte(PhotoCountBrowse);
|
||||
await photo.checkPhotoVisibility("pqzuein2pdcg1kc7", true);
|
||||
|
||||
await menu.openPage("places");
|
||||
|
||||
await t
|
||||
.expect(Selector("#map").exists, { timeout: 15000 })
|
||||
.ok()
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok()
|
||||
.wait(5000);
|
||||
|
||||
await t
|
||||
.typeText(Selector('input[aria-label="Search"]'), "oaxaca", { replace: true })
|
||||
.pressKey("enter");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok()
|
||||
.expect(getLocation())
|
||||
.contains("oaxaca")
|
||||
.wait(5000)
|
||||
.expect(Selector('div[title="Viewpoint / Mexico / 2017"]').visible)
|
||||
.notOk()
|
||||
.expect(Selector('div[title="Viewpoint / Mexico / 2018"]').visible)
|
||||
.notOk();
|
||||
|
||||
await t
|
||||
.typeText(Selector('input[aria-label="Search"]'), "canada", { replace: true })
|
||||
.pressKey("enter");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.p-map-control").visible)
|
||||
.ok()
|
||||
.expect(getLocation())
|
||||
.contains("canada")
|
||||
.wait(8000)
|
||||
.expect(Selector('div[title="Cape / Bowen Island / 2019"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('div[title="Truck / Vancouver / 2019"]').visible)
|
||||
.notOk();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-007").meta({ type: "smoke" })(
|
||||
"No upload functionality",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await menu.openPage("albums");
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await menu.openPage("video");
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await menu.openPage("people");
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await menu.openPage("favorites");
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await menu.openPage("moments");
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await menu.openPage("calendar");
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await menu.openPage("states");
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await menu.openPage("folders");
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-008").meta({ type: "smoke" })(
|
||||
"Member cannot like photos",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
const SecondPhotoUid = await photo.getNthPhotoUid("all", 1);
|
||||
await menu.openPage("favorites");
|
||||
const FirstFavoriteUid = await photo.getNthPhotoUid("image", 0);
|
||||
|
||||
await photo.checkHoverActionState("uid", FirstFavoriteUid, "favorite", true);
|
||||
|
||||
await photo.triggerHoverAction("uid", FirstFavoriteUid, "favorite");
|
||||
|
||||
await photo.checkHoverActionState("uid", FirstFavoriteUid, "favorite", true);
|
||||
await photo.checkPhotoVisibility(FirstPhotoUid, false);
|
||||
await photo.checkPhotoVisibility(SecondPhotoUid, false);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await photoviewer.openPhotoViewer("uid", FirstPhotoUid);
|
||||
|
||||
await photoviewer.checkPhotoViewerActionAvailability("like", false);
|
||||
await photoviewer.checkPhotoViewerActionAvailability("close", true);
|
||||
|
||||
await photoviewer.triggerPhotoViewerAction("close");
|
||||
await t.wait(5000).click(Selector(".p-expand-search", { timeout: 10000 }));
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
|
||||
await photo.checkHoverActionState("uid", FirstPhotoUid, "favorite", false);
|
||||
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "favorite");
|
||||
|
||||
await photo.checkHoverActionState("uid", FirstPhotoUid, "favorite", false);
|
||||
|
||||
await photo.selectPhotoFromUID(SecondPhotoUid);
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.infoTab);
|
||||
|
||||
await photoedit.turnSwitchOn("favorite");
|
||||
await t.click(photoedit.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
|
||||
await photo.checkHoverActionState("uid", SecondPhotoUid, "favorite", false);
|
||||
|
||||
await menu.openPage("browse");
|
||||
await toolbar.setFilter("view", "Mosaic");
|
||||
|
||||
await photo.checkHoverActionState("uid", FirstPhotoUid, "favorite", false);
|
||||
|
||||
await photo.triggerHoverAction("uid", FirstPhotoUid, "favorite");
|
||||
|
||||
await photo.checkHoverActionState("uid", FirstPhotoUid, "favorite", false);
|
||||
|
||||
await toolbar.setFilter("view", "List");
|
||||
|
||||
await photo.checkListViewActionAvailability("like", true);
|
||||
|
||||
await photo.triggerListViewActions("nth", 0, "like");
|
||||
await toolbar.setFilter("view", "Cards");
|
||||
|
||||
await photo.checkHoverActionState("uid", FirstPhotoUid, "favorite", false);
|
||||
|
||||
await menu.openPage("albums");
|
||||
await album.openNthAlbum(0);
|
||||
await toolbar.triggerToolbarAction("list-view");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await toolbar.triggerToolbarAction("list-view");
|
||||
}
|
||||
|
||||
await photo.checkListViewActionAvailability("like", true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-009").meta({ type: "smoke" })(
|
||||
"Member cannot private, archive, share, add/remove to album",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
await photo.selectPhotoFromUID(FirstPhotoUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("private", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("archive", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", true);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await toolbar.setFilter("view", "List");
|
||||
|
||||
await photo.checkListViewActionAvailability("private", true);
|
||||
|
||||
await menu.openPage("albums");
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("private", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await toolbar.triggerToolbarAction("list-view");
|
||||
if (t.browser.platform === "mobile") {
|
||||
await toolbar.triggerToolbarAction("list-view");
|
||||
}
|
||||
|
||||
await photo.checkListViewActionAvailability("private", true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-010").meta({ type: "smoke" })(
|
||||
"Member cannot approve low quality photos",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await toolbar.search('quality:0 name:"photos-013_1"');
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
|
||||
await t.expect(photoedit.detailsApprove.visible).notOk();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-011").meta({ type: "smoke" })(
|
||||
"Edit dialog is read only for member",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await toolbar.search("faces:new");
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
await photo.selectPhotoFromUID(FirstPhotoUid);
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
|
||||
await photoedit.checkAllDetailsFieldsDisabled(true);
|
||||
|
||||
await t.expect(photoedit.detailsApply.visible).notOk();
|
||||
if (t.browser.platform !== "mobile") {
|
||||
await t.expect(photoedit.detailsDone.visible).notOk();
|
||||
}
|
||||
|
||||
await t.click(photoedit.labelsTab);
|
||||
|
||||
await photoedit.checkFieldDisabled(photoedit.removeLabel, true);
|
||||
await t
|
||||
.expect(photoedit.inputLabelName.exists)
|
||||
.notOk()
|
||||
.expect(photoedit.addLabel.exists)
|
||||
.notOk();
|
||||
|
||||
await t.click(photoedit.openInlineEdit);
|
||||
|
||||
await t.expect(photoedit.inputLabelRename.exists).notOk();
|
||||
|
||||
await t.click(photoedit.peopleTab);
|
||||
|
||||
await photoedit.checkFieldDisabled(photoedit.inputName, true);
|
||||
await t.expect(photoedit.removeMarker.exists).notOk();
|
||||
|
||||
await t.click(photoedit.infoTab);
|
||||
|
||||
await photoedit.checkAllInfoFieldsDisabled(true);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-012").meta({ type: "smoke" })(
|
||||
"No edit album functionality",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await menu.openPage("albums");
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("add", false);
|
||||
await album.checkHoverActionAvailability("nth", 1, "share", false);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).notOk();
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
await toolbar.checkToolbarActionAvailability("edit", false);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", false);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-013")("No edit moment functionality", async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await menu.openPage("moments");
|
||||
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", false);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).notOk();
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
await toolbar.checkToolbarActionAvailability("edit", false);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", false);
|
||||
});
|
||||
|
||||
test.meta("testID", "member-role-014")("No edit state functionality", async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await menu.openPage("states");
|
||||
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", false);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).notOk();
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
await toolbar.checkToolbarActionAvailability("edit", false);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", false);
|
||||
});
|
||||
|
||||
test.meta("testID", "member-role-015")("No edit calendar functionality", async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await menu.openPage("calendar");
|
||||
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", false);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).notOk();
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
await toolbar.checkToolbarActionAvailability("edit", false);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", false);
|
||||
});
|
||||
|
||||
test.meta("testID", "member-role-016").meta({ type: "smoke" })(
|
||||
"No edit folder functionality",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await menu.openPage("folders");
|
||||
|
||||
await album.checkHoverActionAvailability("nth", 0, "share", false);
|
||||
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.selectAlbumFromUID(FirstAlbumUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("clone", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
await t.click(page.cardTitle);
|
||||
|
||||
await t.expect(albumdialog.description.visible).notOk();
|
||||
|
||||
if (await Selector(`a.uid-${FirstAlbumUid}`).hasClass("is-favorite")) {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", true);
|
||||
} else {
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "favorite");
|
||||
await album.checkHoverActionState("uid", FirstAlbumUid, "favorite", false);
|
||||
}
|
||||
|
||||
await album.openNthAlbum(0);
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
await toolbar.checkToolbarActionAvailability("edit", false);
|
||||
|
||||
await photo.toggleSelectNthPhoto(0, "all");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("remove", false);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-017").meta({ type: "smoke" })(
|
||||
"No edit labels functionality",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await menu.openPage("labels");
|
||||
const FirstLabelUid = await label.getNthLabeltUid(0);
|
||||
|
||||
await label.checkHoverActionState("uid", FirstLabelUid, "favorite", false);
|
||||
|
||||
await label.triggerHoverAction("uid", FirstLabelUid, "favorite");
|
||||
|
||||
await label.checkHoverActionState("uid", FirstLabelUid, "favorite", false);
|
||||
|
||||
await t.click(Selector(`a.uid-${FirstLabelUid} div.inline-edit`));
|
||||
|
||||
await t.expect(photoedit.inputLabelRename.visible).notOk();
|
||||
|
||||
await label.selectLabelFromUID(FirstLabelUid);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-018").meta({ type: "smoke" })(
|
||||
"No unstack, change primary actions",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await toolbar.search("stack:true");
|
||||
|
||||
const FirstPhotoUid = await photo.getNthPhotoUid("image", 0);
|
||||
await photo.selectPhotoFromUID(FirstPhotoUid);
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.filesTab);
|
||||
|
||||
await t
|
||||
.expect(photoedit.downloadFile.visible)
|
||||
.ok()
|
||||
.expect(photoedit.downloadFile.hasAttribute("disabled"))
|
||||
.notOk();
|
||||
|
||||
await t.click(photoedit.toggleExpandFile.nth(1));
|
||||
|
||||
await photoedit.checkFieldDisabled(photoedit.downloadFile, false);
|
||||
await t
|
||||
.expect(photoedit.downloadFile.visible)
|
||||
.ok()
|
||||
.expect(photoedit.unstackFile.visible)
|
||||
.notOk()
|
||||
.expect(photoedit.makeFilePrimary.visible)
|
||||
.notOk()
|
||||
.expect(photoedit.deleteFile.visible)
|
||||
.notOk();
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "member-role-019").meta({ type: "smoke" })(
|
||||
"No edit people functionality",
|
||||
async (t) => {
|
||||
await page.login("member", "passwdmember");
|
||||
await menu.openPage("people");
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("show-hidden", false);
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await subject.checkSubjectVisibility("name", "Otto Visible", true);
|
||||
await subject.checkSubjectVisibility("name", "Monika Hide", false);
|
||||
await t.expect(subject.newTab.exists).notOk();
|
||||
|
||||
await t.click(Selector("a div.v-card__title").nth(0));
|
||||
|
||||
await t.expect(Selector("div.input-rename input").visible).notOk();
|
||||
await subject.checkHoverActionAvailability("nth", 0, "hidden", false);
|
||||
|
||||
await subject.toggleSelectNthSubject(0);
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("album", "false");
|
||||
await contextmenu.checkContextMenuActionAvailability("download", "true");
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
const FirstSubjectUid = subject.getNthSubjectUid(0);
|
||||
|
||||
if (await Selector(`a.uid-${FirstSubjectUid}`).hasClass("is-favorite")) {
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", true);
|
||||
await subject.triggerHoverAction("uid", FirstSubjectUid, "favorite");
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", true);
|
||||
} else {
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", false);
|
||||
await subject.triggerHoverAction("uid", FirstSubjectUid, "favorite");
|
||||
await subject.checkHoverActionState("uid", FirstSubjectUid, "favorite", false);
|
||||
}
|
||||
|
||||
await subject.openNthSubject(0);
|
||||
await photo.toggleSelectNthPhoto(0);
|
||||
await contextmenu.triggerContextMenuAction("edit", "");
|
||||
await t.click(photoedit.peopleTab);
|
||||
|
||||
await photoedit.checkFieldDisabled(photoedit.inputName);
|
||||
await t.expect(photoedit.rejectName.hasClass("v-icon--disabled")).ok();
|
||||
|
||||
await t.navigateTo("/people/new");
|
||||
|
||||
await t.expect(Selector("div.is-face").visible).notOk().expect(subject.newTab.exists).notOk();
|
||||
|
||||
await t.navigateTo("/people?hidden=yes&order=relevance");
|
||||
await t.expect(Selector("a.is-subject").visible).notOk();
|
||||
}
|
||||
);
|
|
@ -1,194 +1,192 @@
|
|||
import { Selector } from "testcafe";
|
||||
import { Role } from "testcafe";
|
||||
import testcafeconfig from "../acceptance/testcafeconfig";
|
||||
import Page from "../acceptance/page-model";
|
||||
import Page from "../page-model/page";
|
||||
import Menu from "../page-model/menu";
|
||||
import Toolbar from "../page-model/toolbar";
|
||||
import ContextMenu from "../page-model/context-menu";
|
||||
import Album from "../page-model/album";
|
||||
import PhotoViewer from "../page-model/photoviewer";
|
||||
import ShareDialog from "../page-model/dialog-share";
|
||||
import Photo from "../page-model/photo";
|
||||
|
||||
fixture`Test link sharing`.page`${testcafeconfig.url}`;
|
||||
fixture`Test link sharing`.page`${testcafeconfig.url}`.skip("Urls are not working anymore");
|
||||
|
||||
const page = new Page();
|
||||
const menu = new Menu();
|
||||
const toolbar = new Toolbar();
|
||||
const contextmenu = new ContextMenu();
|
||||
const album = new Album();
|
||||
const photoviewer = new PhotoViewer();
|
||||
const sharedialog = new ShareDialog();
|
||||
const photo = new Photo();
|
||||
|
||||
test.meta("testID", "authentication-000")(
|
||||
//TODO merge with other sharing test
|
||||
test.skip.meta("testID", "authentication-000")(
|
||||
"Time to start instance (will be marked as unstable)",
|
||||
async (t) => {
|
||||
await t.wait(5000);
|
||||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "sharing-001")("View shared albums", async (t) => {
|
||||
test.skip.meta("testID", "sharing-001")("View shared albums", async (t) => {
|
||||
await page.login("admin", "photoprism");
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-albums"));
|
||||
const FirstAlbum = await Selector("a.is-album").nth(0).getAttribute("data-uid");
|
||||
await page.selectFromUID(FirstAlbum);
|
||||
const clipboardCount = await Selector("span.count-clipboard");
|
||||
await menu.openPage("albums");
|
||||
const FirstAlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.triggerHoverAction("uid", FirstAlbumUid, "select");
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.triggerContextMenuAction("share", "");
|
||||
await t.click(sharedialog.expandLink.nth(0));
|
||||
await t
|
||||
.expect(clipboardCount.textContent)
|
||||
.eql("1")
|
||||
.click(Selector("button.action-menu"))
|
||||
.click(Selector("button.action-share"))
|
||||
.click(Selector("div.v-expansion-panel__header__icon").nth(0));
|
||||
await t
|
||||
.typeText(Selector(".input-secret input"), "secretForTesting", { replace: true })
|
||||
.click(Selector(".input-expires input"))
|
||||
.typeText(sharedialog.linkSecretInput, "secretForTesting", { replace: true })
|
||||
.click(sharedialog.linkExpireInput)
|
||||
.click(Selector("div").withText("After 1 day").parent('div[role="listitem"]'))
|
||||
.click(Selector("button.action-save"));
|
||||
const Url = await Selector("div.input-url input").value;
|
||||
.click(sharedialog.dialogSave);
|
||||
const Url = await sharedialog.linkUrl.value;
|
||||
const Expire = await Selector("div.v-select__selections").innerText;
|
||||
|
||||
await t.expect(Url).contains("secretfortesting").expect(Expire).contains("After 1 day");
|
||||
|
||||
let url = Url.replace("2342", "2343");
|
||||
await t.click(Selector("button.action-close"));
|
||||
await page.clearSelection();
|
||||
await t.click(Selector(".nav-folders"));
|
||||
const FirstFolder = await Selector("a.is-album").nth(0).getAttribute("data-uid");
|
||||
await page.selectFromUID(FirstFolder);
|
||||
await t.click(sharedialog.dialogClose);
|
||||
await contextmenu.clearSelection();
|
||||
await menu.openPage("folders");
|
||||
const FirstFolderUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.triggerHoverAction("uid", FirstFolderUid, "select");
|
||||
await contextmenu.checkContextMenuCount("1");
|
||||
await contextmenu.triggerContextMenuAction("share", "");
|
||||
await t.click(sharedialog.expandLink.nth(0));
|
||||
await t
|
||||
.click(Selector("button.action-menu"))
|
||||
.click(Selector("button.action-share"))
|
||||
.click(Selector("div.v-expansion-panel__header__icon").nth(0));
|
||||
await t
|
||||
.typeText(Selector(".input-secret input"), "secretForTesting", { replace: true })
|
||||
.click(Selector(".input-expires input"))
|
||||
.typeText(sharedialog.linkSecretInput, "secretForTesting", { replace: true })
|
||||
.click(sharedialog.linkExpireInput)
|
||||
.click(Selector("div").withText("After 1 day").parent('div[role="listitem"]'))
|
||||
.click(Selector("button.action-save"))
|
||||
.click(Selector("button.action-close"));
|
||||
await page.clearSelection();
|
||||
.click(sharedialog.dialogSave)
|
||||
.click(sharedialog.dialogSave);
|
||||
await contextmenu.clearSelection();
|
||||
await t.navigateTo(url);
|
||||
await t
|
||||
.expect(Selector("div.v-toolbar__title").withText("Christmas").visible)
|
||||
.ok()
|
||||
.click(Selector("button").withText("@photoprism_app"))
|
||||
.expect(Selector("div.v-toolbar__title").withText("Albums").visible)
|
||||
.ok();
|
||||
const countAlbums = await Selector("a.is-album").count;
|
||||
await t.expect(countAlbums).gte(40).useRole(Role.anonymous());
|
||||
|
||||
await t.expect(toolbar.toolbarTitle.withText("Christmas").visible).ok();
|
||||
|
||||
await t.click(Selector("button").withText("@photoprism_app"));
|
||||
|
||||
await t.expect(toolbar.toolbarTitle.withText("Albums").visible).ok();
|
||||
|
||||
const AlbumCount = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCount).gte(40);
|
||||
|
||||
await t.useRole(Role.anonymous());
|
||||
await t.navigateTo(url);
|
||||
await t
|
||||
.expect(Selector("div.v-toolbar__title").withText("Christmas").visible)
|
||||
.ok()
|
||||
.click(Selector("button").withText("@photoprism_app"))
|
||||
.expect(Selector("div.v-toolbar__title").withText("Albums").visible)
|
||||
.ok();
|
||||
const countAlbumsAnonymous = await Selector("a.is-album").count;
|
||||
await t.expect(countAlbumsAnonymous).eql(2);
|
||||
|
||||
await t.expect(toolbar.toolbarTitle.withText("Christmas").visible).ok();
|
||||
|
||||
await t.click(Selector("button").withText("@photoprism_app"));
|
||||
|
||||
await t.expect(toolbar.toolbarTitle.withText("Albums").visible).ok();
|
||||
|
||||
const AlbumCountAnonymous = await Selector("a.is-album").count;
|
||||
|
||||
await t.expect(AlbumCountAnonymous).eql(2);
|
||||
|
||||
await t.navigateTo("http://localhost:2343/browse");
|
||||
await page.login("admin", "photoprism");
|
||||
await page.openNav();
|
||||
await menu.openPage("albums");
|
||||
await album.openAlbumWithUid(FirstAlbumUid);
|
||||
await toolbar.triggerToolbarAction("share");
|
||||
await t
|
||||
.click(Selector(".nav-albums"))
|
||||
.click(Selector("a.is-album").withAttribute("data-uid", FirstAlbum))
|
||||
.click(Selector("button.action-share"))
|
||||
.click(Selector("div.v-expansion-panel__header__icon").nth(0))
|
||||
.click(Selector(".action-delete"))
|
||||
.useRole(Role.anonymous())
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok();
|
||||
.click(sharedialog.expandLink.nth(0))
|
||||
.click(sharedialog.deleteLink)
|
||||
.useRole(Role.anonymous());
|
||||
|
||||
await t.expect(Selector(".input-name input").visible).ok();
|
||||
|
||||
await t.navigateTo("http://localhost:2343/s/secretfortesting");
|
||||
await t.expect(Selector("div.v-toolbar__title").withText("Albums").visible).ok();
|
||||
const countAlbumsAnonymousAfterDelete = await Selector("a.is-album").count;
|
||||
await t.expect(countAlbumsAnonymousAfterDelete).eql(1);
|
||||
|
||||
await t.expect(toolbar.toolbarTitle.withText("Albums").visible).ok();
|
||||
|
||||
const AlbumCountAnonymousAfterDelete = await album.getAlbumCount("all");
|
||||
|
||||
await t.expect(AlbumCountAnonymousAfterDelete).eql(1);
|
||||
|
||||
await t.navigateTo("http://localhost:2343/browse");
|
||||
await page.login("admin", "photoprism");
|
||||
await page.openNav();
|
||||
await menu.openPage("folders");
|
||||
await album.openAlbumWithUid(FirstFolderUid);
|
||||
await toolbar.triggerToolbarAction("share");
|
||||
await t
|
||||
.click(Selector(".nav-folders"))
|
||||
.click(Selector("a.is-album").withAttribute("data-uid", FirstFolder))
|
||||
.click(Selector("button.action-share"))
|
||||
.click(Selector("div.v-expansion-panel__header__icon").nth(0))
|
||||
.click(Selector(".action-delete"))
|
||||
.useRole(Role.anonymous())
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok();
|
||||
.click(sharedialog.expandLink.nth(0))
|
||||
.click(sharedialog.deleteLink)
|
||||
.useRole(Role.anonymous());
|
||||
|
||||
await t.expect(Selector(".input-name input").visible).ok();
|
||||
|
||||
await t.navigateTo("http://localhost:2343/s/secretfortesting");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.v-toolbar__title").withText("Christmas").visible)
|
||||
.expect(toolbar.toolbarTitle.withText("Christmas").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.v-toolbar__title").withText("Albums").visible)
|
||||
.expect(toolbar.toolbarTitle.withText("Albums").visible)
|
||||
.notOk()
|
||||
.expect(Selector(".input-name input").visible)
|
||||
.ok();
|
||||
});
|
||||
|
||||
test.meta("testID", "sharing-002")("Verify anonymous user has limited options", async (t) => {
|
||||
test.skip.meta("testID", "sharing-002")("Verify anonymous user has limited options", async (t) => {
|
||||
await t.navigateTo("http://localhost:2343/s/jxoux5ub1e/british-columbia-canada");
|
||||
// check album toolbar
|
||||
|
||||
await t.expect(toolbar.toolbarTitle.withText("British Columbia").visible).ok();
|
||||
|
||||
await toolbar.checkToolbarActionAvailability("edit", false);
|
||||
await toolbar.checkToolbarActionAvailability("share", false);
|
||||
await toolbar.checkToolbarActionAvailability("upload", false);
|
||||
await toolbar.checkToolbarActionAvailability("reload", true);
|
||||
await toolbar.checkToolbarActionAvailability("download", true);
|
||||
|
||||
await photo.triggerHoverAction("nth", 0, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("archive", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("private", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
|
||||
await contextmenu.clearSelection();
|
||||
|
||||
await t.expect(page.cardTitle.visible).notOk();
|
||||
|
||||
await photoviewer.openPhotoViewer("nth", 0);
|
||||
|
||||
await photoviewer.checkPhotoViewerActionAvailability("download", true);
|
||||
await photoviewer.checkPhotoViewerActionAvailability("select", true);
|
||||
await photoviewer.checkPhotoViewerActionAvailability("fullscreen", true);
|
||||
await photoviewer.checkPhotoViewerActionAvailability("slideshow", true);
|
||||
await photoviewer.checkPhotoViewerActionAvailability("like", false);
|
||||
await photoviewer.checkPhotoViewerActionAvailability("edit", false);
|
||||
|
||||
await photoviewer.triggerPhotoViewerAction("close");
|
||||
|
||||
await photo.checkHoverActionAvailability("nth", 0, "favorite", false);
|
||||
await photo.checkHoverActionAvailability("nth", 0, "select", true);
|
||||
|
||||
await toolbar.triggerToolbarAction("view-list");
|
||||
|
||||
await t
|
||||
.expect(Selector("div.v-toolbar__title").withText("British Columbia").visible)
|
||||
.ok()
|
||||
.expect(Selector("button.action-edit").visible)
|
||||
.expect(Selector(`td button.input-private`).visible)
|
||||
.notOk()
|
||||
.expect(Selector("button.action-share").visible)
|
||||
.expect(Selector(`td button.input-favorite`).visible)
|
||||
.notOk()
|
||||
.expect(Selector("button.action-upload").visible)
|
||||
.notOk()
|
||||
.expect(Selector("button.action-reload").visible)
|
||||
.ok()
|
||||
.expect(Selector("button.action-download").visible)
|
||||
.ok();
|
||||
//check photo context menu
|
||||
await page.toggleSelectNthPhoto(0);
|
||||
await t
|
||||
.click("button.action-menu")
|
||||
.expect(Selector("div.v-speed-dial__list button.action-download").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.v-speed-dial__list button.action-archive").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.v-speed-dial__list button.action-album").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.v-speed-dial__list button.action-private").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.v-speed-dial__list button.action-edit").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.v-speed-dial__list button.action-share").visible)
|
||||
.notOk();
|
||||
await page.clearSelection();
|
||||
await t.expect(Selector("button.action-title-edit").visible).notOk();
|
||||
//check fullscreen actions
|
||||
await t
|
||||
.click(Selector('h3[title="Cape / Bowen Island / 2019"]'))
|
||||
.expect(Selector("#photo-viewer").visible)
|
||||
.ok()
|
||||
.expect(Selector("img.pswp__img").visible)
|
||||
.ok()
|
||||
.expect(Selector("button.action-select").visible)
|
||||
.ok()
|
||||
.expect(Selector('button[title="Start/Stop Slideshow"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('button[title="Fullscreen"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('button[title="Start/Stop Slideshow"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('button[title="Download"]').visible)
|
||||
.ok()
|
||||
.expect(Selector('button[title="Like"]').visible)
|
||||
.notOk()
|
||||
.expect(Selector('button[title="Edit"]').visible)
|
||||
.notOk()
|
||||
.click(Selector('button[title="Close"]'))
|
||||
//check hover like actions card and mosaic
|
||||
.expect(Selector("button.input-favorite").visible)
|
||||
.notOk()
|
||||
//check list view actions
|
||||
//hover on mosaic
|
||||
//action-menu albums
|
||||
.click(Selector("button").withText("@photoprism_app"))
|
||||
.expect(Selector("div.v-toolbar__title").withText("Albums").visible)
|
||||
.expect(toolbar.toolbarTitle.withText("Albums").visible)
|
||||
.ok();
|
||||
//album edit dialog
|
||||
const AlbumUid = await Selector("a.is-album", { timeout: 55000 }).nth(0).getAttribute("data-uid");
|
||||
await page.selectFromUID(AlbumUid);
|
||||
await t
|
||||
.click(Selector("button.action-menu"))
|
||||
.expect(Selector("div.v-speed-dial__list button.action-download").visible)
|
||||
.ok()
|
||||
.expect(Selector("div.v-speed-dial__list button.action-delete").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.v-speed-dial__list button.action-album").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.v-speed-dial__list button.action-edit").visible)
|
||||
.notOk()
|
||||
.expect(Selector("div.v-speed-dial__list button.action-share").visible)
|
||||
.notOk();
|
||||
await page.clearSelection();
|
||||
await t.expect(Selector("button.action-title-edit").visible).notOk();
|
||||
//TODO control + page model
|
||||
|
||||
const AlbumUid = await album.getNthAlbumUid("all", 0);
|
||||
await album.triggerHoverAction("uid", AlbumUid, "select");
|
||||
|
||||
await contextmenu.checkContextMenuActionAvailability("download", true);
|
||||
await contextmenu.checkContextMenuActionAvailability("delete", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("album", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("edit", false);
|
||||
await contextmenu.checkContextMenuActionAvailability("share", false);
|
||||
await contextmenu.clearSelection();
|
||||
});
|
||||
|
|
|
@ -170,8 +170,6 @@ test.meta("testID", "albums-007")("Create/delete album during add to album", asy
|
|||
});
|
||||
|
||||
test.meta("testID", "albums-008")("Test album autocomplete", async (t) => {
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-browse"));
|
||||
await page.search("photo:true");
|
||||
const FirstPhotoUid = await Selector("div.is-photo.type-image").nth(0).getAttribute("data-uid");
|
||||
await page.selectPhotoFromUID(FirstPhotoUid);
|
||||
|
|
|
@ -660,4 +660,129 @@ export default class Page {
|
|||
await t.click(Selector("button.action-done", { timeout: 5000 }));
|
||||
}
|
||||
}
|
||||
|
||||
async checkMemberAlbumRights(type) {
|
||||
await t.expect(Selector("a.is-album button.action-share").visible).notOk();
|
||||
const FirstAlbum = await Selector("a.is-album").nth(0).getAttribute("data-uid");
|
||||
await this.selectFromUID(FirstAlbum);
|
||||
await t
|
||||
.click(Selector("button.action-menu"))
|
||||
.expect(Selector("button.action-edit").visible)
|
||||
.notOk()
|
||||
.expect(Selector("button.action-share").visible)
|
||||
.notOk()
|
||||
.expect(Selector("button.action-clone").visible)
|
||||
.notOk()
|
||||
.expect(Selector("button.action-download").visible)
|
||||
.ok();
|
||||
if (type == "album" || type == "moment" || type == "state") {
|
||||
await t.expect(Selector("button.action-delete").visible).notOk();
|
||||
}
|
||||
await this.clearSelection();
|
||||
await t
|
||||
.click(Selector("button.action-title-edit"))
|
||||
.expect(Selector(".input-description textarea").visible)
|
||||
.notOk();
|
||||
if (await Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite")) {
|
||||
await t
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.ok()
|
||||
.click(Selector(`.uid-${FirstAlbum} .input-favorite`))
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.ok();
|
||||
} else {
|
||||
await t
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.notOk()
|
||||
.click(Selector(`.uid-${FirstAlbum} .input-favorite`))
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.notOk();
|
||||
}
|
||||
await t
|
||||
.click(Selector("a.is-album").nth(0))
|
||||
.expect(Selector("button.action-share").visible)
|
||||
.notOk()
|
||||
.expect(Selector("button.action-edit").visible)
|
||||
.notOk();
|
||||
await this.toggleSelectNthPhoto(0);
|
||||
await t.click(Selector("button.action-menu"));
|
||||
if (type == "album") {
|
||||
await t.expect(Selector("button.action-remove").visible).notOk();
|
||||
} else {
|
||||
await t.expect(Selector("button.action-archive").visible).notOk();
|
||||
}
|
||||
await t
|
||||
.expect(Selector("button.action-album").visible)
|
||||
.notOk()
|
||||
.expect(Selector("button.action-private").visible)
|
||||
.notOk()
|
||||
.expect(Selector("button.action-share").visible)
|
||||
.notOk();
|
||||
}
|
||||
|
||||
async checkAdminAlbumRights(type) {
|
||||
await t.expect(Selector("a.is-album button.action-share").visible).ok();
|
||||
const FirstAlbum = await Selector("a.is-album").nth(0).getAttribute("data-uid");
|
||||
await this.selectFromUID(FirstAlbum);
|
||||
await t
|
||||
.click(Selector("button.action-menu"))
|
||||
.expect(Selector("button.action-edit").visible)
|
||||
.ok()
|
||||
.expect(Selector("button.action-share").visible)
|
||||
.ok()
|
||||
.expect(Selector("button.action-clone").visible)
|
||||
.ok()
|
||||
.expect(Selector("button.action-download").visible)
|
||||
.ok();
|
||||
if (type == "album" || type == "moment" || type == "state") {
|
||||
await t.expect(Selector("button.action-delete").visible).ok();
|
||||
}
|
||||
await this.clearSelection();
|
||||
await t
|
||||
.click(Selector("button.action-title-edit"))
|
||||
.expect(Selector(".input-description textarea").visible)
|
||||
.ok()
|
||||
.click(Selector("button.action-cancel"));
|
||||
if (await Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite")) {
|
||||
await t
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.ok()
|
||||
.click(Selector(`.uid-${FirstAlbum} .input-favorite`))
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.notOk()
|
||||
.click(Selector(`.uid-${FirstAlbum} .input-favorite`))
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.ok();
|
||||
} else {
|
||||
await t
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.notOk()
|
||||
.click(Selector(`.uid-${FirstAlbum} .input-favorite`))
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.ok()
|
||||
.click(Selector(`.uid-${FirstAlbum} .input-favorite`))
|
||||
.expect(Selector(`a.uid-${FirstAlbum}`).hasClass("is-favorite"))
|
||||
.notOk();
|
||||
}
|
||||
await t
|
||||
.click(Selector("a.is-album").nth(0))
|
||||
.expect(Selector("button.action-share").visible)
|
||||
.ok()
|
||||
.expect(Selector("button.action-edit").visible)
|
||||
.ok();
|
||||
await this.toggleSelectNthPhoto(0);
|
||||
await t.click(Selector("button.action-menu"));
|
||||
if (type == "album") {
|
||||
await t.expect(Selector("button.action-remove").visible).ok();
|
||||
} else {
|
||||
await t.expect(Selector("button.action-archive").visible).ok();
|
||||
}
|
||||
await t
|
||||
.expect(Selector("button.action-album").visible)
|
||||
.ok()
|
||||
.expect(Selector("button.action-private").visible)
|
||||
.ok()
|
||||
.expect(Selector("button.action-share").visible)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ const page = new Page();
|
|||
test.meta("testID", "photos-005")(
|
||||
"Private/unprivate photo/video using clipboard and list",
|
||||
async (t) => {
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-browse"));
|
||||
await page.search("photo:true");
|
||||
await page.setFilter("view", "Mosaic");
|
||||
const FirstPhoto = await Selector("div.is-photo").nth(0).getAttribute("data-uid");
|
||||
|
@ -168,8 +166,6 @@ test.meta("testID", "photos-005")(
|
|||
test.meta("testID", "photos-006")(
|
||||
"Archive/restore video, photos, private photos and review photos using clipboard",
|
||||
async (t) => {
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-browse"));
|
||||
await page.search("photo:true");
|
||||
await page.setFilter("view", "Mosaic");
|
||||
const FirstPhoto = await Selector("div.is-photo").nth(0).getAttribute("data-uid");
|
||||
|
|
|
@ -41,25 +41,20 @@ test.meta("testID", "photos-download-001")(
|
|||
}
|
||||
);
|
||||
|
||||
test.meta("testID", "photos-download-002")(
|
||||
"Test download video from context menu",
|
||||
async (t) => {
|
||||
await page.openNav();
|
||||
await t.click(Selector("div.nav-browse"));
|
||||
await page.search("name:Mohn.mp4");
|
||||
const Photo = await Selector("div.is-photo").nth(0).getAttribute("data-uid");
|
||||
await page.selectPhotoFromUID(Photo);
|
||||
await t.click(Selector("button.action-menu"));
|
||||
await logger.clear();
|
||||
await t.click(Selector("button.action-download"));
|
||||
const requestInfo = await logger.requests[1].response;
|
||||
const requestInfo2 = await logger.requests[2].response;
|
||||
await page.validateDownloadRequest(requestInfo, "Mohn", ".mp4.jpg");
|
||||
await page.validateDownloadRequest(requestInfo2, "Mohn", ".mp4");
|
||||
await logger.clear();
|
||||
await page.clearSelection();
|
||||
}
|
||||
);
|
||||
test.meta("testID", "photos-download-002")("Test download video from context menu", async (t) => {
|
||||
await page.search("name:Mohn.mp4");
|
||||
const Photo = await Selector("div.is-photo").nth(0).getAttribute("data-uid");
|
||||
await page.selectPhotoFromUID(Photo);
|
||||
await t.click(Selector("button.action-menu"));
|
||||
await logger.clear();
|
||||
await t.click(Selector("button.action-download"));
|
||||
const requestInfo = await logger.requests[1].response;
|
||||
const requestInfo2 = await logger.requests[2].response;
|
||||
await page.validateDownloadRequest(requestInfo, "Mohn", ".mp4.jpg");
|
||||
await page.validateDownloadRequest(requestInfo2, "Mohn", ".mp4");
|
||||
await logger.clear();
|
||||
await page.clearSelection();
|
||||
});
|
||||
|
||||
test.meta("testID", "photos-download-003")(
|
||||
"Test download multiple jpg files from context menu",
|
||||
|
@ -85,8 +80,6 @@ test.meta("testID", "photos-download-003")(
|
|||
test.meta("testID", "photos-download-004")(
|
||||
"Test raw file from context menu and fullscreen mode",
|
||||
async (t) => {
|
||||
await page.openNav();
|
||||
await t.click(Selector("div.nav-browse"));
|
||||
await page.search("name:elephantRAW");
|
||||
const Photo = await Selector("div.is-photo").nth(0).getAttribute("data-uid");
|
||||
await page.selectPhotoFromUID(Photo);
|
||||
|
|
|
@ -10,8 +10,6 @@ const page = new Page();
|
|||
|
||||
test.meta("testID", "photos-upload-delete-001")("Upload + Delete jpg/json", async (t) => {
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/10")).notOk();
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-browse"));
|
||||
await page.search("digikam");
|
||||
const PhotoCount = await Selector("div.is-photo").count;
|
||||
await t
|
||||
|
@ -42,9 +40,11 @@ test.meta("testID", "photos-upload-delete-001")("Upload + Delete jpg/json", asyn
|
|||
.ok()
|
||||
.click(Selector(".action-close"));
|
||||
await page.clearSelection();
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/10")).ok();
|
||||
const originalsLength = fs.readdirSync("../storage/acceptance/originals/2020/10").length;
|
||||
await t.expect(originalsLength).eql(2);
|
||||
if (t.browser.platform !== "mobile") {
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/10")).ok();
|
||||
const originalsLength = fs.readdirSync("../storage/acceptance/originals/2020/10").length;
|
||||
await t.expect(originalsLength).eql(2);
|
||||
}
|
||||
await page.deletePhotoFromUID(UploadedPhoto);
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-browse"));
|
||||
|
@ -55,24 +55,23 @@ test.meta("testID", "photos-upload-delete-001")("Upload + Delete jpg/json", asyn
|
|||
.navigateTo("/library/files/2020/10");
|
||||
const FileCountAfterDelete = await Selector("div.is-file").count;
|
||||
await t.expect(FileCountAfterDelete).eql(0);
|
||||
const originalsLengthAfterDelete = fs.readdirSync("../storage/acceptance/originals/2020/10")
|
||||
.length;
|
||||
await t.expect(originalsLengthAfterDelete).eql(0);
|
||||
if (t.browser.platform !== "mobile") {
|
||||
const originalsLengthAfterDelete = fs.readdirSync(
|
||||
"../storage/acceptance/originals/2020/10"
|
||||
).length;
|
||||
await t.expect(originalsLengthAfterDelete).eql(0);
|
||||
}
|
||||
});
|
||||
|
||||
test.meta("testID", "photos-upload-delete-002")("Upload + Delete video", async (t) => {
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/06")).notOk();
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-browse"));
|
||||
await page.search("korn");
|
||||
const PhotoCount = await Selector("div.is-photo").count;
|
||||
await t
|
||||
.expect(PhotoCount)
|
||||
.eql(0)
|
||||
.click(Selector(".action-upload"))
|
||||
.setFilesToUpload(Selector(".input-upload"), [
|
||||
"./upload-files/korn.mp4",
|
||||
])
|
||||
.setFilesToUpload(Selector(".input-upload"), ["./upload-files/korn.mp4"])
|
||||
.wait(15000);
|
||||
const PhotoCountAfterUpload = await Selector("div.is-photo").count;
|
||||
await t.expect(PhotoCountAfterUpload).eql(1);
|
||||
|
@ -93,11 +92,13 @@ test.meta("testID", "photos-upload-delete-002")("Upload + Delete video", async (
|
|||
.ok()
|
||||
.click(Selector(".action-close"));
|
||||
await page.clearSelection();
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/06")).ok();
|
||||
const originalsLength = fs.readdirSync("../storage/acceptance/originals/2020/06").length;
|
||||
await t.expect(originalsLength).eql(1);
|
||||
const sidecarLength = fs.readdirSync("../storage/acceptance/originals/2020/06").length;
|
||||
await t.expect(sidecarLength).eql(1);
|
||||
if (t.browser.platform !== "mobile") {
|
||||
await t.expect(fs.existsSync("../storage/acceptance/originals/2020/06")).ok();
|
||||
const originalsLength = fs.readdirSync("../storage/acceptance/originals/2020/06").length;
|
||||
await t.expect(originalsLength).eql(1);
|
||||
const sidecarLength = fs.readdirSync("../storage/acceptance/originals/2020/06").length;
|
||||
await t.expect(sidecarLength).eql(1);
|
||||
}
|
||||
await page.deletePhotoFromUID(UploadedPhoto);
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-browse"));
|
||||
|
@ -108,11 +109,16 @@ test.meta("testID", "photos-upload-delete-002")("Upload + Delete video", async (
|
|||
.navigateTo("/library/files/2020/06");
|
||||
const FileCountAfterDelete = await Selector("div.is-file").count;
|
||||
await t.expect(FileCountAfterDelete).eql(0);
|
||||
const originalsLengthAfterDelete = fs.readdirSync("../storage/acceptance/originals/2020/06")
|
||||
.length;
|
||||
await t.expect(originalsLengthAfterDelete).eql(0);
|
||||
const sidecarLengthAfterDelete = fs.readdirSync("../storage/acceptance/originals/2020/06").length;
|
||||
await t.expect(sidecarLengthAfterDelete).eql(0);
|
||||
if (t.browser.platform !== "mobile") {
|
||||
const originalsLengthAfterDelete = fs.readdirSync(
|
||||
"../storage/acceptance/originals/2020/06"
|
||||
).length;
|
||||
await t.expect(originalsLengthAfterDelete).eql(0);
|
||||
const sidecarLengthAfterDelete = fs.readdirSync(
|
||||
"../storage/acceptance/originals/2020/06"
|
||||
).length;
|
||||
await t.expect(sidecarLengthAfterDelete).eql(0);
|
||||
}
|
||||
});
|
||||
|
||||
test.meta("testID", "photos-upload-delete-003")("Upload to existing Album + Delete", async (t) => {
|
||||
|
@ -201,8 +207,6 @@ test.meta("testID", "photos-upload-delete-004")("Upload jpg to new Album + Delet
|
|||
});
|
||||
|
||||
test.meta("testID", "photos-upload-delete-005")("Try uploading nsfw file", async (t) => {
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-browse"));
|
||||
await t
|
||||
.click(Selector(".action-upload"))
|
||||
.setFilesToUpload(Selector(".input-upload"), ["./upload-files/hentai_2.jpg"])
|
||||
|
@ -216,8 +220,6 @@ test.meta("testID", "photos-upload-delete-005")("Try uploading nsfw file", async
|
|||
});
|
||||
|
||||
test.meta("testID", "photos-upload-delete-006")("Try uploading txt file", async (t) => {
|
||||
await page.openNav();
|
||||
await t.click(Selector(".nav-browse"));
|
||||
await t
|
||||
.click(Selector(".action-upload"))
|
||||
.setFilesToUpload(Selector(".input-upload"), ["./upload-files/foo.txt"])
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue