mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 13:37:35 +00:00
acceptance
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,16 +7,12 @@ dist
|
|||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
.env
|
.env
|
||||||
.cache
|
|
||||||
server/dist
|
server/dist
|
||||||
public/dist
|
public/dist
|
||||||
.vscode
|
.vscode
|
||||||
.idea
|
.idea
|
||||||
.vercel
|
.vercel
|
||||||
.env*.local
|
.env*.local
|
||||||
/test-results/
|
|
||||||
/playwright-report/
|
|
||||||
/blob-report/
|
/blob-report/
|
||||||
/playwright/.cache/
|
|
||||||
/out
|
/out
|
||||||
/docker
|
/docker
|
||||||
|
@@ -94,6 +94,8 @@ pnpm run-oidcop
|
|||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
|
To test the quality of your code, make sure
|
||||||
|
|
||||||
You can execute the following commands `pnpm test` for a single test run or `pnpm test:watch` in the following directories:
|
You can execute the following commands `pnpm test` for a single test run or `pnpm test:watch` in the following directories:
|
||||||
|
|
||||||
- apps/login
|
- apps/login
|
||||||
|
11
Makefile
11
Makefile
@@ -5,6 +5,8 @@ export BAKE_CLI ?= docker buildx bake
|
|||||||
BAKE_CLI_WITH_COMMON_ARGS := $(BAKE_CLI) --file ./docker-bake.hcl --file ./apps/login-test-acceptance/docker-compose.yaml
|
BAKE_CLI_WITH_COMMON_ARGS := $(BAKE_CLI) --file ./docker-bake.hcl --file ./apps/login-test-acceptance/docker-compose.yaml
|
||||||
|
|
||||||
export COMPOSE_BAKE=true
|
export COMPOSE_BAKE=true
|
||||||
|
export UID := $(id -u)
|
||||||
|
export GID := $(id -g)
|
||||||
|
|
||||||
export LOGIN_TEST_ACCEPTANCE_BUILD_CONTEXT := apps/login-test-acceptance
|
export LOGIN_TEST_ACCEPTANCE_BUILD_CONTEXT := apps/login-test-acceptance
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ login-test-unit:
|
|||||||
login-test-integration-build:
|
login-test-integration-build:
|
||||||
$(BAKE_CLI_WITH_COMMON_ARGS) core-mock login-test-integration login-standalone
|
$(BAKE_CLI_WITH_COMMON_ARGS) core-mock login-test-integration login-standalone
|
||||||
|
|
||||||
login-test-integration-dev:
|
login-test-integration-dev: login-test-integration-cleanup
|
||||||
$(BAKE_CLI_WITH_COMMON_ARGS) core-mock && docker compose --file ./apps/login-test-integration/docker-compose.yaml run --service-ports --rm core-mock
|
$(BAKE_CLI_WITH_COMMON_ARGS) core-mock && docker compose --file ./apps/login-test-integration/docker-compose.yaml run --service-ports --rm core-mock
|
||||||
|
|
||||||
login-test-integration-run: login-test-integration-cleanup
|
login-test-integration-run: login-test-integration-cleanup
|
||||||
@@ -72,11 +74,14 @@ login-test-acceptance-build-compose:
|
|||||||
login-test-acceptance-build: login-test-acceptance-build-compose login-test-acceptance-build-bake
|
login-test-acceptance-build: login-test-acceptance-build-compose login-test-acceptance-build-bake
|
||||||
@:
|
@:
|
||||||
|
|
||||||
|
login-test-acceptance-dev: login-test-acceptance-build-compose login-test-acceptance-cleanup
|
||||||
|
docker compose --file ./apps/login-test-acceptance/docker-compose.yaml up zitadel setup traefik setup sink
|
||||||
|
|
||||||
login-test-acceptance-run: login-test-acceptance-cleanup
|
login-test-acceptance-run: login-test-acceptance-cleanup
|
||||||
docker compose --file ./apps/login-test-acceptance/docker-compose.yaml run --rm --service-ports acceptance
|
docker compose --file ./apps/login-test-acceptance/docker-compose.yaml --file ./apps/login-test-acceptance/docker-compose-ci.yaml run --rm --service-ports acceptance
|
||||||
|
|
||||||
login-test-acceptance-cleanup:
|
login-test-acceptance-cleanup:
|
||||||
docker compose --file ./apps/login-test-acceptance/docker-compose.yaml down --volumes
|
docker compose --file ./apps/login-test-acceptance/docker-compose.yaml --file ./apps/login-test-acceptance/docker-compose-ci.yaml down --volumes
|
||||||
|
|
||||||
login-test-acceptance: login-test-acceptance-build
|
login-test-acceptance: login-test-acceptance-build
|
||||||
./scripts/run_or_skip.sh login-test-acceptance-run \
|
./scripts/run_or_skip.sh login-test-acceptance-run \
|
||||||
|
2
apps/login-test-acceptance/.gitignore
vendored
2
apps/login-test-acceptance/.gitignore
vendored
@@ -1,3 +1 @@
|
|||||||
go-command
|
go-command
|
||||||
.env.local
|
|
||||||
test-results
|
|
||||||
|
57
apps/login-test-acceptance/docker-compose-ci.yaml
Normal file
57
apps/login-test-acceptance/docker-compose-ci.yaml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
services:
|
||||||
|
|
||||||
|
zitadel:
|
||||||
|
environment:
|
||||||
|
ZITADEL_EXTERNALDOMAIN: traefik
|
||||||
|
|
||||||
|
traefik:
|
||||||
|
labels: !reset []
|
||||||
|
|
||||||
|
setup:
|
||||||
|
environment:
|
||||||
|
WRITE_ENVIRONMENT_FILE: /login-env/.env
|
||||||
|
ZITADEL_API_DOMAIN: traefik
|
||||||
|
ZITADEL_API_URL: https://traefik
|
||||||
|
LOGIN_BASE_URL: https://traefik/ui/v2/login/
|
||||||
|
SINK_NOTIFICATION_URL: http://sink:3333/notification
|
||||||
|
ZITADEL_ADMIN_USER: zitadel-admin@zitadel.traefik
|
||||||
|
|
||||||
|
login:
|
||||||
|
image: "${LOGIN_TAG:-login:local}"
|
||||||
|
container_name: acceptance-login
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.login.rule=PathPrefix(`/ui/v2/login`)"
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||||
|
depends_on:
|
||||||
|
setup:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
|
||||||
|
acceptance:
|
||||||
|
user: "${UID:-1000}:${GID:-1000}"
|
||||||
|
image: "${LOGIN_TEST_ACCEPTANCE_TAG:-login-test-acceptance:local}"
|
||||||
|
container_name: acceptance
|
||||||
|
environment:
|
||||||
|
- CI
|
||||||
|
- LOGIN_BASE_URL=https://traefik/ui/v2/login/
|
||||||
|
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||||
|
ports:
|
||||||
|
- 9323:9323
|
||||||
|
ipc: "host"
|
||||||
|
init: true
|
||||||
|
depends_on:
|
||||||
|
login:
|
||||||
|
condition: "service_healthy"
|
||||||
|
sink:
|
||||||
|
condition: service_healthy
|
||||||
|
# oidcrp:
|
||||||
|
# condition: service_healthy
|
||||||
|
# oidcop:
|
||||||
|
# condition: service_healthy
|
||||||
|
# samlsp:
|
||||||
|
# condition: service_healthy
|
||||||
|
# samlidp:
|
||||||
|
# condition: service_healthy
|
@@ -1,14 +0,0 @@
|
|||||||
services:
|
|
||||||
traefik:
|
|
||||||
extra_hosts:
|
|
||||||
- host.docker.internal:host-gateway
|
|
||||||
setup:
|
|
||||||
environment:
|
|
||||||
LOGIN_BASE_URL: https://localhost/ui/v2/login/
|
|
||||||
ZITADEL_API_INTERNAL_URL: http://zitadel:8080
|
|
||||||
ZITADEL_API_URL: https://localhost
|
|
||||||
ZITADEL_API_DOMAIN: localhost
|
|
||||||
volumes:
|
|
||||||
- pat:/pat # Read the PAT file from zitadels setup
|
|
||||||
- ./env:/acceptance-env # Write the environment variables file for the tests
|
|
||||||
- ../login:/login-env # Write the environment variables file for the login
|
|
@@ -1,6 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
|
|
||||||
zitadel:
|
zitadel:
|
||||||
user: "root"
|
user: "${UID:-1000}:${GID:-1000}"
|
||||||
image: "${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:latest}"
|
image: "${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:latest}"
|
||||||
container_name: acceptance-zitadel
|
container_name: acceptance-zitadel
|
||||||
pull_policy: always
|
pull_policy: always
|
||||||
@@ -8,12 +9,14 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.zitadel.rule=!PathPrefix(`/ui/v2/login`)"
|
- "traefik.http.routers.zitadel.rule=!PathPrefix(`/ui/v2/login`)"
|
||||||
|
# - "traefik.http.middlewares.zitadel.headers.customrequestheaders.Host=localhost"
|
||||||
|
# - "traefik.http.routers.zitadel.middlewares=zitadel@docker"
|
||||||
- "traefik.http.services.zitadel-service.loadbalancer.server.scheme=h2c"
|
- "traefik.http.services.zitadel-service.loadbalancer.server.scheme=h2c"
|
||||||
- "traefik.http.middlewares.zitadel-headers.headers.customrequestheaders.Host=zitadel"
|
- "traefik.http.services.zitadel-service.loadbalancer.passHostHeader=false"
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- pat:/pat
|
- ./pat:/pat
|
||||||
- ./zitadel.yaml:/zitadel.yaml
|
- ./zitadel.yaml:/zitadel.yaml
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
@@ -48,13 +51,16 @@ services:
|
|||||||
traefik:
|
traefik:
|
||||||
image: "traefik:v3.4"
|
image: "traefik:v3.4"
|
||||||
container_name: "acceptance-traefik"
|
container_name: "acceptance-traefik"
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.login.rule=PathPrefix(`/ui/v2/login`)"
|
||||||
|
- "traefik.http.services.login-service.loadbalancer.server.url=http://host.docker.internal:3000"
|
||||||
command:
|
command:
|
||||||
- "--log.level=DEBUG"
|
- "--log.level=DEBUG"
|
||||||
- "--ping"
|
- "--ping"
|
||||||
- "--api.insecure=true"
|
- "--api.insecure=true"
|
||||||
- "--providers.docker=true"
|
- "--providers.docker=true"
|
||||||
- "--providers.docker.exposedbydefault=false"
|
- "--providers.docker.exposedbydefault=false"
|
||||||
- "--entryPoints.web.address=:80"
|
|
||||||
- "--entrypoints.websecure.http.tls=true"
|
- "--entrypoints.websecure.http.tls=true"
|
||||||
- "--entryPoints.websecure.address=:443"
|
- "--entryPoints.websecure.address=:443"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@@ -67,51 +73,39 @@ services:
|
|||||||
- "443:443"
|
- "443:443"
|
||||||
volumes:
|
volumes:
|
||||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||||
depends_on:
|
extra_hosts:
|
||||||
wait-for-zitadel:
|
- host.docker.internal:host-gateway
|
||||||
condition: "service_completed_successfully"
|
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
|
user: "${UID:-1000}:${GID:-1000}"
|
||||||
image: ${LOGIN_TEST_ACCEPTANCE_SETUP_TAG:-login-test-acceptance-setup:local}
|
image: ${LOGIN_TEST_ACCEPTANCE_SETUP_TAG:-login-test-acceptance-setup:local}
|
||||||
container_name: acceptance-setup
|
container_name: acceptance-setup
|
||||||
|
restart: no
|
||||||
build:
|
build:
|
||||||
context: "${LOGIN_TEST_ACCEPTANCE_BUILD_CONTEXT:-.}/setup"
|
context: "${LOGIN_TEST_ACCEPTANCE_BUILD_CONTEXT:-.}/setup"
|
||||||
dockerfile: ../go-command.Dockerfile
|
dockerfile: ../go-command.Dockerfile
|
||||||
entrypoint: "./setup.sh"
|
entrypoint: "./setup.sh"
|
||||||
environment:
|
environment:
|
||||||
PAT_FILE: /pat/zitadel-admin-sa.pat
|
PAT_FILE: /pat/zitadel-admin-sa.pat
|
||||||
LOGIN_BASE_URL: https://traefik/ui/v2/login/
|
ZITADEL_API_INTERNAL_URL: http://zitadel:8080
|
||||||
ZITADEL_API_INTERNAL_URL: http://traefik
|
WRITE_ENVIRONMENT_FILE: /login-env/.env.local
|
||||||
WRITE_ENVIRONMENT_FILE: /login-env/.env
|
|
||||||
WRITE_TEST_ENVIRONMENT_FILE: /acceptance-env/.env
|
WRITE_TEST_ENVIRONMENT_FILE: /acceptance-env/.env
|
||||||
SINK_EMAIL_INTERNAL_URL: http://sink:3333/email
|
SINK_EMAIL_INTERNAL_URL: http://sink:3333/email
|
||||||
SINK_SMS_INTERNAL_URL: http://sink:3333/sms
|
SINK_SMS_INTERNAL_URL: http://sink:3333/sms
|
||||||
SINK_NOTIFICATION_URL: http://sink:3333/notification
|
SINK_NOTIFICATION_URL: http://localhost:3333/notification
|
||||||
ZITADEL_API_DOMAIN: traefik
|
LOGIN_BASE_URL: https://localhost/ui/v2/login/
|
||||||
ZITADEL_API_URL: https://traefik
|
ZITADEL_API_URL: https://localhost
|
||||||
|
ZITADEL_API_DOMAIN: localhost
|
||||||
|
ZITADEL_ADMIN_USER: zitadel-admin@zitadel.localhost
|
||||||
volumes:
|
volumes:
|
||||||
- "pat:/pat" # Read the PAT file from zitadels setup
|
- ./pat:/pat # Read the PAT file from zitadels setup
|
||||||
- "acceptance-env:/acceptance-env" # Write the environment variables file for the tests
|
- ./env:/acceptance-env # Write the environment variables file for the tests
|
||||||
- "login-env:/login-env" # Write the environment variables file for the login
|
- ../login:/login-env # Write the environment variables file for the login
|
||||||
depends_on:
|
depends_on:
|
||||||
traefik:
|
traefik:
|
||||||
condition: "service_healthy"
|
condition: "service_healthy"
|
||||||
|
wait-for-zitadel:
|
||||||
login:
|
condition: "service_completed_successfully"
|
||||||
image: "${LOGIN_TAG:-login:local}"
|
|
||||||
container_name: acceptance-login
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.routers.login.rule=PathPrefix(`/ui/v2/login`)"
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
volumes:
|
|
||||||
- "login-env:/.env-file/"
|
|
||||||
environment:
|
|
||||||
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
|
||||||
depends_on:
|
|
||||||
setup:
|
|
||||||
condition: service_completed_successfully
|
|
||||||
|
|
||||||
sink:
|
sink:
|
||||||
image: ${LOGIN_TEST_ACCEPTANCE_SINK_TAG:-login-test-acceptance-sink:local}
|
image: ${LOGIN_TEST_ACCEPTANCE_SINK_TAG:-login-test-acceptance-sink:local}
|
||||||
@@ -139,6 +133,7 @@ services:
|
|||||||
condition: "service_completed_successfully"
|
condition: "service_completed_successfully"
|
||||||
|
|
||||||
oidcrp:
|
oidcrp:
|
||||||
|
user: "${UID:-1000}:${GID:-1000}"
|
||||||
image: ${LOGIN_TEST_ACCEPTANCE_OIDCRP_TAG:-login-test-acceptance-oidcrp:local}
|
image: ${LOGIN_TEST_ACCEPTANCE_OIDCRP_TAG:-login-test-acceptance-oidcrp:local}
|
||||||
container_name: acceptance-oidcrp
|
container_name: acceptance-oidcrp
|
||||||
build:
|
build:
|
||||||
@@ -158,14 +153,15 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
volumes:
|
volumes:
|
||||||
- "pat:/pat"
|
- "./pat:/pat"
|
||||||
depends_on:
|
depends_on:
|
||||||
traefik:
|
traefik:
|
||||||
condition: "service_healthy"
|
condition: "service_healthy"
|
||||||
login:
|
setup:
|
||||||
condition: "service_healthy"
|
condition: "service_completed_successfully"
|
||||||
|
|
||||||
oidcop:
|
oidcop:
|
||||||
|
user: "${UID:-1000}:${GID:-1000}"
|
||||||
image: ${LOGIN_TEST_ACCEPTANCE_OIDCOP_TAG:-login-test-acceptance-oidcop:local}
|
image: ${LOGIN_TEST_ACCEPTANCE_OIDCOP_TAG:-login-test-acceptance-oidcop:local}
|
||||||
container_name: acceptance-oidcop
|
container_name: acceptance-oidcop
|
||||||
build:
|
build:
|
||||||
@@ -183,14 +179,15 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8004:8004
|
- 8004:8004
|
||||||
volumes:
|
volumes:
|
||||||
- "pat:/pat"
|
- "./pat:/pat"
|
||||||
depends_on:
|
depends_on:
|
||||||
traefik:
|
traefik:
|
||||||
condition: "service_healthy"
|
condition: "service_healthy"
|
||||||
login:
|
setup:
|
||||||
condition: "service_healthy"
|
condition: "service_completed_successfully"
|
||||||
|
|
||||||
samlsp:
|
samlsp:
|
||||||
|
user: "${UID:-1000}:${GID:-1000}"
|
||||||
image: "${LOGIN_TEST_ACCEPTANCE_SAMLSP_TAG:-login-test-acceptance-samlsp:local}"
|
image: "${LOGIN_TEST_ACCEPTANCE_SAMLSP_TAG:-login-test-acceptance-samlsp:local}"
|
||||||
container_name: acceptance-samlsp
|
container_name: acceptance-samlsp
|
||||||
build:
|
build:
|
||||||
@@ -203,18 +200,21 @@ services:
|
|||||||
API_DOMAIN: 'traefik'
|
API_DOMAIN: 'traefik'
|
||||||
PAT_FILE: '/pat/zitadel-admin-sa.pat'
|
PAT_FILE: '/pat/zitadel-admin-sa.pat'
|
||||||
LOGIN_URL: 'https://traefik/ui/v2/login'
|
LOGIN_URL: 'https://traefik/ui/v2/login'
|
||||||
IDP_URL: 'http://traefik/saml/v2/metadata'
|
IDP_URL: 'http://zitadel:8080/saml/v2/metadata'
|
||||||
HOST: 'https://traefik'
|
HOST: 'https://traefik'
|
||||||
PORT: '8001'
|
PORT: '8001'
|
||||||
ports:
|
ports:
|
||||||
- 8001:8001
|
- 8001:8001
|
||||||
volumes:
|
volumes:
|
||||||
- "pat:/pat"
|
- "./pat:/pat"
|
||||||
depends_on:
|
depends_on:
|
||||||
traefik:
|
traefik:
|
||||||
condition: "service_healthy"
|
condition: "service_healthy"
|
||||||
|
setup:
|
||||||
|
condition: "service_completed_successfully"
|
||||||
|
|
||||||
samlidp:
|
samlidp:
|
||||||
|
user: "${UID:-1000}:${GID:-1000}"
|
||||||
image: "${LOGIN_TEST_ACCEPTANCE_SAMLIDP_TAG:-login-test-acceptance-samlidp:local}"
|
image: "${LOGIN_TEST_ACCEPTANCE_SAMLIDP_TAG:-login-test-acceptance-samlidp:local}"
|
||||||
container_name: acceptance-samlidp
|
container_name: acceptance-samlidp
|
||||||
build:
|
build:
|
||||||
@@ -232,41 +232,9 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 8003:8003
|
- 8003:8003
|
||||||
volumes:
|
volumes:
|
||||||
- "pat:/pat"
|
- "./pat:/pat"
|
||||||
depends_on:
|
depends_on:
|
||||||
traefik:
|
traefik:
|
||||||
condition: "service_healthy"
|
condition: "service_healthy"
|
||||||
|
setup:
|
||||||
acceptance:
|
condition: "service_completed_successfully"
|
||||||
image: "${LOGIN_TEST_ACCEPTANCE_TAG:-login-test-acceptance:local}"
|
|
||||||
container_name: acceptance
|
|
||||||
environment:
|
|
||||||
- CI
|
|
||||||
- LOGIN_BASE_URL=https://traefik/ui/v2/login/
|
|
||||||
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
|
||||||
volumes:
|
|
||||||
- "acceptance-env:/build/apps/login-test-acceptance/.env-file/"
|
|
||||||
- "pat:/pat"
|
|
||||||
- "./test-results:/build/apps/login-test-acceptance/test-results"
|
|
||||||
ports:
|
|
||||||
- 9323:9323
|
|
||||||
ipc: "host"
|
|
||||||
init: true
|
|
||||||
depends_on:
|
|
||||||
login:
|
|
||||||
condition: "service_healthy"
|
|
||||||
sink:
|
|
||||||
condition: service_healthy
|
|
||||||
# oidcrp:
|
|
||||||
# condition: service_healthy
|
|
||||||
# oidcop:
|
|
||||||
# condition: service_healthy
|
|
||||||
# samlsp:
|
|
||||||
# condition: service_healthy
|
|
||||||
# samlidp:
|
|
||||||
# condition: service_healthy
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
pat:
|
|
||||||
login-env:
|
|
||||||
acceptance-env:
|
|
||||||
|
2
apps/login-test-acceptance/env/.gitignore
vendored
Normal file
2
apps/login-test-acceptance/env/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitkeep
|
0
apps/login-test-acceptance/env/.gitkeep
vendored
Normal file
0
apps/login-test-acceptance/env/.gitkeep
vendored
Normal file
@@ -3,14 +3,15 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test:acceptance": "pnpm exec playwright",
|
"test:acceptance": "pnpm exec playwright",
|
||||||
"test:acceptance:setup": "pnpm exec playwright"
|
"test:acceptance:setup": "cd ../.. && make login-test-acceptance-dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@otplib/core": "^12.0.0",
|
|
||||||
"@otplib/plugin-thirty-two": "^12.0.0",
|
|
||||||
"@otplib/plugin-crypto": "^12.0.0",
|
|
||||||
"@faker-js/faker": "^9.7.0",
|
"@faker-js/faker": "^9.7.0",
|
||||||
|
"@otplib/core": "^12.0.0",
|
||||||
|
"@otplib/plugin-crypto": "^12.0.0",
|
||||||
|
"@otplib/plugin-thirty-two": "^12.0.0",
|
||||||
"@playwright/test": "^1.52.0",
|
"@playwright/test": "^1.52.0",
|
||||||
|
"gaxios": "^7.1.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
apps/login-test-acceptance/pat/.gitignore
vendored
Normal file
2
apps/login-test-acceptance/pat/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitkeep
|
2
apps/login-test-acceptance/playwright-report/.gitignore
vendored
Normal file
2
apps/login-test-acceptance/playwright-report/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitkeep
|
@@ -1,12 +1,8 @@
|
|||||||
import { defineConfig, devices } from "@playwright/test";
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
/**
|
dotenv.config({ path: path.resolve(__dirname, "./env/.env") });
|
||||||
* Read environment variables from file.
|
|
||||||
* https://github.com/motdotla/dotenv
|
|
||||||
*/
|
|
||||||
// import dotenv from 'dotenv';
|
|
||||||
// import path from 'path';
|
|
||||||
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://playwright.dev/docs/test-configuration.
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
set -e
|
set -ex pipefail
|
||||||
|
|
||||||
PAT_FILE=${PAT_FILE:-./pat/zitadel-admin-sa.pat}
|
PAT_FILE=${PAT_FILE:-./pat/zitadel-admin-sa.pat}
|
||||||
LOGIN_BASE_URL=${LOGIN_BASE_URL:-"http://localhost:3000"}
|
LOGIN_BASE_URL=${LOGIN_BASE_URL:-"http://localhost:3000"}
|
||||||
@@ -68,6 +68,9 @@ SINK_NOTIFICATION_URL=${SINK_NOTIFICATION_URL}
|
|||||||
EMAIL_VERIFICATION=true
|
EMAIL_VERIFICATION=true
|
||||||
DEBUG=false
|
DEBUG=false
|
||||||
LOGIN_BASE_URL=${LOGIN_BASE_URL}
|
LOGIN_BASE_URL=${LOGIN_BASE_URL}
|
||||||
|
NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||||
|
ZITADEL_ADMIN_USER=${ZITADEL_ADMIN_USER:-"zitadel-admin@zitadel.localhost"}
|
||||||
|
NEXT_PUBLIC_BASE_PATH=/ui/v2/login
|
||||||
" | tee "${WRITE_ENVIRONMENT_FILE}" "${WRITE_TEST_ENVIRONMENT_FILE}" > /dev/null
|
" | tee "${WRITE_ENVIRONMENT_FILE}" "${WRITE_TEST_ENVIRONMENT_FILE}" > /dev/null
|
||||||
|
|
||||||
echo "Wrote environment file ${WRITE_ENVIRONMENT_FILE}"
|
echo "Wrote environment file ${WRITE_ENVIRONMENT_FILE}"
|
||||||
|
@@ -84,12 +84,17 @@ func main() {
|
|||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
msg, ok := messages[response.Recipient]
|
||||||
serializableData, err := json.Marshal(messages[response.Recipient])
|
if !ok {
|
||||||
|
http.Error(w, "No messages found for recipient: "+response.Recipient, http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serializableData, err := json.Marshal(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
io.WriteString(w, string(serializableData))
|
io.WriteString(w, string(serializableData))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
2
apps/login-test-acceptance/test-results/.gitignore
vendored
Normal file
2
apps/login-test-acceptance/test-results/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitkeep
|
0
apps/login-test-acceptance/test-results/.gitkeep
Normal file
0
apps/login-test-acceptance/test-results/.gitkeep
Normal file
@@ -2,6 +2,6 @@ import { test } from "@playwright/test";
|
|||||||
import { loginScreenExpect, loginWithPassword } from "./login";
|
import { loginScreenExpect, loginWithPassword } from "./login";
|
||||||
|
|
||||||
test("admin login", async ({ page }) => {
|
test("admin login", async ({ page }) => {
|
||||||
await loginWithPassword(page, "zitadel-admin@zitadel.traefik", "Password1!");
|
await loginWithPassword(page, process.env["ZITADEL_ADMIN_USER"], "Password1!");
|
||||||
await loginScreenExpect(page, "ZITADEL Admin");
|
await loginScreenExpect(page, "ZITADEL Admin");
|
||||||
});
|
});
|
||||||
|
@@ -3,8 +3,6 @@ import { codeScreen } from "./code-screen";
|
|||||||
import { getOtpFromSink } from "./sink";
|
import { getOtpFromSink } from "./sink";
|
||||||
|
|
||||||
export async function otpFromSink(page: Page, key: string) {
|
export async function otpFromSink(page: Page, key: string) {
|
||||||
// wait for send of the code
|
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
const c = await getOtpFromSink(key);
|
const c = await getOtpFromSink(key);
|
||||||
await code(page, c);
|
await code(page, c);
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import { getCodeFromSink } from "./sink";
|
|||||||
import { PasswordUser } from "./user";
|
import { PasswordUser } from "./user";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../.env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
const test = base.extend<{ user: PasswordUser }>({
|
const test = base.extend<{ user: PasswordUser }>({
|
||||||
user: async ({ page }, use) => {
|
user: async ({ page }, use) => {
|
||||||
@@ -32,11 +32,10 @@ const test = base.extend<{ user: PasswordUser }>({
|
|||||||
|
|
||||||
test("user email not verified, verify", async ({ user, page }) => {
|
test("user email not verified, verify", async ({ user, page }) => {
|
||||||
await loginWithPassword(page, user.getUsername(), user.getPassword());
|
await loginWithPassword(page, user.getUsername(), user.getPassword());
|
||||||
// auto-redirect on /verify
|
|
||||||
// wait for send of the code
|
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
const c = await getCodeFromSink(user.getUsername());
|
const c = await getCodeFromSink(user.getUsername());
|
||||||
await emailVerify(page, c);
|
await emailVerify(page, c);
|
||||||
|
// wait for resend of the code
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
await loginScreenExpect(page, user.getFullName());
|
await loginScreenExpect(page, user.getFullName());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -44,22 +43,18 @@ test("user email not verified, resend, verify", async ({ user, page }) => {
|
|||||||
await loginWithPassword(page, user.getUsername(), user.getPassword());
|
await loginWithPassword(page, user.getUsername(), user.getPassword());
|
||||||
// auto-redirect on /verify
|
// auto-redirect on /verify
|
||||||
await emailVerifyResend(page);
|
await emailVerifyResend(page);
|
||||||
// wait for send of the code
|
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
const c = await getCodeFromSink(user.getUsername());
|
const c = await getCodeFromSink(user.getUsername());
|
||||||
await emailVerify(page, c);
|
// wait for resend of the code
|
||||||
|
await page.waitForTimeout(2000); await emailVerify(page, c);
|
||||||
await loginScreenExpect(page, user.getFullName());
|
await loginScreenExpect(page, user.getFullName());
|
||||||
});
|
});
|
||||||
|
|
||||||
test("user email not verified, resend, old code", async ({ user, page }) => {
|
test("user email not verified, resend, old code", async ({ user, page }) => {
|
||||||
await loginWithPassword(page, user.getUsername(), user.getPassword());
|
await loginWithPassword(page, user.getUsername(), user.getPassword());
|
||||||
// auto-redirect on /verify
|
|
||||||
// wait for send of the code
|
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
const c = await getCodeFromSink(user.getUsername());
|
const c = await getCodeFromSink(user.getUsername());
|
||||||
await emailVerifyResend(page);
|
await emailVerifyResend(page);
|
||||||
// wait for resend of the code
|
// wait for resend of the code
|
||||||
await page.waitForTimeout(10000);
|
await page.waitForTimeout(2000);
|
||||||
await emailVerify(page, c);
|
await emailVerify(page, c);
|
||||||
await emailVerifyScreenExpect(page, c);
|
await emailVerifyScreenExpect(page, c);
|
||||||
});
|
});
|
||||||
|
@@ -1,13 +1,9 @@
|
|||||||
import { expect, Page } from "@playwright/test";
|
import { expect, Page } from "@playwright/test";
|
||||||
import dotenv from "dotenv";
|
|
||||||
import path from "path";
|
|
||||||
import { code, otpFromSink } from "./code";
|
import { code, otpFromSink } from "./code";
|
||||||
import { loginname } from "./loginname";
|
import { loginname } from "./loginname";
|
||||||
import { password } from "./password";
|
import { password } from "./password";
|
||||||
import { totp } from "./zitadel";
|
import { totp } from "./zitadel";
|
||||||
|
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../.env-file/.env") });
|
|
||||||
|
|
||||||
export async function startLogin(page: Page) {
|
export async function startLogin(page: Page) {
|
||||||
await page.goto(`./loginname`);
|
await page.goto(`./loginname`);
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@ import { getCodeFromSink } from "./sink";
|
|||||||
|
|
||||||
const codeField = "code-text-input";
|
const codeField = "code-text-input";
|
||||||
const passwordField = "password-text-input";
|
const passwordField = "password-text-input";
|
||||||
const passwordConfirmField = "password-confirm-text-input";
|
|
||||||
const passwordChangeField = "password-change-text-input";
|
const passwordChangeField = "password-change-text-input";
|
||||||
const passwordChangeConfirmField = "password-change-confirm-text-input";
|
const passwordChangeConfirmField = "password-change-confirm-text-input";
|
||||||
const passwordSetField = "password-set-text-input";
|
const passwordSetField = "password-set-text-input";
|
||||||
@@ -75,8 +74,6 @@ async function checkContent(page: Page, testid: string, match: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function resetPasswordScreen(page: Page, username: string, password1: string, password2: string) {
|
export async function resetPasswordScreen(page: Page, username: string, password1: string, password2: string) {
|
||||||
// wait for send of the code
|
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
const c = await getCodeFromSink(username);
|
const c = await getCodeFromSink(username);
|
||||||
await page.getByTestId(codeField).pressSequentially(c);
|
await page.getByTestId(codeField).pressSequentially(c);
|
||||||
await page.getByTestId(passwordSetField).pressSequentially(password1);
|
await page.getByTestId(passwordSetField).pressSequentially(password1);
|
||||||
|
@@ -7,7 +7,7 @@ import { registerWithPasskey, registerWithPassword } from "./register";
|
|||||||
import { removeUserByUsername } from "./zitadel";
|
import { removeUserByUsername } from "./zitadel";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
test("register with password", async ({ page }) => {
|
test("register with password", async ({ page }) => {
|
||||||
const username = faker.internet.email();
|
const username = faker.internet.email();
|
||||||
|
@@ -17,8 +17,6 @@ export async function registerWithPassword(
|
|||||||
await page.getByTestId("submit-button").click();
|
await page.getByTestId("submit-button").click();
|
||||||
await registerPasswordScreen(page, password1, password2);
|
await registerPasswordScreen(page, password1, password2);
|
||||||
await page.getByTestId("submit-button").click();
|
await page.getByTestId("submit-button").click();
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
|
|
||||||
await verifyEmail(page, email);
|
await verifyEmail(page, email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +34,6 @@ export async function registerWithPasskey(page: Page, firstname: string, lastnam
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function verifyEmail(page: Page, email: string) {
|
async function verifyEmail(page: Page, email: string) {
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
const c = await getCodeFromSink(email);
|
const c = await getCodeFromSink(email);
|
||||||
await emailVerify(page, c);
|
await emailVerify(page, c);
|
||||||
}
|
}
|
||||||
|
@@ -1,55 +1,43 @@
|
|||||||
import axios from "axios";
|
import {Gaxios, GaxiosResponse} from 'gaxios';
|
||||||
|
|
||||||
export async function getOtpFromSink(key: string): Promise<any> {
|
const awaitNotification = new Gaxios({
|
||||||
try {
|
url: process.env.SINK_NOTIFICATION_URL,
|
||||||
const response = await axios.post(
|
method: 'POST',
|
||||||
process.env.SINK_NOTIFICATION_URL!,
|
retryConfig: {
|
||||||
{
|
httpMethodsToRetry: ['POST'],
|
||||||
recipient: key,
|
statusCodesToRetry: [[404, 404]],
|
||||||
},
|
retry: Number.MAX_SAFE_INTEGER, // totalTimeout limits the number of retries
|
||||||
{
|
totalTimeout: 10000, // 10 seconds
|
||||||
headers: {
|
onRetryAttempt: (error) => {
|
||||||
"Content-Type": "application/json",
|
console.warn(`Retrying request to sink notification service: ${error.message}`);
|
||||||
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
|
}
|
||||||
},
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (response.status >= 400) {
|
export async function getOtpFromSink(recipient: string): Promise<any> {
|
||||||
const error = `HTTP Error: ${response.status} - ${response.statusText}`;
|
return awaitNotification.request({data: {recipient}}).then((response) => {
|
||||||
console.error(error);
|
expectSuccess(response);
|
||||||
throw new Error(error);
|
const otp = response?.data?.args?.otp
|
||||||
}
|
if (!otp) {
|
||||||
return response.data.args.otp;
|
throw new Error(`Response does not contain an otp property: ${JSON.stringify(response.data, null, 2)}`);
|
||||||
} catch (error) {
|
|
||||||
console.error("Error making request:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
return otp;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCodeFromSink(key: string): Promise<any> {
|
export async function getCodeFromSink(recipient: string): Promise<any> {
|
||||||
try {
|
return awaitNotification.request({data: {recipient}}).then((response) => {
|
||||||
const response = await axios.post(
|
expectSuccess(response);
|
||||||
process.env.SINK_NOTIFICATION_URL!,
|
const code = response?.data?.args?.code
|
||||||
{
|
if (!code) {
|
||||||
recipient: key,
|
throw new Error(`Response does not contain a code property: ${JSON.stringify(response.data, null, 2)}`);
|
||||||
},
|
}
|
||||||
{
|
return code;
|
||||||
headers: {
|
})
|
||||||
"Content-Type": "application/json",
|
}
|
||||||
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.status >= 400) {
|
function expectSuccess(response: GaxiosResponse): void {
|
||||||
const error = `HTTP Error: ${response.status} - ${response.statusText}`;
|
if (response.status !== 200) {
|
||||||
console.error(error);
|
throw new Error(`Expected HTTP status 200, but got: ${response.status} - ${response.statusText}`);
|
||||||
throw new Error(error);
|
|
||||||
}
|
|
||||||
return response.data.args.code;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error making request:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Page } from "@playwright/test";
|
import { Page } from "@playwright/test";
|
||||||
import { registerWithPasskey } from "./register";
|
import { registerWithPasskey } from "./register";
|
||||||
import { activateOTP, addTOTP, addUser, getUserByUsername, removeUser } from "./zitadel";
|
import {activateOTP, addTOTP, addUser, eventualNewUser, getUserByUsername, removeUser} from "./zitadel";
|
||||||
|
import {request} from 'gaxios';
|
||||||
|
|
||||||
export interface userProps {
|
export interface userProps {
|
||||||
email: string;
|
email: string;
|
||||||
@@ -68,8 +69,7 @@ class User {
|
|||||||
export class PasswordUser extends User {
|
export class PasswordUser extends User {
|
||||||
async ensure(page: Page) {
|
async ensure(page: Page) {
|
||||||
await super.ensure(page);
|
await super.ensure(page);
|
||||||
// wait for projection of user
|
await eventualNewUser(this.getUserId());
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,11 +111,8 @@ export class PasswordUserWithOTP extends User {
|
|||||||
|
|
||||||
async ensure(page: Page) {
|
async ensure(page: Page) {
|
||||||
await super.ensure(page);
|
await super.ensure(page);
|
||||||
|
|
||||||
await activateOTP(this.getUserId(), this.type);
|
await activateOTP(this.getUserId(), this.type);
|
||||||
|
await eventualNewUser(this.getUserId())
|
||||||
// wait for projection of user
|
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +121,8 @@ export class PasswordUserWithTOTP extends User {
|
|||||||
|
|
||||||
async ensure(page: Page) {
|
async ensure(page: Page) {
|
||||||
await super.ensure(page);
|
await super.ensure(page);
|
||||||
|
|
||||||
this.secret = await addTOTP(this.getUserId());
|
this.secret = await addTOTP(this.getUserId());
|
||||||
|
await eventualNewUser(this.getUserId())
|
||||||
// wait for projection of user
|
|
||||||
await page.waitForTimeout(10000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSecret(): string {
|
public getSecret(): string {
|
||||||
|
@@ -6,7 +6,7 @@ import { loginScreenExpect, loginWithPasskey } from "./login";
|
|||||||
import { PasskeyUser } from "./user";
|
import { PasskeyUser } from "./user";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
const test = base.extend<{ user: PasskeyUser }>({
|
const test = base.extend<{ user: PasskeyUser }>({
|
||||||
user: async ({ page }, use) => {
|
user: async ({ page }, use) => {
|
||||||
|
@@ -7,7 +7,7 @@ import { changePassword } from "./password";
|
|||||||
import { PasswordUser } from "./user";
|
import { PasswordUser } from "./user";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
const test = base.extend<{ user: PasswordUser }>({
|
const test = base.extend<{ user: PasswordUser }>({
|
||||||
user: async ({ page }, use) => {
|
user: async ({ page }, use) => {
|
||||||
|
@@ -8,7 +8,7 @@ import { changePasswordScreen, changePasswordScreenExpect } from "./password-scr
|
|||||||
import { PasswordUser } from "./user";
|
import { PasswordUser } from "./user";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
const test = base.extend<{ user: PasswordUser }>({
|
const test = base.extend<{ user: PasswordUser }>({
|
||||||
user: async ({ page }, use) => {
|
user: async ({ page }, use) => {
|
||||||
|
@@ -8,7 +8,7 @@ import { loginScreenExpect, loginWithPassword, loginWithPasswordAndEmailOTP } fr
|
|||||||
import { OtpType, PasswordUserWithOTP } from "./user";
|
import { OtpType, PasswordUserWithOTP } from "./user";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
const test = base.extend<{ user: PasswordUserWithOTP; sink: any }>({
|
const test = base.extend<{ user: PasswordUserWithOTP; sink: any }>({
|
||||||
user: async ({ page }, use) => {
|
user: async ({ page }, use) => {
|
||||||
|
@@ -8,7 +8,7 @@ import { loginScreenExpect, loginWithPassword, loginWithPasswordAndPhoneOTP } fr
|
|||||||
import { OtpType, PasswordUserWithOTP } from "./user";
|
import { OtpType, PasswordUserWithOTP } from "./user";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
const test = base.extend<{ user: PasswordUserWithOTP; sink: any }>({
|
const test = base.extend<{ user: PasswordUserWithOTP; sink: any }>({
|
||||||
user: async ({ page }, use) => {
|
user: async ({ page }, use) => {
|
||||||
|
@@ -9,7 +9,7 @@ import { resetPasswordScreen, resetPasswordScreenExpect } from "./password-scree
|
|||||||
import { PasswordUser } from "./user";
|
import { PasswordUser } from "./user";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
const test = base.extend<{ user: PasswordUser }>({
|
const test = base.extend<{ user: PasswordUser }>({
|
||||||
user: async ({ page }, use) => {
|
user: async ({ page }, use) => {
|
||||||
|
@@ -8,7 +8,7 @@ import { loginScreenExpect, loginWithPassword, loginWithPasswordAndTOTP } from "
|
|||||||
import { PasswordUserWithTOTP } from "./user";
|
import { PasswordUserWithTOTP } from "./user";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
const test = base.extend<{ user: PasswordUserWithTOTP; sink: any }>({
|
const test = base.extend<{ user: PasswordUserWithTOTP; sink: any }>({
|
||||||
user: async ({ page }, use) => {
|
user: async ({ page }, use) => {
|
||||||
|
@@ -10,7 +10,7 @@ import { passwordScreenExpect } from "./password-screen";
|
|||||||
import { PasswordUser } from "./user";
|
import { PasswordUser } from "./user";
|
||||||
|
|
||||||
// Read from ".env" file.
|
// Read from ".env" file.
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") });
|
||||||
|
|
||||||
const test = base.extend<{ user: PasswordUser }>({
|
const test = base.extend<{ user: PasswordUser }>({
|
||||||
user: async ({ page }, use) => {
|
user: async ({ page }, use) => {
|
||||||
|
@@ -5,8 +5,9 @@ import axios from "axios";
|
|||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { OtpType, userProps } from "./user";
|
import { OtpType, userProps } from "./user";
|
||||||
|
import {request} from "gaxios";
|
||||||
|
|
||||||
dotenv.config({ path: path.resolve(__dirname, "../.env-file/.env") });
|
dotenv.config({ path: path.resolve(__dirname, "../env/.env") })
|
||||||
|
|
||||||
export async function addUser(props: userProps) {
|
export async function addUser(props: userProps) {
|
||||||
const body = {
|
const body = {
|
||||||
@@ -168,3 +169,22 @@ export function totp(secret: string) {
|
|||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function eventualNewUser(id: string) {
|
||||||
|
return request({
|
||||||
|
url: `${process.env.ZITADEL_API_URL}/v2/users/${id}`,
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${process.env.ZITADEL_ADMIN_TOKEN}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
retryConfig: {
|
||||||
|
statusCodesToRetry: [[404, 404]],
|
||||||
|
retry: Number.MAX_SAFE_INTEGER, // totalTimeout limits the number of retries
|
||||||
|
totalTimeout: 10000, // 10 seconds
|
||||||
|
onRetryAttempt: (error) => {
|
||||||
|
console.warn(`Retrying to query new user ${id}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
11
apps/login-test-acceptance/turbo.json
Normal file
11
apps/login-test-acceptance/turbo.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": ["//"],
|
||||||
|
"tasks": {
|
||||||
|
"test:acceptance:setup": {
|
||||||
|
"interactive": true,
|
||||||
|
"cache": false,
|
||||||
|
"persistent": true,
|
||||||
|
"with": ["@zitadel/login#dev"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
ExternalDomain: zitadel
|
ExternalDomain: localhost
|
||||||
ExternalSecure: true
|
ExternalSecure: true
|
||||||
ExternalPort: 443
|
ExternalPort: 443
|
||||||
|
|
||||||
|
@@ -93,7 +93,7 @@ describe("verify invite", () => {
|
|||||||
stub("zitadel.user.v2.UserService", "VerifyInviteCode");
|
stub("zitadel.user.v2.UserService", "VerifyInviteCode");
|
||||||
|
|
||||||
cy.visit("/verify?userId=221394658884845598&code=abc&invite=true");
|
cy.visit("/verify?userId=221394658884845598&code=abc&invite=true");
|
||||||
cy.location("pathname", { timeout: 10_000 }).should("eq", "/ui/v2/login/authenticator/set");
|
cy.url({ timeout: 10_000 }).should("include", Cypress.config().baseUrl +"/authenticator/set");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows an error if invite code validation failed", () => {
|
it("shows an error if invite code validation failed", () => {
|
||||||
|
@@ -95,7 +95,7 @@ describe("login", () => {
|
|||||||
});
|
});
|
||||||
it("should redirect a user with password authentication to /password", () => {
|
it("should redirect a user with password authentication to /password", () => {
|
||||||
cy.visit("/loginname?loginName=john%40zitadel.com&submit=true");
|
cy.visit("/loginname?loginName=john%40zitadel.com&submit=true");
|
||||||
cy.location("pathname", { timeout: 10_000 }).should("eq", "/ui/v2/login/password");
|
cy.url({ timeout: 10_000 }).should("include", Cypress.config().baseUrl +"/password");
|
||||||
});
|
});
|
||||||
describe("with passkey prompt", () => {
|
describe("with passkey prompt", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -166,7 +166,7 @@ describe("login", () => {
|
|||||||
|
|
||||||
it("should redirect a user with passwordless authentication to /passkey", () => {
|
it("should redirect a user with passwordless authentication to /passkey", () => {
|
||||||
cy.visit("/loginname?loginName=john%40zitadel.com&submit=true");
|
cy.visit("/loginname?loginName=john%40zitadel.com&submit=true");
|
||||||
cy.location("pathname", { timeout: 10_000 }).should("eq", "/ui/v2/login/passkey");
|
cy.url({ timeout: 10_000 }).should("include", Cypress.config().baseUrl +"/passkey");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -68,6 +68,6 @@ describe("register", () => {
|
|||||||
cy.get('input[type="checkbox"][value="privacypolicy"]').check();
|
cy.get('input[type="checkbox"][value="privacypolicy"]').check();
|
||||||
cy.get('input[type="checkbox"][value="tos"]').check();
|
cy.get('input[type="checkbox"][value="tos"]').check();
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get('button[type="submit"]').click();
|
||||||
cy.location("pathname", { timeout: 10_000 }).should("eq", "/ui/v2/login/passkey/set");
|
cy.url({ timeout: 10_000 }).should("include", Cypress.config().baseUrl +"/passkey/set");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -2,13 +2,8 @@
|
|||||||
"name": "login-test-integration",
|
"name": "login-test-integration",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test:integration": "pnpm exec concurrently --names 'mock,test' --success command-test --kill-others 'pnpm:mock' 'env-cmd -f ./.env.integration start-server-and-test start http://localhost:3000 \"test:integration:run\"'",
|
"test:integration": "pnpm exec cypress",
|
||||||
"test:integration:watch:run": "pnpm exec concurrently --names 'mock,test' --kill-others 'pnpm:mock' 'env-cmd -f ./.env.integration start-server-and-test dev http://localhost:3000 \"pnpm nodemon -e js,jsx,ts,tsx,css,scss --ignore \\\"__test__/**\\\" --exec \\\"pnpm test:integration:run\\\"\"'",
|
"test:integration:setup": "cd ../.. && make login-test-integration-dev"
|
||||||
"test:integration:watch:open": "pnpm exec concurrently --names 'mock,test' --kill-others 'pnpm:mock' 'env-cmd -f ./.env.integration start-server-and-test dev http://localhost:3000 \"pnpm nodemon -e js,jsx,ts,tsx,css,scss --ignore \\\"__test__/**\\\" --exec \\\"pnpm test:integration:open\\\"\"'",
|
|
||||||
"test:integration:run": "pnpm exec cypress run --quiet",
|
|
||||||
"test:integration:open": "pnpm exec cypress open",
|
|
||||||
"mock": "make login-test-integration-build-dev",
|
|
||||||
"mock:stop": "docker compose down core-mock"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.14.1",
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"extends": ["//"],
|
"extends": ["//"],
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"test:integration": {
|
"test:integration:setup": {
|
||||||
"dependsOn": ["@zitadel/client#build"]
|
"interactive": true,
|
||||||
},
|
"cache": false,
|
||||||
"test:integration:run": {
|
"persistent": true,
|
||||||
"dependsOn": ["@zitadel/client#build"]
|
"with": ["@zitadel/login#dev"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,4 +5,4 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
|
|||||||
cd apps/login-test-acceptance && \
|
cd apps/login-test-acceptance && \
|
||||||
pnpm exec playwright install --with-deps chromium
|
pnpm exec playwright install --with-deps chromium
|
||||||
COPY ./apps/login-test-acceptance ./apps/login-test-acceptance
|
COPY ./apps/login-test-acceptance ./apps/login-test-acceptance
|
||||||
CMD ["bash", "-c", "cd apps/login-test-acceptance && pnpm test:acceptance"]
|
CMD ["bash", "-c", "cd apps/login-test-acceptance && pnpm test:acceptance test"]
|
||||||
|
@@ -5,7 +5,7 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
|
|||||||
FROM cypress/factory:5.10.0 AS login-test-integration
|
FROM cypress/factory:5.10.0 AS login-test-integration
|
||||||
WORKDIR /opt/app
|
WORKDIR /opt/app
|
||||||
COPY --from=login-test-integration-dependencies /build/apps/login-test-integration .
|
COPY --from=login-test-integration-dependencies /build/apps/login-test-integration .
|
||||||
COPY ./apps/login-test-integration .
|
|
||||||
RUN npm install cypress
|
RUN npm install cypress
|
||||||
RUN npx cypress install
|
RUN npx cypress install
|
||||||
|
COPY ./apps/login-test-integration .
|
||||||
CMD ["npx", "cypress", "run"]
|
CMD ["npx", "cypress", "run"]
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
*
|
*
|
||||||
!/apps/login-test-integration/*.json
|
|
||||||
!/apps/login-test-integration/*.ts
|
!/apps/login-test-integration
|
||||||
!/apps/login-test-integration/integration
|
|
||||||
!/apps/login-test-integration/fixtures
|
**/*.md
|
||||||
!/apps/login-test-integration/support
|
**/*.png
|
||||||
|
**/node_modules
|
||||||
|
**/.turbo
|
||||||
|
11
package.json
11
package.json
@@ -13,10 +13,10 @@
|
|||||||
"start": "pnpm exec turbo run start",
|
"start": "pnpm exec turbo run start",
|
||||||
"start:built": "pnpm exec turbo run start:built",
|
"start:built": "pnpm exec turbo run start:built",
|
||||||
"test:unit": "pnpm exec turbo run test:unit -- --passWithNoTests",
|
"test:unit": "pnpm exec turbo run test:unit -- --passWithNoTests",
|
||||||
"test:unit:standalone": "pnpm exec turbo run test:unit:standalone -- --passWithNoTests",
|
"test:integration:setup": "dotenv -e ./apps/login-test-integration/.env pnpm exec turbo run test:integration:setup",
|
||||||
"test:integration": "pnpm exec turbo run test:integration",
|
"test:integration": "cd apps/login-test-integration && dotenv -e ./.env pnpm test:integration",
|
||||||
"test:integration:run": "pnpm exec turbo run test:integration:run",
|
"test:acceptance:setup": "pnpm exec turbo run test:acceptance:setup",
|
||||||
"test:acceptance": "pnpm exec turbo run test:acceptance",
|
"test:acceptance": "cd apps/login-test-acceptance && dotenv -e ./env/.env pnpm test:acceptance",
|
||||||
"test:watch": "pnpm exec turbo run test:watch",
|
"test:watch": "pnpm exec turbo run test:watch",
|
||||||
"dev": "pnpm exec turbo run dev --no-cache --continue",
|
"dev": "pnpm exec turbo run dev --no-cache --continue",
|
||||||
"lint": "pnpm exec turbo run lint",
|
"lint": "pnpm exec turbo run lint",
|
||||||
@@ -36,11 +36,12 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@changesets/cli": "^2.29.2",
|
"@changesets/cli": "^2.29.2",
|
||||||
"@vitejs/plugin-react": "^4.4.1",
|
"@vitejs/plugin-react": "^4.4.1",
|
||||||
|
"@zitadel/eslint-config": "workspace:*",
|
||||||
"@zitadel/prettier-config": "workspace:*",
|
"@zitadel/prettier-config": "workspace:*",
|
||||||
"axios": "^1.8.4",
|
"axios": "^1.8.4",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
|
"dotenv-cli": "^8.0.0",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"@zitadel/eslint-config": "workspace:*",
|
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
"prettier-plugin-organize-imports": "^4.1.0",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
|
81
pnpm-lock.yaml
generated
81
pnpm-lock.yaml
generated
@@ -29,6 +29,9 @@ importers:
|
|||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.5.0
|
specifier: ^16.5.0
|
||||||
version: 16.5.0
|
version: 16.5.0
|
||||||
|
dotenv-cli:
|
||||||
|
specifier: ^8.0.0
|
||||||
|
version: 8.0.0
|
||||||
eslint:
|
eslint:
|
||||||
specifier: 8.57.1
|
specifier: 8.57.1
|
||||||
version: 8.57.1
|
version: 8.57.1
|
||||||
@@ -219,6 +222,9 @@ importers:
|
|||||||
'@playwright/test':
|
'@playwright/test':
|
||||||
specifier: ^1.52.0
|
specifier: ^1.52.0
|
||||||
version: 1.52.0
|
version: 1.52.0
|
||||||
|
gaxios:
|
||||||
|
specifier: ^7.1.0
|
||||||
|
version: 7.1.0
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.8.3
|
specifier: ^5.8.3
|
||||||
version: 5.8.3
|
version: 5.8.3
|
||||||
@@ -2063,6 +2069,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
|
resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
|
|
||||||
|
data-uri-to-buffer@4.0.1:
|
||||||
|
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
|
||||||
data-urls@5.0.0:
|
data-urls@5.0.0:
|
||||||
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
|
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -2186,6 +2196,14 @@ packages:
|
|||||||
dom-accessibility-api@0.6.3:
|
dom-accessibility-api@0.6.3:
|
||||||
resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
|
resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
|
||||||
|
|
||||||
|
dotenv-cli@8.0.0:
|
||||||
|
resolution: {integrity: sha512-aLqYbK7xKOiTMIRf1lDPbI+Y+Ip/wo5k3eyp6ePysVaSqbyxjyK3dK35BTxG+rmd7djf5q2UPs4noPNH+cj0Qw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
dotenv-expand@10.0.0:
|
||||||
|
resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
dotenv@16.0.3:
|
dotenv@16.0.3:
|
||||||
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
|
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -2539,6 +2557,10 @@ packages:
|
|||||||
picomatch:
|
picomatch:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
fetch-blob@3.2.0:
|
||||||
|
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||||
|
engines: {node: ^12.20 || >= 14.13}
|
||||||
|
|
||||||
fflate@0.8.2:
|
fflate@0.8.2:
|
||||||
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
|
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
|
||||||
|
|
||||||
@@ -2596,6 +2618,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
|
resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
formdata-polyfill@4.0.10:
|
||||||
|
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||||
|
engines: {node: '>=12.20.0'}
|
||||||
|
|
||||||
fraction.js@4.3.7:
|
fraction.js@4.3.7:
|
||||||
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
||||||
|
|
||||||
@@ -2646,6 +2672,10 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
deprecated: This package is no longer supported.
|
deprecated: This package is no longer supported.
|
||||||
|
|
||||||
|
gaxios@7.1.0:
|
||||||
|
resolution: {integrity: sha512-y1Q0MX1Ba6eg67Zz92kW0MHHhdtWksYckQy1KJsI6P4UlDQ8cvdvpLEPslD/k7vFkdPppMESFGTvk7XpSiKj8g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
gensync@1.0.0-beta.2:
|
gensync@1.0.0-beta.2:
|
||||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -3415,6 +3445,11 @@ packages:
|
|||||||
node-addon-api@7.1.1:
|
node-addon-api@7.1.1:
|
||||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||||
|
|
||||||
|
node-domexception@1.0.0:
|
||||||
|
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||||
|
engines: {node: '>=10.5.0'}
|
||||||
|
deprecated: Use your platform's native DOMException instead
|
||||||
|
|
||||||
node-fetch@2.7.0:
|
node-fetch@2.7.0:
|
||||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||||
engines: {node: 4.x || >=6.0.0}
|
engines: {node: 4.x || >=6.0.0}
|
||||||
@@ -3424,6 +3459,10 @@ packages:
|
|||||||
encoding:
|
encoding:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
node-fetch@3.3.2:
|
||||||
|
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
|
||||||
node-releases@2.0.19:
|
node-releases@2.0.19:
|
||||||
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
|
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
|
||||||
|
|
||||||
@@ -4642,6 +4681,10 @@ packages:
|
|||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
web-streams-polyfill@3.3.3:
|
||||||
|
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
webidl-conversions@3.0.1:
|
webidl-conversions@3.0.1:
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
|
||||||
@@ -6560,6 +6603,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
assert-plus: 1.0.0
|
assert-plus: 1.0.0
|
||||||
|
|
||||||
|
data-uri-to-buffer@4.0.1: {}
|
||||||
|
|
||||||
data-urls@5.0.0:
|
data-urls@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-mimetype: 4.0.0
|
whatwg-mimetype: 4.0.0
|
||||||
@@ -6683,6 +6728,15 @@ snapshots:
|
|||||||
|
|
||||||
dom-accessibility-api@0.6.3: {}
|
dom-accessibility-api@0.6.3: {}
|
||||||
|
|
||||||
|
dotenv-cli@8.0.0:
|
||||||
|
dependencies:
|
||||||
|
cross-spawn: 7.0.6
|
||||||
|
dotenv: 16.5.0
|
||||||
|
dotenv-expand: 10.0.0
|
||||||
|
minimist: 1.2.8
|
||||||
|
|
||||||
|
dotenv-expand@10.0.0: {}
|
||||||
|
|
||||||
dotenv@16.0.3: {}
|
dotenv@16.0.3: {}
|
||||||
|
|
||||||
dotenv@16.5.0: {}
|
dotenv@16.5.0: {}
|
||||||
@@ -7232,6 +7286,11 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
picomatch: 4.0.2
|
picomatch: 4.0.2
|
||||||
|
|
||||||
|
fetch-blob@3.2.0:
|
||||||
|
dependencies:
|
||||||
|
node-domexception: 1.0.0
|
||||||
|
web-streams-polyfill: 3.3.3
|
||||||
|
|
||||||
fflate@0.8.2: {}
|
fflate@0.8.2: {}
|
||||||
|
|
||||||
figures@3.2.0:
|
figures@3.2.0:
|
||||||
@@ -7291,6 +7350,10 @@ snapshots:
|
|||||||
es-set-tostringtag: 2.1.0
|
es-set-tostringtag: 2.1.0
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
|
|
||||||
|
formdata-polyfill@4.0.10:
|
||||||
|
dependencies:
|
||||||
|
fetch-blob: 3.2.0
|
||||||
|
|
||||||
fraction.js@4.3.7: {}
|
fraction.js@4.3.7: {}
|
||||||
|
|
||||||
from@0.1.7: {}
|
from@0.1.7: {}
|
||||||
@@ -7349,6 +7412,14 @@ snapshots:
|
|||||||
strip-ansi: 6.0.1
|
strip-ansi: 6.0.1
|
||||||
wide-align: 1.1.5
|
wide-align: 1.1.5
|
||||||
|
|
||||||
|
gaxios@7.1.0:
|
||||||
|
dependencies:
|
||||||
|
extend: 3.0.2
|
||||||
|
https-proxy-agent: 7.0.6
|
||||||
|
node-fetch: 3.3.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
gensync@1.0.0-beta.2: {}
|
gensync@1.0.0-beta.2: {}
|
||||||
|
|
||||||
get-caller-file@2.0.5: {}
|
get-caller-file@2.0.5: {}
|
||||||
@@ -8120,10 +8191,18 @@ snapshots:
|
|||||||
node-addon-api@7.1.1:
|
node-addon-api@7.1.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
node-domexception@1.0.0: {}
|
||||||
|
|
||||||
node-fetch@2.7.0:
|
node-fetch@2.7.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url: 5.0.0
|
whatwg-url: 5.0.0
|
||||||
|
|
||||||
|
node-fetch@3.3.2:
|
||||||
|
dependencies:
|
||||||
|
data-uri-to-buffer: 4.0.1
|
||||||
|
fetch-blob: 3.2.0
|
||||||
|
formdata-polyfill: 4.0.10
|
||||||
|
|
||||||
node-releases@2.0.19: {}
|
node-releases@2.0.19: {}
|
||||||
|
|
||||||
nodemon@3.1.9:
|
nodemon@3.1.9:
|
||||||
@@ -9356,6 +9435,8 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
|
|
||||||
|
web-streams-polyfill@3.3.3: {}
|
||||||
|
|
||||||
webidl-conversions@3.0.1: {}
|
webidl-conversions@3.0.1: {}
|
||||||
|
|
||||||
webidl-conversions@4.0.2: {}
|
webidl-conversions@4.0.2: {}
|
||||||
|
@@ -27,9 +27,8 @@
|
|||||||
"start:built": {},
|
"start:built": {},
|
||||||
"test:unit": {},
|
"test:unit": {},
|
||||||
"test:unit:standalone": {},
|
"test:unit:standalone": {},
|
||||||
"test:integration": {},
|
"test:integration:setup": {},
|
||||||
"test:integration:run": {},
|
"test:acceptance:setup": {},
|
||||||
"test:acceptance": {},
|
|
||||||
"test:watch": {
|
"test:watch": {
|
||||||
"persistent": true
|
"persistent": true
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user