@blamejs/blamejs-shop 0.5.12 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +632 -416
- package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +21 -10
- package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +30 -9
- package/lib/vendor/blamejs/.github/dependabot.yml +8 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/ci.yml +21 -21
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +7 -7
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/.gitignore +2 -0
- package/lib/vendor/blamejs/CHANGELOG.md +122 -0
- package/lib/vendor/blamejs/NOTICE +26 -1
- package/lib/vendor/blamejs/SECURITY.md +1 -1
- package/lib/vendor/blamejs/api-snapshot.json +207 -5
- package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +29 -0
- package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +31 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +109 -0
- package/lib/vendor/blamejs/examples/wiki/nginx.conf +106 -0
- package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +92 -0
- package/lib/vendor/blamejs/fuzz/asn1-der.fuzz.js +28 -0
- package/lib/vendor/blamejs/fuzz/cms-codec.fuzz.js +17 -0
- package/lib/vendor/blamejs/fuzz/link-header.fuzz.js +18 -0
- package/lib/vendor/blamejs/index.js +6 -0
- package/lib/vendor/blamejs/lib/a2a.js +4 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +18 -2
- package/lib/vendor/blamejs/lib/agent-saga.js +36 -8
- package/lib/vendor/blamejs/lib/agent-snapshot.js +63 -6
- package/lib/vendor/blamejs/lib/app-shutdown.js +23 -20
- package/lib/vendor/blamejs/lib/archive-read.js +8 -0
- package/lib/vendor/blamejs/lib/archive-tar.js +12 -2
- package/lib/vendor/blamejs/lib/arg-parser.js +49 -5
- package/lib/vendor/blamejs/lib/asn1-der.js +12 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +47 -1
- package/lib/vendor/blamejs/lib/audit.js +71 -8
- package/lib/vendor/blamejs/lib/auth/ciba.js +14 -2
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +58 -47
- package/lib/vendor/blamejs/lib/auth/jwt.js +34 -0
- package/lib/vendor/blamejs/lib/auth/oauth.js +90 -13
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +20 -17
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +214 -23
- package/lib/vendor/blamejs/lib/auth/saml.js +38 -20
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +10 -4
- package/lib/vendor/blamejs/lib/backup/bundle.js +13 -3
- package/lib/vendor/blamejs/lib/backup/index.js +16 -5
- package/lib/vendor/blamejs/lib/backup/manifest.js +18 -5
- package/lib/vendor/blamejs/lib/base32.js +21 -0
- package/lib/vendor/blamejs/lib/break-glass.js +19 -10
- package/lib/vendor/blamejs/lib/bundler.js +5 -1
- package/lib/vendor/blamejs/lib/cache-status.js +3 -7
- package/lib/vendor/blamejs/lib/calendar.js +21 -1
- package/lib/vendor/blamejs/lib/cbor.js +12 -1
- package/lib/vendor/blamejs/lib/cert.js +29 -11
- package/lib/vendor/blamejs/lib/cli.js +94 -24
- package/lib/vendor/blamejs/lib/cloud-events.js +2 -2
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +13 -3
- package/lib/vendor/blamejs/lib/codepoint-class.js +85 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +8 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +19 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act.js +6 -8
- package/lib/vendor/blamejs/lib/content-credentials.js +1 -1
- package/lib/vendor/blamejs/lib/content-digest.js +2 -1
- package/lib/vendor/blamejs/lib/cookies.js +13 -3
- package/lib/vendor/blamejs/lib/cose.js +31 -16
- package/lib/vendor/blamejs/lib/crypto-field.js +32 -0
- package/lib/vendor/blamejs/lib/crypto-oprf.js +11 -2
- package/lib/vendor/blamejs/lib/crypto-xwing.js +22 -4
- package/lib/vendor/blamejs/lib/crypto.js +314 -19
- package/lib/vendor/blamejs/lib/csp.js +11 -3
- package/lib/vendor/blamejs/lib/daemon.js +270 -24
- package/lib/vendor/blamejs/lib/dark-patterns.js +5 -2
- package/lib/vendor/blamejs/lib/db-declare-view.js +2 -2
- package/lib/vendor/blamejs/lib/db-query.js +34 -7
- package/lib/vendor/blamejs/lib/db.js +18 -6
- package/lib/vendor/blamejs/lib/dbsc.js +17 -0
- package/lib/vendor/blamejs/lib/dev.js +21 -2
- package/lib/vendor/blamejs/lib/dsr.js +157 -102
- package/lib/vendor/blamejs/lib/external-db.js +41 -2
- package/lib/vendor/blamejs/lib/file-upload.js +25 -7
- package/lib/vendor/blamejs/lib/flag-providers.js +27 -15
- package/lib/vendor/blamejs/lib/forms.js +113 -27
- package/lib/vendor/blamejs/lib/gate-contract.js +23 -10
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +8 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +1 -1
- package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
- package/lib/vendor/blamejs/lib/guard-cidr.js +27 -1
- package/lib/vendor/blamejs/lib/guard-graphql.js +30 -11
- package/lib/vendor/blamejs/lib/guard-html.js +26 -53
- package/lib/vendor/blamejs/lib/guard-image.js +24 -5
- package/lib/vendor/blamejs/lib/guard-imap-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-jmap.js +19 -3
- package/lib/vendor/blamejs/lib/guard-jwt.js +18 -2
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -2
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-markdown.js +11 -24
- package/lib/vendor/blamejs/lib/guard-pdf.js +18 -2
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-regex.js +8 -5
- package/lib/vendor/blamejs/lib/guard-svg.js +31 -32
- package/lib/vendor/blamejs/lib/guard-time.js +1 -1
- package/lib/vendor/blamejs/lib/guard-uuid.js +2 -2
- package/lib/vendor/blamejs/lib/html-balance.js +3 -3
- package/lib/vendor/blamejs/lib/http-client-cache.js +82 -4
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +16 -1
- package/lib/vendor/blamejs/lib/http-client.js +39 -7
- package/lib/vendor/blamejs/lib/http-message-signature.js +35 -1
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +31 -6
- package/lib/vendor/blamejs/lib/i18n.js +35 -21
- package/lib/vendor/blamejs/lib/incident-report.js +8 -1
- package/lib/vendor/blamejs/lib/ip-utils.js +11 -1
- package/lib/vendor/blamejs/lib/json-path.js +23 -1
- package/lib/vendor/blamejs/lib/jwk.js +20 -3
- package/lib/vendor/blamejs/lib/link-header.js +2 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream.js +8 -3
- package/lib/vendor/blamejs/lib/mail-auth.js +123 -22
- package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
- package/lib/vendor/blamejs/lib/mail-bounce.js +8 -0
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +46 -10
- package/lib/vendor/blamejs/lib/mail-dav.js +22 -3
- package/lib/vendor/blamejs/lib/mail-dkim.js +96 -30
- package/lib/vendor/blamejs/lib/mail-helo.js +6 -1
- package/lib/vendor/blamejs/lib/mail-scan.js +23 -16
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +34 -8
- package/lib/vendor/blamejs/lib/mail-server-imap.js +3 -4
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +7 -11
- package/lib/vendor/blamejs/lib/mail-server-mx.js +93 -7
- package/lib/vendor/blamejs/lib/mail-server-registry.js +8 -2
- package/lib/vendor/blamejs/lib/mail.js +15 -5
- package/lib/vendor/blamejs/lib/metrics.js +268 -63
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +7 -1
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +19 -4
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +5 -9
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +40 -8
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +24 -10
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -5
- package/lib/vendor/blamejs/lib/middleware/index.js +6 -1
- package/lib/vendor/blamejs/lib/middleware/nel.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +12 -1
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +6 -2
- package/lib/vendor/blamejs/lib/mtls-ca.js +18 -2
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +11 -1
- package/lib/vendor/blamejs/lib/network-dns.js +21 -2
- package/lib/vendor/blamejs/lib/network-tls.js +69 -7
- package/lib/vendor/blamejs/lib/nonce-store.js +9 -1
- package/lib/vendor/blamejs/lib/object-store/gcs.js +7 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +31 -0
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +1 -1
- package/lib/vendor/blamejs/lib/outbox.js +1 -1
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +9 -1
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +10 -1
- package/lib/vendor/blamejs/lib/permissions.js +6 -3
- package/lib/vendor/blamejs/lib/pid-probe.js +55 -0
- package/lib/vendor/blamejs/lib/pqc-agent.js +8 -1
- package/lib/vendor/blamejs/lib/pqc-software.js +3 -3
- package/lib/vendor/blamejs/lib/pubsub.js +20 -9
- package/lib/vendor/blamejs/lib/queue-local.js +8 -1
- package/lib/vendor/blamejs/lib/queue-redis.js +5 -0
- package/lib/vendor/blamejs/lib/redact.js +54 -0
- package/lib/vendor/blamejs/lib/restore-bundle.js +10 -2
- package/lib/vendor/blamejs/lib/restore.js +19 -0
- package/lib/vendor/blamejs/lib/retention.js +12 -4
- package/lib/vendor/blamejs/lib/router.js +60 -6
- package/lib/vendor/blamejs/lib/safe-buffer.js +69 -0
- package/lib/vendor/blamejs/lib/safe-decompress.js +8 -2
- package/lib/vendor/blamejs/lib/safe-dns.js +25 -20
- package/lib/vendor/blamejs/lib/safe-ical.js +7 -3
- package/lib/vendor/blamejs/lib/safe-mime.js +12 -1
- package/lib/vendor/blamejs/lib/safe-object.js +80 -0
- package/lib/vendor/blamejs/lib/safe-path.js +52 -4
- package/lib/vendor/blamejs/lib/safe-redirect.js +10 -2
- package/lib/vendor/blamejs/lib/safe-schema.js +4 -1
- package/lib/vendor/blamejs/lib/safe-sieve.js +33 -1
- package/lib/vendor/blamejs/lib/safe-sql.js +88 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +7 -3
- package/lib/vendor/blamejs/lib/scheduler.js +8 -1
- package/lib/vendor/blamejs/lib/security-assert.js +6 -6
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +74 -27
- package/lib/vendor/blamejs/lib/self-update.js +517 -87
- package/lib/vendor/blamejs/lib/server-timing.js +2 -4
- package/lib/vendor/blamejs/lib/session.js +34 -12
- package/lib/vendor/blamejs/lib/sql.js +15 -1
- package/lib/vendor/blamejs/lib/ssrf-guard.js +52 -0
- package/lib/vendor/blamejs/lib/static.js +55 -13
- package/lib/vendor/blamejs/lib/storage.js +8 -2
- package/lib/vendor/blamejs/lib/subject.js +6 -1
- package/lib/vendor/blamejs/lib/tenant-quota.js +52 -13
- package/lib/vendor/blamejs/lib/tls-exporter.js +3 -3
- package/lib/vendor/blamejs/lib/tsa.js +34 -13
- package/lib/vendor/blamejs/lib/uri-template.js +8 -1
- package/lib/vendor/blamejs/lib/vault/index.js +13 -0
- package/lib/vendor/blamejs/lib/vc.js +44 -5
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +26 -26
- package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +792 -1
- package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +3443 -1
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +2737 -1
- package/lib/vendor/blamejs/lib/vendor/pki.cjs +39550 -13
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +55 -53
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5551 -5554
- package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +22459 -32
- package/lib/vendor/blamejs/lib/watcher.js +89 -17
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +25 -3
- package/lib/vendor/blamejs/lib/webhook.js +12 -2
- package/lib/vendor/blamejs/lib/ws-client.js +32 -1
- package/lib/vendor/blamejs/lib/xml-c14n.js +29 -1
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +1 -1
- package/lib/vendor/blamejs/package-lock.json +2 -2
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.x.json +1462 -0
- package/lib/vendor/blamejs/release-notes/v0.17.0.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.1.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.10.json +30 -0
- package/lib/vendor/blamejs/release-notes/v0.17.11.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.12.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.13.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.17.14.json +40 -0
- package/lib/vendor/blamejs/release-notes/v0.17.15.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.16.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.17.17.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.18.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.19.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.2.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.20.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.21.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.22.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.3.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.4.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.5.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.6.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.7.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.8.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.9.json +26 -0
- package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +4 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +293 -9
- package/lib/vendor/blamejs/socket.yml +94 -0
- package/lib/vendor/blamejs/test/00-primitives.js +605 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +12 -9
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +90 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +122 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +30 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +29 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-mysql.test.js +479 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-pg.test.js +487 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +313 -5
- package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +80 -0
- package/lib/vendor/blamejs/test/integration/openid-federation-chain.test.js +275 -0
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +1649 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +891 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +128 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-adapters.test.js +271 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +628 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-read.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-wrap.test.js +227 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +141 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-tools.test.js +624 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit.test.js +769 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth.test.js +1697 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oid4vp.test.js +495 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-password.test.js +500 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml.test.js +1809 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +243 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index.test.js +2226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/base32.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +693 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +603 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +557 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/case-insensitive-security-token-guard.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cbor.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +843 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba-authreqid-binding.test.js +77 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli.test.js +2790 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster-lease-renewal-ttl.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster.test.js +1032 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +739 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +8 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +194 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-lifecycle-proto-key.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +254 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +613 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/cookies.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +407 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-adversarial.test.js +352 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-spki-pin.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-xwing.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto.test.js +345 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +459 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query.test.js +1019 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db.test.js +1621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +462 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-verify.test.js +562 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +901 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +249 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +561 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db.test.js +1214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +46 -20
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +147 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +907 -38
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload.test.js +1135 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/forms.test.js +747 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract.test.js +1855 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-auth.test.js +117 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-cidr.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-domain.test.js +422 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +424 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-gate-disposition-coverage.test.js → guard-gate-disposition.test.js} +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-graphql.test.js +340 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +519 -45
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jsonpath.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jwt.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +16 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mime.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-oauth.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-regex.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-shell.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-sql-coverage.test.js → guard-sql.test.js} +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-template.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-time.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-uuid.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache-authorization.test.js +347 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +908 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client.test.js +2874 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +643 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +1153 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +494 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jsdoc-example-execution.test.js +177 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-path.test.js +103 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-schema.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwk.test.js +41 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +276 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream.test.js +241 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +441 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +2136 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +759 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +811 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +401 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +751 -8
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +864 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +830 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +635 -86
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +686 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +778 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +1438 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +449 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +773 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +623 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +13 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +877 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +326 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +242 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +1596 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +440 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics.test.js +1230 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-assetlinks.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-body-parser.test.js +1169 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-bot-disclose.test.js +123 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-cookies.test.js +108 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-gpc.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-headers.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-host-allowlist.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-require-content-type.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{middleware-tus-upload-coverage.test.js → middleware-tus-upload.test.js} +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-web-app-manifest.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-crl-fingerprint-only.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +1727 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/network-nts.test.js +699 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy.test.js +1181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +1876 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-enforce-replay-nonboolean.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ntp-check.test.js +122 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +378 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oid4vci.test.js +1016 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/openid-federation.test.js +946 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/own-property-membership-guards.test.js +109 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-toml.test.js +543 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{parsers-safe-yaml-coverage.test.js → parsers-safe-yaml.test.js} +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +649 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +295 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact.test.js +1031 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-bound-key.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/restore-empty-manifest-wipe.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention.test.js +600 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-host-path-injection.test.js +231 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router.test.js +1277 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +245 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ini.test.js +376 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-json.test.js +175 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +518 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-object.test.js +62 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-schema.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-sql.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +26 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler.test.js +157 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +396 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +930 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +755 -82
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +1053 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +200 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-strict-binding-missing-failclosed.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +636 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-offset-without-limit.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +1190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +850 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage.test.js +621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/subject.test.js +47 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/template.test.js +582 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +587 -25
- package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +11 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tsa.test.js +524 -63
- package/lib/vendor/blamejs/test/layer-0-primitives/uri-template.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-rotate.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +215 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +550 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +388 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +262 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket.test.js +1294 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +629 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/xml-c14n-attr-normalization.test.js +164 -0
- package/lib/vendor/blamejs/test/smoke.js +2 -0
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.0.json +0 -44
- package/lib/vendor/blamejs/release-notes/v0.16.1.json +0 -36
- package/lib/vendor/blamejs/release-notes/v0.16.2.json +0 -71
- package/lib/vendor/blamejs/release-notes/v0.16.3.json +0 -71
- package/lib/vendor/blamejs/test/layer-0-primitives/acme-coverage.test.js +0 -441
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth-coverage.test.js +0 -482
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml-coverage.test.js +0 -671
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index-coverage.test.js +0 -690
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-coverage.test.js +0 -238
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +0 -319
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain-coverage.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth-coverage.test.js +0 -437
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +0 -443
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +0 -395
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy-coverage.test.js +0 -565
- package/lib/vendor/blamejs/test/layer-0-primitives/router-coverage.test.js +0 -592
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-coverage.test.js +0 -422
- package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +0 -573
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* b.forms — canonical coverage of the form primitives: CSRF token
|
|
6
|
+
* issue/verify, attribute escaping, spec-driven `<form>` rendering, and
|
|
7
|
+
* shared-spec server-side validation.
|
|
8
|
+
*
|
|
9
|
+
* Drives the real b.forms.* consumer surface (never requires lib/forms
|
|
10
|
+
* directly): generateCsrfToken / verifyCsrfToken / escapeAttribute /
|
|
11
|
+
* render / validate. Covers every field-type render branch, the CSRF
|
|
12
|
+
* hidden-input method gating, the config-time throws (missing action,
|
|
13
|
+
* non-array fields, unnamed field, unsupported type, string pattern,
|
|
14
|
+
* ReDoS pattern), and validate's coercion + required + numeric bounds +
|
|
15
|
+
* email/url + length + pattern + enum paths across the returned
|
|
16
|
+
* { valid, errors, values } shape.
|
|
17
|
+
*
|
|
18
|
+
* Run standalone: `node test/layer-0-primitives/forms.test.js`
|
|
19
|
+
* Or via smoke: `node test/smoke.js`
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
var helpers = require("../helpers");
|
|
23
|
+
var check = helpers.check;
|
|
24
|
+
var b = helpers.b;
|
|
25
|
+
|
|
26
|
+
// Returns true iff fn() throws. Keeps the config-time-throw assertions
|
|
27
|
+
// terse without a bespoke harness (audit-existing applies to tests too).
|
|
28
|
+
function _throws(fn) {
|
|
29
|
+
try { fn(); return false; } catch (_e) { return true; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ============================================================
|
|
33
|
+
// generateCsrfToken
|
|
34
|
+
// ============================================================
|
|
35
|
+
|
|
36
|
+
function testGenerateCsrfToken() {
|
|
37
|
+
var t1 = b.forms.generateCsrfToken();
|
|
38
|
+
var t2 = b.forms.generateCsrfToken();
|
|
39
|
+
check("generateCsrfToken: 64 hex chars", /^[0-9a-f]{64}$/.test(t1));
|
|
40
|
+
check("generateCsrfToken: exactly 64 chars long", t1.length === 64);
|
|
41
|
+
check("generateCsrfToken: distinct per call", t1 !== t2);
|
|
42
|
+
check("forms.CSRF_TOKEN_BYTES is 32", b.forms.CSRF_TOKEN_BYTES === 32);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ============================================================
|
|
46
|
+
// verifyCsrfToken
|
|
47
|
+
// ============================================================
|
|
48
|
+
|
|
49
|
+
function testVerifyCsrfToken() {
|
|
50
|
+
var t = b.forms.generateCsrfToken();
|
|
51
|
+
check("verify: identical real tokens → true", b.forms.verifyCsrfToken(t, t) === true);
|
|
52
|
+
check("verify: byte-equal non-empty strings → true", b.forms.verifyCsrfToken("abc", "abc") === true);
|
|
53
|
+
check("verify: distinct real tokens → false", b.forms.verifyCsrfToken(t, b.forms.generateCsrfToken()) === false);
|
|
54
|
+
check("verify: same-length mismatch → false", b.forms.verifyCsrfToken("abc", "abd") === false);
|
|
55
|
+
check("verify: length mismatch → false", b.forms.verifyCsrfToken("abc", "abcd") === false);
|
|
56
|
+
check("verify: empty submitted vs empty expected → false", b.forms.verifyCsrfToken("", "") === false);
|
|
57
|
+
check("verify: empty submitted vs non-empty → false", b.forms.verifyCsrfToken("", "abc") === false);
|
|
58
|
+
check("verify: non-string submitted → false", b.forms.verifyCsrfToken(123, "abc") === false);
|
|
59
|
+
check("verify: non-string expected → false", b.forms.verifyCsrfToken("abc", 123) === false);
|
|
60
|
+
check("verify: both non-string → false", b.forms.verifyCsrfToken(null, null) === false);
|
|
61
|
+
// Documented contract: never throws, whatever the input type.
|
|
62
|
+
var threw = _throws(function () {
|
|
63
|
+
b.forms.verifyCsrfToken(undefined, undefined);
|
|
64
|
+
b.forms.verifyCsrfToken({}, []);
|
|
65
|
+
b.forms.verifyCsrfToken(NaN, "x");
|
|
66
|
+
});
|
|
67
|
+
check("verify: never throws on adversarial inputs", threw === false);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ============================================================
|
|
71
|
+
// escapeAttribute
|
|
72
|
+
// ============================================================
|
|
73
|
+
|
|
74
|
+
function testEscapeAttribute() {
|
|
75
|
+
check("escape: null → empty string", b.forms.escapeAttribute(null) === "");
|
|
76
|
+
check("escape: undefined → empty string", b.forms.escapeAttribute(undefined) === "");
|
|
77
|
+
check("escape: every special char escaped",
|
|
78
|
+
b.forms.escapeAttribute("&<>\"'`=") === "&<>"'`=");
|
|
79
|
+
check("escape: plain string unchanged", b.forms.escapeAttribute("plain-value") === "plain-value");
|
|
80
|
+
check("escape: number coerced via String()", b.forms.escapeAttribute(42) === "42");
|
|
81
|
+
check("escape: falsy zero coerced (not treated as null)", b.forms.escapeAttribute(0) === "0");
|
|
82
|
+
check("escape: boolean false coerced", b.forms.escapeAttribute(false) === "false");
|
|
83
|
+
check("escape: boolean true coerced", b.forms.escapeAttribute(true) === "true");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ============================================================
|
|
87
|
+
// render — config-time throws
|
|
88
|
+
// ============================================================
|
|
89
|
+
|
|
90
|
+
function testRenderThrows() {
|
|
91
|
+
check("render: no spec throws", _throws(function () { return b.forms.render(); }));
|
|
92
|
+
check("render: null spec throws", _throws(function () { return b.forms.render(null); }));
|
|
93
|
+
check("render: non-string action throws", _throws(function () { return b.forms.render({ action: 123, fields: [] }); }));
|
|
94
|
+
check("render: empty action throws", _throws(function () { return b.forms.render({ action: "", fields: [] }); }));
|
|
95
|
+
check("render: non-array fields throws", _throws(function () { return b.forms.render({ action: "/x", fields: "nope" }); }));
|
|
96
|
+
check("render: field without name throws", _throws(function () { return b.forms.render({ action: "/x", fields: [{ type: "text" }] }); }));
|
|
97
|
+
check("render: null field throws", _throws(function () { return b.forms.render({ action: "/x", fields: [null] }); }));
|
|
98
|
+
check("render: non-string field name throws", _throws(function () { return b.forms.render({ action: "/x", fields: [{ name: 123 }] }); }));
|
|
99
|
+
check("render: unsupported field type throws", _throws(function () { return b.forms.render({ action: "/x", fields: [{ type: "frobnicate", name: "f" }] }); }));
|
|
100
|
+
check("render: string pattern throws (must be pre-compiled RegExp)",
|
|
101
|
+
_throws(function () { return b.forms.render({ action: "/x", fields: [{ type: "text", name: "p", pattern: "^abc$" }] }); }));
|
|
102
|
+
check("render: catastrophic-backtracking pattern refused at config time",
|
|
103
|
+
_throws(function () { return b.forms.render({ action: "/x", fields: [{ type: "text", name: "p", pattern: /(a+)+/ }] }); }));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ============================================================
|
|
107
|
+
// render — form-level attributes, method + CSRF gating
|
|
108
|
+
// ============================================================
|
|
109
|
+
|
|
110
|
+
function testRenderFormAttrs() {
|
|
111
|
+
var html = b.forms.render({
|
|
112
|
+
action: "/submit",
|
|
113
|
+
method: "post", // lower-case → upper-cased
|
|
114
|
+
csrfToken: "tok<en>",
|
|
115
|
+
id: "form-id",
|
|
116
|
+
className: "form-class",
|
|
117
|
+
enctype: "multipart/form-data",
|
|
118
|
+
autocomplete: "off",
|
|
119
|
+
target: "_blank",
|
|
120
|
+
fields: [{ type: "text", name: "q" }],
|
|
121
|
+
});
|
|
122
|
+
check("render: opens with <form", html.indexOf("<form ") === 0);
|
|
123
|
+
check("render: method upper-cased", html.indexOf('method="POST"') !== -1);
|
|
124
|
+
check("render: action attr", html.indexOf('action="/submit"') !== -1);
|
|
125
|
+
check("render: id attr", html.indexOf('id="form-id"') !== -1);
|
|
126
|
+
check("render: class attr", html.indexOf('class="form-class"') !== -1);
|
|
127
|
+
check("render: enctype attr", html.indexOf('enctype="multipart/form-data"') !== -1);
|
|
128
|
+
check("render: autocomplete attr", html.indexOf('autocomplete="off"') !== -1);
|
|
129
|
+
check("render: target attr", html.indexOf('target="_blank"') !== -1);
|
|
130
|
+
check("render: csrf hidden input on POST", html.indexOf('<input type="hidden" name="_csrf"') !== -1);
|
|
131
|
+
check("render: csrf token value escaped in attribute", html.indexOf('value="tok<en>"') !== -1);
|
|
132
|
+
check("render: closes with </form>", html.indexOf("</form>") === html.length - 7);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function testRenderCsrfGating() {
|
|
136
|
+
var post = b.forms.render({ action: "/a", method: "POST", csrfToken: "t", fields: [] });
|
|
137
|
+
check("render: POST with token renders csrf input", post.indexOf('name="_csrf"') !== -1);
|
|
138
|
+
|
|
139
|
+
var get = b.forms.render({ action: "/a", method: "GET", csrfToken: "t", fields: [] });
|
|
140
|
+
check("render: GET omits csrf even when token supplied", get.indexOf("_csrf") === -1);
|
|
141
|
+
|
|
142
|
+
var head = b.forms.render({ action: "/a", method: "HEAD", csrfToken: "t", fields: [] });
|
|
143
|
+
check("render: HEAD omits csrf even when token supplied", head.indexOf("_csrf") === -1);
|
|
144
|
+
|
|
145
|
+
var noToken = b.forms.render({ action: "/a", method: "POST", fields: [] });
|
|
146
|
+
check("render: POST without token has no csrf input", noToken.indexOf("_csrf") === -1);
|
|
147
|
+
|
|
148
|
+
var custom = b.forms.render({ action: "/a", method: "POST", csrfToken: "t", csrfFieldName: "xsrf", fields: [] });
|
|
149
|
+
check("render: custom csrfFieldName honored",
|
|
150
|
+
custom.indexOf('name="xsrf"') !== -1 && custom.indexOf('name="_csrf"') === -1);
|
|
151
|
+
|
|
152
|
+
var deflt = b.forms.render({ action: "/a", fields: [] }); // method defaults to POST
|
|
153
|
+
check("render: method defaults to POST", deflt.indexOf('method="POST"') !== -1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ============================================================
|
|
157
|
+
// render — input widget attributes
|
|
158
|
+
// ============================================================
|
|
159
|
+
|
|
160
|
+
function testRenderInputAttrs() {
|
|
161
|
+
var html = b.forms.render({
|
|
162
|
+
action: "/x",
|
|
163
|
+
fields: [{
|
|
164
|
+
type: "text", name: "kitchen",
|
|
165
|
+
value: "v", placeholder: "p", required: true, readonly: true,
|
|
166
|
+
disabled: true, checked: true, autocomplete: "username",
|
|
167
|
+
pattern: /^[a-z]+$/, min: 0, max: 10, step: 2,
|
|
168
|
+
minlength: 1, maxlength: 5, id: "kid", className: "kc",
|
|
169
|
+
}],
|
|
170
|
+
});
|
|
171
|
+
check("input: type attr", html.indexOf('type="text"') !== -1);
|
|
172
|
+
check("input: name attr", html.indexOf('name="kitchen"') !== -1);
|
|
173
|
+
check("input: value attr", html.indexOf('value="v"') !== -1);
|
|
174
|
+
check("input: placeholder attr", html.indexOf('placeholder="p"') !== -1);
|
|
175
|
+
check("input: required flag", html.indexOf(" required") !== -1);
|
|
176
|
+
check("input: readonly flag", html.indexOf(" readonly") !== -1);
|
|
177
|
+
check("input: disabled flag", html.indexOf(" disabled") !== -1);
|
|
178
|
+
check("input: checked flag", html.indexOf(" checked") !== -1);
|
|
179
|
+
check("input: autocomplete attr", html.indexOf('autocomplete="username"') !== -1);
|
|
180
|
+
check("input: pattern source rendered from RegExp", html.indexOf('pattern="^[a-z]+$"') !== -1);
|
|
181
|
+
check("input: min attr (zero emitted)", html.indexOf('min="0"') !== -1);
|
|
182
|
+
check("input: max attr", html.indexOf('max="10"') !== -1);
|
|
183
|
+
check("input: step attr", html.indexOf('step="2"') !== -1);
|
|
184
|
+
check("input: minlength attr", html.indexOf('minlength="1"') !== -1);
|
|
185
|
+
check("input: maxlength attr", html.indexOf('maxlength="5"') !== -1);
|
|
186
|
+
check("input: id attr", html.indexOf('id="kid"') !== -1);
|
|
187
|
+
check("input: class attr", html.indexOf('class="kc"') !== -1);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function testRenderInputValuePresence() {
|
|
191
|
+
var withEmpty = b.forms.render({ action: "/x", fields: [{ type: "text", name: "e", value: "" }] });
|
|
192
|
+
check("input: empty-string value still emits value attr", withEmpty.indexOf('value=""') !== -1);
|
|
193
|
+
|
|
194
|
+
var withNull = b.forms.render({ action: "/x", fields: [{ type: "text", name: "n", value: null }] });
|
|
195
|
+
check("input: null value omits value attr", withNull.indexOf("value=") === -1);
|
|
196
|
+
|
|
197
|
+
var noValue = b.forms.render({ action: "/x", fields: [{ type: "text", name: "u" }] });
|
|
198
|
+
check("input: undefined value omits value attr", noValue.indexOf("value=") === -1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ============================================================
|
|
202
|
+
// render — textarea
|
|
203
|
+
// ============================================================
|
|
204
|
+
|
|
205
|
+
function testRenderTextarea() {
|
|
206
|
+
var html = b.forms.render({
|
|
207
|
+
action: "/x",
|
|
208
|
+
fields: [{
|
|
209
|
+
type: "textarea", name: "bio", value: "hi<b>", placeholder: "p",
|
|
210
|
+
required: true, readonly: true, disabled: true,
|
|
211
|
+
rows: 4, cols: 20, minlength: 2, maxlength: 500, id: "tid", className: "tc",
|
|
212
|
+
}],
|
|
213
|
+
});
|
|
214
|
+
check("textarea: name attr", html.indexOf('<textarea name="bio"') !== -1);
|
|
215
|
+
check("textarea: placeholder attr", html.indexOf('placeholder="p"') !== -1);
|
|
216
|
+
check("textarea: required flag", html.indexOf(" required") !== -1);
|
|
217
|
+
check("textarea: readonly flag", html.indexOf(" readonly") !== -1);
|
|
218
|
+
check("textarea: disabled flag", html.indexOf(" disabled") !== -1);
|
|
219
|
+
check("textarea: rows attr", html.indexOf('rows="4"') !== -1);
|
|
220
|
+
check("textarea: cols attr", html.indexOf('cols="20"') !== -1);
|
|
221
|
+
check("textarea: minlength attr", html.indexOf('minlength="2"') !== -1);
|
|
222
|
+
check("textarea: maxlength attr", html.indexOf('maxlength="500"') !== -1);
|
|
223
|
+
check("textarea: id attr", html.indexOf('id="tid"') !== -1);
|
|
224
|
+
check("textarea: class attr", html.indexOf('class="tc"') !== -1);
|
|
225
|
+
check("textarea: body html-escaped", html.indexOf(">hi<b></textarea>") !== -1);
|
|
226
|
+
|
|
227
|
+
var empty = b.forms.render({ action: "/x", fields: [{ type: "textarea", name: "t" }] });
|
|
228
|
+
check("textarea: no value → empty body", empty.indexOf('<textarea name="t"></textarea>') !== -1);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ============================================================
|
|
232
|
+
// render — select
|
|
233
|
+
// ============================================================
|
|
234
|
+
|
|
235
|
+
function testRenderSelect() {
|
|
236
|
+
var html = b.forms.render({
|
|
237
|
+
action: "/x",
|
|
238
|
+
fields: [{
|
|
239
|
+
type: "select", name: "sel", value: "b", required: true, disabled: true,
|
|
240
|
+
multiple: true, id: "sid", className: "sc",
|
|
241
|
+
options: [
|
|
242
|
+
{ value: "a", label: "A" },
|
|
243
|
+
{ value: "b" },
|
|
244
|
+
{ value: "c", label: "C", selected: true },
|
|
245
|
+
{ value: "d", label: "D", disabled: true },
|
|
246
|
+
],
|
|
247
|
+
}],
|
|
248
|
+
});
|
|
249
|
+
check("select: name attr", html.indexOf('<select name="sel"') !== -1);
|
|
250
|
+
check("select: required flag", html.indexOf(" required") !== -1);
|
|
251
|
+
check("select: disabled flag", html.indexOf(" disabled") !== -1);
|
|
252
|
+
check("select: multiple flag", html.indexOf(" multiple") !== -1);
|
|
253
|
+
check("select: id attr", html.indexOf('id="sid"') !== -1);
|
|
254
|
+
check("select: class attr", html.indexOf('class="sc"') !== -1);
|
|
255
|
+
check("select: unselected option rendered", html.indexOf('<option value="a">A</option>') !== -1);
|
|
256
|
+
check("select: option selected via field.value match, label falls back to value",
|
|
257
|
+
html.indexOf('<option value="b" selected>b</option>') !== -1);
|
|
258
|
+
check("select: option selected via o.selected", html.indexOf('<option value="c" selected>C</option>') !== -1);
|
|
259
|
+
check("select: disabled option", html.indexOf('<option value="d" disabled>D</option>') !== -1);
|
|
260
|
+
|
|
261
|
+
var noOptions = b.forms.render({ action: "/x", fields: [{ type: "select", name: "empty" }] });
|
|
262
|
+
check("select: missing options → empty select", noOptions.indexOf('<select name="empty"></select>') !== -1);
|
|
263
|
+
|
|
264
|
+
var noValue = b.forms.render({
|
|
265
|
+
action: "/x",
|
|
266
|
+
fields: [{ type: "select", name: "s2", options: [{ value: "a", label: "A" }, { value: "b", label: "B" }] }],
|
|
267
|
+
});
|
|
268
|
+
check("select: no field.value → no option auto-selected", noValue.indexOf("selected") === -1);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ============================================================
|
|
272
|
+
// render — field-type dispatch, labels, submit button
|
|
273
|
+
// ============================================================
|
|
274
|
+
|
|
275
|
+
function testRenderFieldTypesAndLabels() {
|
|
276
|
+
var types = [
|
|
277
|
+
"text", "email", "password", "number", "tel", "url", "search", "date",
|
|
278
|
+
"time", "datetime-local", "month", "week", "checkbox", "radio", "hidden",
|
|
279
|
+
"color", "file", "range", "image", "reset", "button",
|
|
280
|
+
];
|
|
281
|
+
for (var i = 0; i < types.length; i++) {
|
|
282
|
+
var t = types[i];
|
|
283
|
+
var h = b.forms.render({ action: "/x", fields: [{ type: t, name: "f" }] });
|
|
284
|
+
check("render: input widget for type " + t, h.indexOf('type="' + t + '"') !== -1);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
var labeled = b.forms.render({ action: "/x", fields: [{ type: "email", name: "e", label: "Email <addr>" }] });
|
|
288
|
+
check("render: label wraps control (label html-escaped)",
|
|
289
|
+
labeled.indexOf("<label>Email <addr> <input") !== -1);
|
|
290
|
+
|
|
291
|
+
var hidden = b.forms.render({ action: "/x", fields: [{ type: "hidden", name: "h", value: "x", label: "ignored" }] });
|
|
292
|
+
check("render: hidden field skips label wrapper", hidden.indexOf("<label>") === -1);
|
|
293
|
+
|
|
294
|
+
var submit = b.forms.render({ action: "/x", fields: [{ type: "submit", name: "s", value: "Go", label: "ignored" }] });
|
|
295
|
+
check("render: submit field skips label wrapper", submit.indexOf("<label>") === -1);
|
|
296
|
+
|
|
297
|
+
// A field with no `type` defaults to a text input — the dispatch
|
|
298
|
+
// (_renderField's `|| "text"`) and the emitted type attribute both
|
|
299
|
+
// resolve to "text", so the rendered markup stays self-consistent.
|
|
300
|
+
var deflt = b.forms.render({ action: "/x", fields: [{ name: "d" }] });
|
|
301
|
+
check("render: no-type field defaults to a text input", deflt.indexOf('<input type="text" name="d">') !== -1);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function testRenderSubmitButton() {
|
|
305
|
+
var withSubmitInput = b.forms.render({ action: "/x", fields: [{ type: "submit", name: "s", value: "Go" }] });
|
|
306
|
+
check("render: explicit submit field suppresses auto button", withSubmitInput.indexOf('<button type="submit">') === -1);
|
|
307
|
+
check("render: explicit submit field renders as input", withSubmitInput.indexOf('<input type="submit" name="s" value="Go">') !== -1);
|
|
308
|
+
|
|
309
|
+
var auto = b.forms.render({ action: "/x", fields: [] });
|
|
310
|
+
check("render: no submit field → default Submit button", auto.indexOf('<button type="submit">Submit</button>') !== -1);
|
|
311
|
+
|
|
312
|
+
var customLabel = b.forms.render({ action: "/x", submitLabel: "Send <it>", fields: [] });
|
|
313
|
+
check("render: custom submitLabel html-escaped in auto button",
|
|
314
|
+
customLabel.indexOf('<button type="submit">Send <it></button>') !== -1);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ============================================================
|
|
318
|
+
// validate — config-time throws
|
|
319
|
+
// ============================================================
|
|
320
|
+
|
|
321
|
+
function testValidateThrows() {
|
|
322
|
+
check("validate: no spec throws", _throws(function () { return b.forms.validate(); }));
|
|
323
|
+
check("validate: non-array fields throws", _throws(function () { return b.forms.validate({ fields: "x" }, {}); }));
|
|
324
|
+
check("validate: string pattern throws",
|
|
325
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "text", name: "p", pattern: "^abc$" }] }, { p: "abc" }); }));
|
|
326
|
+
check("validate: catastrophic-backtracking pattern refused before test",
|
|
327
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "text", name: "p", pattern: /(a+)+/ }] }, { p: "aaa" }); }));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ============================================================
|
|
331
|
+
// validate — required, shape, skips
|
|
332
|
+
// ============================================================
|
|
333
|
+
|
|
334
|
+
function testValidateRequiredAndShape() {
|
|
335
|
+
var r = b.forms.validate({ fields: [{ type: "text", name: "name", required: true, label: "Name" }] }, {});
|
|
336
|
+
check("validate: missing required field → invalid", r.valid === false);
|
|
337
|
+
check("validate: required message uses label", r.errors.name === "Name is required");
|
|
338
|
+
check("validate: result exposes valid/errors/values",
|
|
339
|
+
typeof r.valid === "boolean" && !!r.errors && !!r.values &&
|
|
340
|
+
Object.prototype.hasOwnProperty.call(r.values, "name"));
|
|
341
|
+
|
|
342
|
+
var r2 = b.forms.validate({ fields: [{ type: "text", name: "email", required: true, errorMessages: { required: "Give an email" } }] }, {});
|
|
343
|
+
check("validate: custom errorMessages.required used", r2.errors.email === "Give an email");
|
|
344
|
+
|
|
345
|
+
var r3 = b.forms.validate({ fields: [{ type: "text", name: "name", required: true }] }, { name: "ada" });
|
|
346
|
+
check("validate: satisfied required field → valid",
|
|
347
|
+
r3.valid === true && r3.errors.name === undefined && r3.values.name === "ada");
|
|
348
|
+
|
|
349
|
+
var r4 = b.forms.validate({ fields: [{ type: "text", name: "name", required: true }] }, { name: "" });
|
|
350
|
+
check("validate: empty string for required → invalid (default message)",
|
|
351
|
+
r4.valid === false && r4.errors.name === "name is required");
|
|
352
|
+
|
|
353
|
+
var noBody = b.forms.validate({ fields: [{ type: "text", name: "x", required: true, label: "X" }] });
|
|
354
|
+
check("validate: omitted body defaults to empty object", noBody.valid === false && noBody.errors.x === "X is required");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function testValidateSkips() {
|
|
358
|
+
var r = b.forms.validate({ fields: [
|
|
359
|
+
{ type: "submit", name: "s", value: "Go" },
|
|
360
|
+
{ type: "text" }, // no name
|
|
361
|
+
{ type: "text", name: "keep" },
|
|
362
|
+
] }, { s: "Go", keep: "hello" });
|
|
363
|
+
check("validate: submit field excluded from values", Object.prototype.hasOwnProperty.call(r.values, "s") === false);
|
|
364
|
+
check("validate: named non-submit field kept", r.values.keep === "hello");
|
|
365
|
+
check("validate: unnamed field produces no value entry", Object.keys(r.values).length === 1);
|
|
366
|
+
|
|
367
|
+
var r2 = b.forms.validate({ fields: [{ type: "text", name: "opt" }] }, {});
|
|
368
|
+
check("validate: optional absent field coerces to undefined",
|
|
369
|
+
Object.prototype.hasOwnProperty.call(r2.values, "opt") && r2.values.opt === undefined);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ============================================================
|
|
373
|
+
// validate — coercion (checkbox / number / range / date / default)
|
|
374
|
+
// ============================================================
|
|
375
|
+
|
|
376
|
+
function testValidateCheckboxCoercion() {
|
|
377
|
+
function chk(body) {
|
|
378
|
+
return b.forms.validate({ fields: [{ type: "checkbox", name: "c" }] }, body).values.c;
|
|
379
|
+
}
|
|
380
|
+
// Unchecked: the field is absent (undefined / null) or, from a typed
|
|
381
|
+
// (JSON) body, an explicit boolean false / numeric 0.
|
|
382
|
+
check("checkbox: unchecked (absent) → false", chk({}) === false);
|
|
383
|
+
check("checkbox: null → false", chk({ c: null }) === false);
|
|
384
|
+
check("checkbox: JSON boolean false → false", chk({ c: false }) === false);
|
|
385
|
+
check("checkbox: JSON numeric 0 → false", chk({ c: 0 }) === false);
|
|
386
|
+
// Checked: any PRESENT string (a urlencoded checkbox submits its value
|
|
387
|
+
// string only when ticked — the value's content is irrelevant), plus a
|
|
388
|
+
// typed truthy value.
|
|
389
|
+
check("checkbox: 'on' → true", chk({ c: "on" }) === true);
|
|
390
|
+
check("checkbox: empty-string value (present) → true", chk({ c: "" }) === true);
|
|
391
|
+
check("checkbox: custom value 'false' string (present) → true", chk({ c: "false" }) === true);
|
|
392
|
+
check("checkbox: custom value '0' string (present) → true", chk({ c: "0" }) === true);
|
|
393
|
+
check("checkbox: boolean true → true", chk({ c: true }) === true);
|
|
394
|
+
check("checkbox: JSON numeric 1 → true", chk({ c: 1 }) === true);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function testValidateRequiredCheckbox() {
|
|
398
|
+
// A required checkbox must be CHECKED. HTML defines `required` on a
|
|
399
|
+
// checkbox as "must be ticked," and the renderer emits that attribute —
|
|
400
|
+
// so server-side validate() enforces the same (the backend validates
|
|
401
|
+
// what the frontend displays). An unchecked required box is an error,
|
|
402
|
+
// not a silent pass.
|
|
403
|
+
var unchecked = b.forms.validate({ fields: [{ type: "checkbox", name: "tos", required: true, label: "Terms" }] }, {});
|
|
404
|
+
check("checkbox: required + unchecked (absent) → invalid",
|
|
405
|
+
unchecked.valid === false && unchecked.errors.tos === "Terms is required");
|
|
406
|
+
|
|
407
|
+
// A checkbox rendered with a custom value (even "false") submits that
|
|
408
|
+
// literal only when ticked, so presence satisfies the requirement.
|
|
409
|
+
var customValChecked = b.forms.validate({ fields: [{ type: "checkbox", name: "tos", required: true }] }, { tos: "false" });
|
|
410
|
+
check("checkbox: required + present string value (checked) → valid",
|
|
411
|
+
customValChecked.valid === true && customValChecked.values.tos === true);
|
|
412
|
+
|
|
413
|
+
// JSON bodies deliver an unchecked box as boolean false / numeric 0 — a
|
|
414
|
+
// non-browser caller must not bypass the required check with either.
|
|
415
|
+
var jsonFalse = b.forms.validate({ fields: [{ type: "checkbox", name: "tos", required: true }] }, { tos: false });
|
|
416
|
+
check("checkbox: required + JSON boolean false → invalid",
|
|
417
|
+
jsonFalse.valid === false && jsonFalse.errors.tos === "tos is required");
|
|
418
|
+
|
|
419
|
+
var jsonZero = b.forms.validate({ fields: [{ type: "checkbox", name: "tos", required: true }] }, { tos: 0 });
|
|
420
|
+
check("checkbox: required + JSON numeric 0 → invalid",
|
|
421
|
+
jsonZero.valid === false && jsonZero.errors.tos === "tos is required");
|
|
422
|
+
|
|
423
|
+
var checked = b.forms.validate({ fields: [{ type: "checkbox", name: "tos", required: true }] }, { tos: "on" });
|
|
424
|
+
check("checkbox: required + checked → valid", checked.valid === true && checked.values.tos === true);
|
|
425
|
+
|
|
426
|
+
var custom = b.forms.validate({ fields: [{ type: "checkbox", name: "tos", required: true, errorMessages: { required: "Accept the terms" } }] }, {});
|
|
427
|
+
check("checkbox: required uses custom errorMessages.required",
|
|
428
|
+
custom.valid === false && custom.errors.tos === "Accept the terms");
|
|
429
|
+
|
|
430
|
+
var optional = b.forms.validate({ fields: [{ type: "checkbox", name: "news" }] }, {});
|
|
431
|
+
check("checkbox: optional + unchecked → valid (coerced false)",
|
|
432
|
+
optional.valid === true && optional.values.news === false);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function testValidateNumberCoercionAndBounds() {
|
|
436
|
+
var num = b.forms.validate({ fields: [{ type: "number", name: "age" }] }, { age: "37" });
|
|
437
|
+
check("number: numeric string coerced to Number", num.values.age === 37 && num.valid === true);
|
|
438
|
+
|
|
439
|
+
var empty = b.forms.validate({ fields: [{ type: "number", name: "age" }] }, { age: "" });
|
|
440
|
+
check("number: empty string coerces to null", empty.values.age === null && empty.valid === true);
|
|
441
|
+
|
|
442
|
+
var nullish = b.forms.validate({ fields: [{ type: "number", name: "age" }] }, { age: null });
|
|
443
|
+
check("number: null coerces to null", nullish.values.age === null);
|
|
444
|
+
|
|
445
|
+
var nan = b.forms.validate({ fields: [{ type: "number", name: "age", label: "Age" }] }, { age: "abc" });
|
|
446
|
+
check("number: non-numeric → NaN → error", nan.valid === false && nan.errors.age === "Age must be a number");
|
|
447
|
+
|
|
448
|
+
var lo = b.forms.validate({ fields: [{ type: "number", name: "age", min: 10 }] }, { age: "5" });
|
|
449
|
+
check("number: below min → error", lo.valid === false && typeof lo.errors.age === "string");
|
|
450
|
+
|
|
451
|
+
var hi = b.forms.validate({ fields: [{ type: "number", name: "age", max: 100 }] }, { age: "150" });
|
|
452
|
+
check("number: above max → error", hi.valid === false && typeof hi.errors.age === "string");
|
|
453
|
+
|
|
454
|
+
var ok = b.forms.validate({ fields: [{ type: "number", name: "age", min: 0, max: 120 }] }, { age: "42" });
|
|
455
|
+
check("number: within bounds → valid", ok.valid === true && ok.values.age === 42);
|
|
456
|
+
|
|
457
|
+
var zero = b.forms.validate({ fields: [{ type: "number", name: "age", required: true }] }, { age: "0" });
|
|
458
|
+
check("number: zero satisfies required (not treated as empty)", zero.valid === true && zero.values.age === 0);
|
|
459
|
+
|
|
460
|
+
var reqEmpty = b.forms.validate({ fields: [{ type: "number", name: "age", required: true, label: "Age" }] }, { age: "" });
|
|
461
|
+
check("number: empty required number → required error", reqEmpty.valid === false && reqEmpty.errors.age === "Age is required");
|
|
462
|
+
|
|
463
|
+
var range = b.forms.validate({ fields: [{ type: "range", name: "vol", min: 0, max: 11 }] }, { vol: "12" });
|
|
464
|
+
check("range: shares numeric bounds path (above max)", range.valid === false && typeof range.errors.vol === "string");
|
|
465
|
+
|
|
466
|
+
var rangeNan = b.forms.validate({ fields: [{ type: "range", name: "vol" }] }, { vol: "x" });
|
|
467
|
+
check("range: non-numeric → NaN error", rangeNan.valid === false && typeof rangeNan.errors.vol === "string");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function testValidateNumericBoundConfig() {
|
|
471
|
+
// A defined-but-non-numeric bound (min/max/minlength/maxlength) is an
|
|
472
|
+
// operator config error: the comparison would silently never fire (NaN
|
|
473
|
+
// comparisons are always false), so the bound is a no-op rather than the
|
|
474
|
+
// guarantee the spec implies. validate() throws at the entry point,
|
|
475
|
+
// matching the string-pattern throw — a malformed spec fails loudly.
|
|
476
|
+
check("validate: non-numeric min throws",
|
|
477
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", min: "abc" }] }, { n: "5" }); }));
|
|
478
|
+
check("validate: non-numeric max throws",
|
|
479
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", max: "xyz" }] }, { n: "5" }); }));
|
|
480
|
+
check("validate: non-numeric minlength throws",
|
|
481
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "text", name: "s", minlength: "two" }] }, { s: "abcd" }); }));
|
|
482
|
+
check("validate: non-numeric maxlength throws",
|
|
483
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "text", name: "s", maxlength: "big" }] }, { s: "abcd" }); }));
|
|
484
|
+
check("validate: Infinity bound throws (must be finite)",
|
|
485
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", max: Infinity }] }, { n: "5" }); }));
|
|
486
|
+
|
|
487
|
+
// Number()-coercible junk (null / "" / whitespace / boolean / array) must
|
|
488
|
+
// NOT be accepted as a bound of 0/1 — those are malformed specs, and
|
|
489
|
+
// silently treating them as numeric diverges from the rendered constraint.
|
|
490
|
+
check("validate: null bound throws (not coerced to 0)",
|
|
491
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", min: null }] }, { n: "5" }); }));
|
|
492
|
+
check("validate: empty-string bound throws",
|
|
493
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", min: "" }] }, { n: "5" }); }));
|
|
494
|
+
check("validate: whitespace-string bound throws",
|
|
495
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", min: " " }] }, { n: "5" }); }));
|
|
496
|
+
check("validate: boolean bound throws",
|
|
497
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", max: false }] }, { n: "5" }); }));
|
|
498
|
+
check("validate: array bound throws",
|
|
499
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", min: [] }] }, { n: "5" }); }));
|
|
500
|
+
check("validate: minlength null throws (string control)",
|
|
501
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "text", name: "s", minlength: null }] }, { s: "abc" }); }));
|
|
502
|
+
check("validate: over-long numeric-literal bound throws (length-capped before regex)",
|
|
503
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", min: "1".repeat(50) }] }, { n: "5" }); }));
|
|
504
|
+
|
|
505
|
+
// Genuine numeric literals — number and clean numeric string — are fine.
|
|
506
|
+
var okStr = b.forms.validate({ fields: [{ type: "number", name: "n", min: "1", max: "10" }] }, { n: "5" });
|
|
507
|
+
check("validate: numeric-string bounds accepted", okStr.valid === true);
|
|
508
|
+
var okFloat = b.forms.validate({ fields: [{ type: "number", name: "n", min: "-2.5", max: "1e3" }] }, { n: "5" });
|
|
509
|
+
check("validate: signed/decimal/exponent numeric strings accepted", okFloat.valid === true);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function testValidateSpecValidatedUpFront() {
|
|
513
|
+
// A malformed field spec is a config error surfaced deterministically at
|
|
514
|
+
// the entry point — the throw must NOT depend on whether the submitted
|
|
515
|
+
// body happens to carry a value that reaches the bound. Each malformed
|
|
516
|
+
// spec throws whether the field is absent, empty, or present.
|
|
517
|
+
function throwsForAllBodies(field, name) {
|
|
518
|
+
var spec = { fields: [field] };
|
|
519
|
+
return _throws(function () { return b.forms.validate(spec, {}); }) && // absent
|
|
520
|
+
_throws(function () { return b.forms.validate(spec, _emptyBody(name)); }) && // empty
|
|
521
|
+
_throws(function () { return b.forms.validate(spec, _presentBody(name)); }); // present
|
|
522
|
+
}
|
|
523
|
+
function _emptyBody(name) { var o = {}; o[name] = ""; return o; }
|
|
524
|
+
function _presentBody(name) { var o = {}; o[name] = "5"; return o; }
|
|
525
|
+
|
|
526
|
+
check("spec: non-finite min throws regardless of body (absent/empty/present)",
|
|
527
|
+
throwsForAllBodies({ type: "number", name: "n", min: "abc" }, "n"));
|
|
528
|
+
check("spec: non-finite maxlength throws regardless of body",
|
|
529
|
+
throwsForAllBodies({ type: "text", name: "s", maxlength: NaN }, "s"));
|
|
530
|
+
check("spec: string pattern throws regardless of body",
|
|
531
|
+
throwsForAllBodies({ type: "text", name: "p", pattern: "^abc$" }, "p"));
|
|
532
|
+
check("spec: ReDoS-prone pattern throws regardless of body",
|
|
533
|
+
throwsForAllBodies({ type: "text", name: "p", pattern: /(a+)+$/ }, "p"));
|
|
534
|
+
|
|
535
|
+
// A well-formed spec with an absent optional value does NOT throw — the
|
|
536
|
+
// up-front pass only rejects malformed specs, never valid ones.
|
|
537
|
+
var okAbsent = b.forms.validate({ fields: [{ type: "number", name: "n", min: 1, max: 10 }, { type: "text", name: "s", pattern: /^[a-z]+$/ }] }, {});
|
|
538
|
+
check("spec: well-formed spec with absent values validates without throwing", okAbsent.valid === true);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function testValidateDateTimeBounds() {
|
|
542
|
+
// date / time / datetime-local / month / week controls carry ISO-string
|
|
543
|
+
// min/max bounds. Those are render-only attributes validate() never
|
|
544
|
+
// compares numerically, so a string bound must NOT be treated as a
|
|
545
|
+
// malformed numeric bound — it must not throw at the spec-validation pass.
|
|
546
|
+
var d = b.forms.validate({ fields: [{ type: "date", name: "d", min: "2026-01-01", max: "2026-12-31" }] }, { d: "2026-06-15" });
|
|
547
|
+
check("date: ISO-string min/max does not throw and validates", d.valid === true && d.values.d === "2026-06-15");
|
|
548
|
+
|
|
549
|
+
var t = b.forms.validate({ fields: [{ type: "time", name: "t", min: "09:00", max: "17:00" }] }, { t: "12:00" });
|
|
550
|
+
check("time: ISO-string min/max does not throw", t.valid === true);
|
|
551
|
+
|
|
552
|
+
var dtlAbsent = b.forms.validate({ fields: [{ type: "datetime-local", name: "dt", min: "2026-01-01T00:00" }] }, {});
|
|
553
|
+
check("datetime-local: ISO-string min with absent value does not throw", dtlAbsent.valid === true);
|
|
554
|
+
|
|
555
|
+
var wk = b.forms.validate({ fields: [{ type: "week", name: "w", max: "2026-W52" }] }, { w: "2026-W10" });
|
|
556
|
+
check("week: ISO-string max does not throw", wk.valid === true);
|
|
557
|
+
|
|
558
|
+
var mo = b.forms.validate({ fields: [{ type: "month", name: "m", min: "2026-01" }] }, { m: "2026-05" });
|
|
559
|
+
check("month: ISO-string min does not throw", mo.valid === true);
|
|
560
|
+
|
|
561
|
+
// The numeric guard is intact for the controls it applies to: a number
|
|
562
|
+
// field with a non-finite min still throws regardless of body.
|
|
563
|
+
check("number: non-finite min still throws (numeric control)",
|
|
564
|
+
_throws(function () { return b.forms.validate({ fields: [{ type: "number", name: "n", min: "abc" }] }, {}); }));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function testValidateMiscCoercion() {
|
|
568
|
+
var d = b.forms.validate({ fields: [{ type: "date", name: "d" }] }, { d: "2026-01-15" });
|
|
569
|
+
check("date: string passes through", d.values.d === "2026-01-15");
|
|
570
|
+
|
|
571
|
+
var dNum = b.forms.validate({ fields: [{ type: "date", name: "d" }] }, { d: 20260115 });
|
|
572
|
+
check("date: non-string coerced via String()", dNum.values.d === "20260115");
|
|
573
|
+
|
|
574
|
+
var tm = b.forms.validate({ fields: [{ type: "time", name: "t" }] }, { t: "13:30" });
|
|
575
|
+
check("time: string passes through", tm.values.t === "13:30");
|
|
576
|
+
|
|
577
|
+
var dtl = b.forms.validate({ fields: [{ type: "datetime-local", name: "dt" }] }, { dt: "2026-01-15T13:30" });
|
|
578
|
+
check("datetime-local: string passes through", dtl.values.dt === "2026-01-15T13:30");
|
|
579
|
+
|
|
580
|
+
var txtNum = b.forms.validate({ fields: [{ type: "text", name: "x" }] }, { x: 123 });
|
|
581
|
+
check("text: non-string body value coerced via String()", txtNum.values.x === "123");
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ============================================================
|
|
585
|
+
// validate — email / url
|
|
586
|
+
// ============================================================
|
|
587
|
+
|
|
588
|
+
function testValidateEmail() {
|
|
589
|
+
var ok = b.forms.validate({ fields: [{ type: "email", name: "e" }] }, { e: "ada@example.com" });
|
|
590
|
+
check("email: valid address passes", ok.valid === true && ok.values.e === "ada@example.com");
|
|
591
|
+
|
|
592
|
+
var bad = b.forms.validate({ fields: [{ type: "email", name: "e", label: "Email" }] }, { e: "not-an-email" });
|
|
593
|
+
check("email: invalid address → error", bad.valid === false && bad.errors.e === "Email must be a valid email address");
|
|
594
|
+
|
|
595
|
+
var over = b.forms.validate({ fields: [{ type: "email", name: "e" }] }, { e: "a".repeat(250) + "@e.com" });
|
|
596
|
+
check("email: over 254 chars → error (length cap before regex)", over.valid === false && typeof over.errors.e === "string");
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function testValidateUrl() {
|
|
600
|
+
var https = b.forms.validate({ fields: [{ type: "url", name: "u" }] }, { u: "https://example.com/path" });
|
|
601
|
+
check("url: valid https passes (default TLS-only)", https.valid === true);
|
|
602
|
+
|
|
603
|
+
var httpDefault = b.forms.validate({ fields: [{ type: "url", name: "u", label: "Site" }] }, { u: "http://example.com" });
|
|
604
|
+
check("url: cleartext http rejected by default", httpDefault.valid === false && httpDefault.errors.u === "Site must be a valid URL");
|
|
605
|
+
|
|
606
|
+
var httpOptIn = b.forms.validate({ fields: [{ type: "url", name: "u", allowHttp: true }] }, { u: "http://example.com" });
|
|
607
|
+
check("url: cleartext http admitted with allowHttp:true", httpOptIn.valid === true);
|
|
608
|
+
|
|
609
|
+
var invalid = b.forms.validate({ fields: [{ type: "url", name: "u" }] }, { u: "notaurl" });
|
|
610
|
+
check("url: unparseable value → error", invalid.valid === false && typeof invalid.errors.u === "string");
|
|
611
|
+
|
|
612
|
+
var over = b.forms.validate({ fields: [{ type: "url", name: "u" }] }, { u: "https://e.com/" + "a".repeat(8200) });
|
|
613
|
+
check("url: over 8 KiB → error without engaging the parser", over.valid === false && typeof over.errors.u === "string");
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// ============================================================
|
|
617
|
+
// validate — length, pattern, enum
|
|
618
|
+
// ============================================================
|
|
619
|
+
|
|
620
|
+
function testValidateLengthBounds() {
|
|
621
|
+
var short = b.forms.validate({ fields: [{ type: "text", name: "u", minlength: 5, label: "User" }] }, { u: "abc" });
|
|
622
|
+
check("string: below minlength → error", short.valid === false && typeof short.errors.u === "string");
|
|
623
|
+
|
|
624
|
+
var long = b.forms.validate({ fields: [{ type: "text", name: "u", maxlength: 3 }] }, { u: "abcdef" });
|
|
625
|
+
check("string: above maxlength → error", long.valid === false && typeof long.errors.u === "string");
|
|
626
|
+
|
|
627
|
+
var ok = b.forms.validate({ fields: [{ type: "text", name: "u", minlength: 2, maxlength: 8 }] }, { u: "abcd" });
|
|
628
|
+
check("string: within length bounds → valid", ok.valid === true);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function testValidatePattern() {
|
|
632
|
+
var ok = b.forms.validate({ fields: [{ type: "text", name: "code", pattern: /^[a-z]+$/ }] }, { code: "abc" });
|
|
633
|
+
check("pattern: RegExp match → valid", ok.valid === true);
|
|
634
|
+
|
|
635
|
+
var bad = b.forms.validate({ fields: [{ type: "text", name: "code", pattern: /^[a-z]+$/, label: "Code" }] }, { code: "ABC123" });
|
|
636
|
+
check("pattern: RegExp mismatch → error (label in default message)", bad.valid === false && bad.errors.code === "Code has an invalid format");
|
|
637
|
+
|
|
638
|
+
var badNoLabel = b.forms.validate({ fields: [{ type: "text", name: "code", pattern: /^[a-z]+$/ }] }, { code: "ABC123" });
|
|
639
|
+
check("pattern: RegExp mismatch without label falls back to field name", badNoLabel.valid === false && badNoLabel.errors.code === "code has an invalid format");
|
|
640
|
+
|
|
641
|
+
var custom = b.forms.validate({ fields: [{ type: "text", name: "code", pattern: /^[a-z]+$/, errorMessages: { pattern: "letters only" } }] }, { code: "9" });
|
|
642
|
+
check("pattern: custom errorMessages.pattern used", custom.valid === false && custom.errors.code === "letters only");
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function testValidateEnum() {
|
|
646
|
+
var okSel = b.forms.validate({ fields: [{ type: "select", name: "s", options: [{ value: "a" }, { value: "b" }] }] }, { s: "a" });
|
|
647
|
+
check("select: value within options → valid", okSel.valid === true && okSel.values.s === "a");
|
|
648
|
+
|
|
649
|
+
var badSel = b.forms.validate({ fields: [{ type: "select", name: "s", label: "Choice", options: [{ value: "a" }, { value: "b" }] }] }, { s: "c" });
|
|
650
|
+
check("select: value outside options → error", badSel.valid === false && badSel.errors.s === "Choice has an invalid value");
|
|
651
|
+
|
|
652
|
+
var okRadio = b.forms.validate({ fields: [{ type: "radio", name: "r", options: [{ value: "x" }, { value: "y" }] }] }, { r: "y" });
|
|
653
|
+
check("radio: value within options → valid", okRadio.valid === true);
|
|
654
|
+
|
|
655
|
+
var badRadio = b.forms.validate({ fields: [{ type: "radio", name: "r", options: [{ value: "x" }] }] }, { r: "z" });
|
|
656
|
+
check("radio: value outside options → error", badRadio.valid === false && typeof badRadio.errors.r === "string");
|
|
657
|
+
|
|
658
|
+
var noOpts = b.forms.validate({ fields: [{ type: "select", name: "s" }] }, { s: "anything" });
|
|
659
|
+
check("select: no options array → enum check skipped (valid)", noOpts.valid === true && noOpts.values.s === "anything");
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// ============================================================
|
|
663
|
+
// validate — full submission + shared-spec render/validate round-trip
|
|
664
|
+
// ============================================================
|
|
665
|
+
|
|
666
|
+
function testValidateEndToEnd() {
|
|
667
|
+
var spec = { fields: [
|
|
668
|
+
{ type: "email", name: "email", required: true },
|
|
669
|
+
{ type: "password", name: "password", required: true, minlength: 8 },
|
|
670
|
+
{ type: "number", name: "age", min: 18, max: 120 },
|
|
671
|
+
{ type: "checkbox", name: "tos" },
|
|
672
|
+
{ type: "submit", name: "submit" },
|
|
673
|
+
] };
|
|
674
|
+
|
|
675
|
+
var good = b.forms.validate(spec, { email: "ada@example.com", password: "hunter2!!", age: "30", tos: "on" });
|
|
676
|
+
check("e2e: fully valid submission", good.valid === true && Object.keys(good.errors).length === 0);
|
|
677
|
+
check("e2e: coerced values", good.values.email === "ada@example.com" && good.values.age === 30 && good.values.tos === true);
|
|
678
|
+
check("e2e: submit field excluded from values", Object.prototype.hasOwnProperty.call(good.values, "submit") === false);
|
|
679
|
+
|
|
680
|
+
var bad = b.forms.validate(spec, { email: "nope", password: "short", age: "5" });
|
|
681
|
+
check("e2e: multiple errors collected",
|
|
682
|
+
bad.valid === false && !!bad.errors.email && !!bad.errors.password && !!bad.errors.age);
|
|
683
|
+
check("e2e: unchecked checkbox coerces to false", bad.values.tos === false);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function testSharedSpecRoundTrip() {
|
|
687
|
+
var spec = {
|
|
688
|
+
action: "/signup",
|
|
689
|
+
method: "POST",
|
|
690
|
+
csrfToken: b.forms.generateCsrfToken(),
|
|
691
|
+
fields: [
|
|
692
|
+
{ type: "email", name: "email", label: "Email", required: true },
|
|
693
|
+
{ type: "text", name: "user", minlength: 3, maxlength: 20 },
|
|
694
|
+
{ type: "submit", name: "go", value: "Sign up" },
|
|
695
|
+
],
|
|
696
|
+
};
|
|
697
|
+
var html = b.forms.render(spec);
|
|
698
|
+
check("shared-spec: render emits form + csrf + fields",
|
|
699
|
+
html.indexOf("<form ") === 0 && html.indexOf('name="_csrf"') !== -1 && html.indexOf('type="email"') !== -1);
|
|
700
|
+
var res = b.forms.validate(spec, { email: "ada@example.com", user: "ada" });
|
|
701
|
+
check("shared-spec: the same spec validates a matching body", res.valid === true);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// ============================================================
|
|
705
|
+
|
|
706
|
+
async function run() {
|
|
707
|
+
testGenerateCsrfToken();
|
|
708
|
+
testVerifyCsrfToken();
|
|
709
|
+
testEscapeAttribute();
|
|
710
|
+
|
|
711
|
+
testRenderThrows();
|
|
712
|
+
testRenderFormAttrs();
|
|
713
|
+
testRenderCsrfGating();
|
|
714
|
+
testRenderInputAttrs();
|
|
715
|
+
testRenderInputValuePresence();
|
|
716
|
+
testRenderTextarea();
|
|
717
|
+
testRenderSelect();
|
|
718
|
+
testRenderFieldTypesAndLabels();
|
|
719
|
+
testRenderSubmitButton();
|
|
720
|
+
|
|
721
|
+
testValidateThrows();
|
|
722
|
+
testValidateRequiredAndShape();
|
|
723
|
+
testValidateSkips();
|
|
724
|
+
testValidateCheckboxCoercion();
|
|
725
|
+
testValidateRequiredCheckbox();
|
|
726
|
+
testValidateNumberCoercionAndBounds();
|
|
727
|
+
testValidateNumericBoundConfig();
|
|
728
|
+
testValidateSpecValidatedUpFront();
|
|
729
|
+
testValidateDateTimeBounds();
|
|
730
|
+
testValidateMiscCoercion();
|
|
731
|
+
testValidateEmail();
|
|
732
|
+
testValidateUrl();
|
|
733
|
+
testValidateLengthBounds();
|
|
734
|
+
testValidatePattern();
|
|
735
|
+
testValidateEnum();
|
|
736
|
+
testValidateEndToEnd();
|
|
737
|
+
testSharedSpecRoundTrip();
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
module.exports = { run: run };
|
|
741
|
+
|
|
742
|
+
if (require.main === module) {
|
|
743
|
+
run().then(
|
|
744
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
|
|
745
|
+
function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
|
|
746
|
+
);
|
|
747
|
+
}
|