aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-07-15 15:37:46 +0530
committerGitHub <[email protected]>2026-07-15 15:37:46 +0530
commit119475cb439ea3cbad7c75bdddb2f35033e17202 (patch)
tree20d7bf2b8241465ae7eb93ef8eaa90c83ea41338 /tests
parent3dcca5d124fb0c5e92995bf5116f34c8794235d8 (diff)
parentbe086a5933d29acb63572c666deb4d5fa23e0f0e (diff)
downloadedify-119475cb439ea3cbad7c75bdddb2f35033e17202.tar.xz
edify-119475cb439ea3cbad7c75bdddb2f35033e17202.zip
feat: snapshot suite, char-class normalization, ReDoS detector, and structured open-frame errors (#281)
Ships the compile-path correctness epic + the build-time validation epic end to end. ## Snapshot suite - **Snapshot helper.** `edify/testing/snapshots.py` — an ~80-line difflib-based helper (`assert_snapshot(actual, path)` + `SnapshotMismatchError` / `SnapshotMissingError`) with `EDIFY_UPDATE_SNAPSHOTS=1` regenerate mode. Publicly re-exported through `edify.testing`. - **Library snapshots.** Every one of the 223 public library validators emits a committed `.regex` snapshot under `tests/snapshots/library/`. Any compile-path change that touches the emitted form surfaces here first. - **Doc snapshots.** Every `.. code-block:: python` block across `docs/` is exec-ed in a fresh edify-populated namespace and its resulting `Regex` / `Pattern` bodies are snapshotted under `tests/snapshots/docs/<file-stem>/<line>.regex`. Broken blocks pinned to planned validators (`email_rfc_5322`, `ipv4`, `ipv6`, `iso_date`, `zip`) are explicitly deferred to the docs rewrite (epic #81). - **Edge-case fixtures.** Deeply-nested alternation, combined lookaround, item-4 named-backref, item-6 ReDoS-flagged constructs, hex/email/composition — 8 fixtures under `tests/snapshots/fixtures/`. A parallel test asserts the ReDoS warning fires for the nested-quantifier fixture. - **Orphan detection.** A discipline test walks all three snapshot roots and fails CI if any snapshot has no live source (library Pattern / registered fixture / discovered code-block). The reverse direction (source without snapshot) is caught by `SnapshotMissingError` in the per-source test. ## Compile-path invariants - **Insertion-order determinism** pinned: `any_of_chars("zyxabc321")` emits `[zyxabc321]` deterministically across runs; a discipline test defends against future set/dict-backed rewrites. - **`to_regex_string() == to_regex().pattern`** pinned per registered library Pattern (223 parametrized checks). Item 5's regex-module backend was the change most likely to silently split these — the invariant now holds them together. ## Char-class escaping normalization (breaking) - Introduces `edify.compile.escape.escape_for_char_class(value)` — the minimal correct form. Inside `[...]`, only `\`, `]`, `^` (first position), and `-` (interior position) are escaped; everything else is a literal. - `any_of_chars` and `anything_but_chars` now use the minimal escaper; `string`, `char`, `anything_but_string` keep the full `re.escape` since they live outside a class. - **Before:** `any_of_chars('#?!@$%^&*-')` → `[\#\?!@$%\^\&\*\-]`. **After:** `[#?!@$%^&*-]`. Match behavior is identical (equivalence corpus test asserts this). ## AST walker substrate + ReDoS detector - **`edify.elements.walk.walk_elements`** — the shared depth-first walker that yields every `BaseElement` in an emission-order tree. Serialize already had one; extracted, generalized, and reused by ReDoS. - **Build-time ReDoS detector** (`edify.compile.redos.warn_on_redos_constructs`) — walks the element tree at `to_regex()` time and emits `ReDoSWarning` when it finds the classic `(x+)+` shape (an unbounded quantifier wrapping a single-child group whose one child is itself an unbounded quantifier). The check is intentionally conservative — it requires the group's children tuple to have length one, so composite patterns like `(?:X)*(?:Y*)*` aren't false-positived. ## Structured open-frame stack in error messages - `CannotCallSubexpressionError.__init__(open_frames: tuple[OpenFrameInfo, ...])` — lists every open frame innermost-first, one line per frame, with its opener call site (`filename:line:col`). Named captures reproduce the declared name (`.named_capture("username")`). - `DanglingQuantifierError.__init__(pending_quantifier_name, pending_quantifier_call_site)` — names the specific queued quantifier (`.exactly(3)`) and points at its call site. - `edify.builder.diagnose.describe_open_frames(state)` — the shared helper both terminals and subexpression use. ## Breaking changes - `any_of_chars` and `anything_but_chars` emit the minimal-correct form, not the over-escaped form. Match behavior is unchanged (equivalence corpus test verifies), but any caller comparing the emitted string against a hard-coded expected value with `\.`/`\-` etc. needs to drop the redundant backslashes. - `CannotCallSubexpressionError.__init__` now takes `tuple[OpenFrameInfo, ...]` instead of a bare string. Direct constructors need updating. - `DanglingQuantifierError.__init__` accepts optional `pending_quantifier_name` / `pending_quantifier_call_site` kwargs; the bare `DanglingQuantifierError()` call still works. Closes #72, closes #98, closes #99, closes #100, closes #101, closes #102, closes #103, closes #104, closes #105, closes #106, closes #73, closes #107, closes #114, closes #192.
Diffstat (limited to 'tests')
-rw-r--r--tests/builder/builder.test.py8
-rw-r--r--tests/builder/insertion_order.test.py27
-rw-r--r--tests/compile/escape.test.py115
-rw-r--r--tests/compile/redos.test.py78
-rw-r--r--tests/discipline/orphans.test.py131
-rw-r--r--tests/docs/snapshots.test.py143
-rw-r--r--tests/errors/errors.test.py23
-rw-r--r--tests/errors/frames.test.py69
-rw-r--r--tests/fixtures/edge_cases.test.py130
-rw-r--r--tests/library/invariants.test.py28
-rw-r--r--tests/library/snapshots.test.py36
-rw-r--r--tests/snapshots/docs/built-in/index/0028.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/0034.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/0064.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/0115.regexbin0 -> 17748 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/0128.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/0160.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/0199.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/0957.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/0983.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/1001.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/built-in/index/1017.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0019.regexbin0 -> 17816 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0044.regexbin0 -> 17941 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0059.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0080.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0103.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0120.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0139.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0156.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0175.regexbin0 -> 17944 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0189.regexbin0 -> 17944 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0201.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0216.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0232.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0248.regexbin0 -> 17942 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0265.regexbin0 -> 17958 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0293.regexbin0 -> 17957 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0318.regexbin0 -> 17977 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0347.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0371.regexbin0 -> 17959 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0421.regexbin0 -> 17960 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0445.regexbin0 -> 17954 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0469.regexbin0 -> 17954 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0493.regexbin0 -> 17956 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0514.regexbin0 -> 17956 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0535.regexbin0 -> 17943 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0554.regexbin0 -> 17943 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0573.regexbin0 -> 17944 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0592.regexbin0 -> 17943 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0611.regexbin0 -> 17944 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0630.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0649.regexbin0 -> 17946 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0668.regexbin0 -> 17947 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0687.regexbin0 -> 17948 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0706.regexbin0 -> 17946 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0725.regexbin0 -> 17946 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0744.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0762.regexbin0 -> 17946 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0780.regexbin0 -> 17964 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0798.regexbin0 -> 17946 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0816.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0834.regexbin0 -> 17941 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0852.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0892.regexbin0 -> 17956 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0913.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/builder/index/0942.regexbin0 -> 17965 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/flags/index/0026.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/flags/index/0038.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/flags/index/0051.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/flags/index/0065.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/flags/index/0080.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/regex-builder/flags/index/0094.regexbin0 -> 17945 bytes
-rw-r--r--tests/snapshots/docs/upgrading/0.3-to-1.0/0018.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/docs/upgrading/0.3-to-1.0/0037.regexbin0 -> 17922 bytes
-rw-r--r--tests/snapshots/fixtures/alternation_with_overlapping_prefixes_redos.regex1
-rw-r--r--tests/snapshots/fixtures/combined_lookaround.regex1
-rw-r--r--tests/snapshots/fixtures/deeply_nested_alternation.regex1
-rw-r--r--tests/snapshots/fixtures/email_shape_pattern.regex1
-rw-r--r--tests/snapshots/fixtures/hex_color_pattern.regex1
-rw-r--r--tests/snapshots/fixtures/named_backreference_pair.regex1
-rw-r--r--tests/snapshots/fixtures/nested_unbounded_quantifier_redos.regex1
-rw-r--r--tests/snapshots/fixtures/pattern_composition_via_use.regex1
-rw-r--r--tests/snapshots/library/abnf.regex1
-rw-r--r--tests/snapshots/library/address.regex1
-rw-r--r--tests/snapshots/library/age.regex1
-rw-r--r--tests/snapshots/library/aircraft.regex1
-rw-r--r--tests/snapshots/library/alpha.regex1
-rw-r--r--tests/snapshots/library/alphanumeric.regex1
-rw-r--r--tests/snapshots/library/altitude.regex1
-rw-r--r--tests/snapshots/library/antlr.regex1
-rw-r--r--tests/snapshots/library/apache.regex1
-rw-r--r--tests/snapshots/library/apikey.regex1
-rw-r--r--tests/snapshots/library/arn.regex1
-rw-r--r--tests/snapshots/library/arxiv.regex1
-rw-r--r--tests/snapshots/library/ascii.regex1
-rw-r--r--tests/snapshots/library/asin.regex1
-rw-r--r--tests/snapshots/library/atom.regex1
-rw-r--r--tests/snapshots/library/avro.regex1
-rw-r--r--tests/snapshots/library/barcode.regex1
-rw-r--r--tests/snapshots/library/base.regex1
-rw-r--r--tests/snapshots/library/bearer.regex1
-rw-r--r--tests/snapshots/library/bearing.regex1
-rw-r--r--tests/snapshots/library/bic.regex1
-rw-r--r--tests/snapshots/library/blood.regex1
-rw-r--r--tests/snapshots/library/bnf.regex1
-rw-r--r--tests/snapshots/library/bump.regex1
-rw-r--r--tests/snapshots/library/captcha.regex1
-rw-r--r--tests/snapshots/library/card.regex1
-rw-r--r--tests/snapshots/library/cargo.regex1
-rw-r--r--tests/snapshots/library/certificate.regex1
-rw-r--r--tests/snapshots/library/challenge.regex1
-rw-r--r--tests/snapshots/library/charset.regex1
-rw-r--r--tests/snapshots/library/checksum.regex1
-rw-r--r--tests/snapshots/library/cidr.regex1
-rw-r--r--tests/snapshots/library/codec.regex1
-rw-r--r--tests/snapshots/library/color.regex1
-rw-r--r--tests/snapshots/library/component.regex1
-rw-r--r--tests/snapshots/library/cookie.regex1
-rw-r--r--tests/snapshots/library/coordinate.regex1
-rw-r--r--tests/snapshots/library/cron.regex1
-rw-r--r--tests/snapshots/library/crypto.regex1
-rw-r--r--tests/snapshots/library/csp.regex1
-rw-r--r--tests/snapshots/library/csr.regex1
-rw-r--r--tests/snapshots/library/csrf.regex1
-rw-r--r--tests/snapshots/library/csv.regex1
-rw-r--r--tests/snapshots/library/currency.regex1
-rw-r--r--tests/snapshots/library/cusip.regex1
-rw-r--r--tests/snapshots/library/date.regex1
-rw-r--r--tests/snapshots/library/datetime.regex1
-rw-r--r--tests/snapshots/library/der.regex1
-rw-r--r--tests/snapshots/library/dicom.regex1
-rw-r--r--tests/snapshots/library/did.regex1
-rw-r--r--tests/snapshots/library/digest.regex1
-rw-r--r--tests/snapshots/library/docker.regex1
-rw-r--r--tests/snapshots/library/docx.regex1
-rw-r--r--tests/snapshots/library/doi.regex1
-rw-r--r--tests/snapshots/library/domain.regex1
-rw-r--r--tests/snapshots/library/dosage.regex1
-rw-r--r--tests/snapshots/library/duration.regex1
-rw-r--r--tests/snapshots/library/ebnf.regex1
-rw-r--r--tests/snapshots/library/ein.regex1
-rw-r--r--tests/snapshots/library/email.regex1
-rw-r--r--tests/snapshots/library/emoji.regex1
-rw-r--r--tests/snapshots/library/encoding.regex1
-rw-r--r--tests/snapshots/library/epoch.regex1
-rw-r--r--tests/snapshots/library/epub.regex1
-rw-r--r--tests/snapshots/library/extension.regex1
-rw-r--r--tests/snapshots/library/favicon.regexbin0 -> 58 bytes
-rw-r--r--tests/snapshots/library/fax.regex1
-rw-r--r--tests/snapshots/library/filename.regexbin0 -> 49 bytes
-rw-r--r--tests/snapshots/library/filter.regex1
-rw-r--r--tests/snapshots/library/flight.regex1
-rw-r--r--tests/snapshots/library/fraction.regex1
-rw-r--r--tests/snapshots/library/geohash.regex1
-rw-r--r--tests/snapshots/library/git.regex1
-rw-r--r--tests/snapshots/library/glob.regexbin0 -> 39 bytes
-rw-r--r--tests/snapshots/library/gradient.regex1
-rw-r--r--tests/snapshots/library/graphql.regex1
-rw-r--r--tests/snapshots/library/gtin.regex1
-rw-r--r--tests/snapshots/library/guid.regex1
-rw-r--r--tests/snapshots/library/hal.regex1
-rw-r--r--tests/snapshots/library/handle.regex1
-rw-r--r--tests/snapshots/library/hash.regex1
-rw-r--r--tests/snapshots/library/hdf5.regex1
-rw-r--r--tests/snapshots/library/hmac.regex1
-rw-r--r--tests/snapshots/library/hostname.regex1
-rw-r--r--tests/snapshots/library/htaccess.regex1
-rw-r--r--tests/snapshots/library/html.regex1
-rw-r--r--tests/snapshots/library/humans.regex1
-rw-r--r--tests/snapshots/library/iata.regex1
-rw-r--r--tests/snapshots/library/iban.regex1
-rw-r--r--tests/snapshots/library/icao.regex1
-rw-r--r--tests/snapshots/library/iccid.regex1
-rw-r--r--tests/snapshots/library/image.regex1
-rw-r--r--tests/snapshots/library/imei.regex1
-rw-r--r--tests/snapshots/library/imo.regex1
-rw-r--r--tests/snapshots/library/ini.regex1
-rw-r--r--tests/snapshots/library/integer.regex1
-rw-r--r--tests/snapshots/library/interval.regex1
-rw-r--r--tests/snapshots/library/ip.regex1
-rw-r--r--tests/snapshots/library/isbn.regex1
-rw-r--r--tests/snapshots/library/isin.regex1
-rw-r--r--tests/snapshots/library/issn.regex1
-rw-r--r--tests/snapshots/library/itin.regex1
-rw-r--r--tests/snapshots/library/json.regex1
-rw-r--r--tests/snapshots/library/jsonapi.regex1
-rw-r--r--tests/snapshots/library/jwt.regex1
-rw-r--r--tests/snapshots/library/keyring.regex1
-rw-r--r--tests/snapshots/library/lei.regex1
-rw-r--r--tests/snapshots/library/locale.regex1
-rw-r--r--tests/snapshots/library/mac.regex1
-rw-r--r--tests/snapshots/library/makefile.regex1
-rw-r--r--tests/snapshots/library/manifest.regex1
-rw-r--r--tests/snapshots/library/medical.regex1
-rw-r--r--tests/snapshots/library/meid.regex1
-rw-r--r--tests/snapshots/library/mfa.regex1
-rw-r--r--tests/snapshots/library/mgrs.regex1
-rw-r--r--tests/snapshots/library/mimetype.regex1
-rw-r--r--tests/snapshots/library/mmsi.regex1
-rw-r--r--tests/snapshots/library/mnemonic.regex1
-rw-r--r--tests/snapshots/library/mobi.regex1
-rw-r--r--tests/snapshots/library/mpn.regex1
-rw-r--r--tests/snapshots/library/msgpack.regex1
-rw-r--r--tests/snapshots/library/natural.regex1
-rw-r--r--tests/snapshots/library/nginx.regex1
-rw-r--r--tests/snapshots/library/nonce.regex1
-rw-r--r--tests/snapshots/library/number.regex1
-rw-r--r--tests/snapshots/library/numeric.regex1
-rw-r--r--tests/snapshots/library/oauth.regex1
-rw-r--r--tests/snapshots/library/odt.regex1
-rw-r--r--tests/snapshots/library/offset.regex1
-rw-r--r--tests/snapshots/library/openapi.regex1
-rw-r--r--tests/snapshots/library/openid.regex1
-rw-r--r--tests/snapshots/library/orc.regex1
-rw-r--r--tests/snapshots/library/orcid.regex1
-rw-r--r--tests/snapshots/library/ordinal.regex1
-rw-r--r--tests/snapshots/library/otp.regex1
-rw-r--r--tests/snapshots/library/package.regex1
-rw-r--r--tests/snapshots/library/pager.regex1
-rw-r--r--tests/snapshots/library/palette.regex1
-rw-r--r--tests/snapshots/library/parquet.regex1
-rw-r--r--tests/snapshots/library/passkey.regex1
-rw-r--r--tests/snapshots/library/password.regex1
-rw-r--r--tests/snapshots/library/path.regexbin0 -> 154 bytes
-rw-r--r--tests/snapshots/library/pdf.regex1
-rw-r--r--tests/snapshots/library/peg.regex1
-rw-r--r--tests/snapshots/library/pem.regex1
-rw-r--r--tests/snapshots/library/percentage.regex1
-rw-r--r--tests/snapshots/library/pest.regex1
-rw-r--r--tests/snapshots/library/pgp.regex1
-rw-r--r--tests/snapshots/library/phone.regex1
-rw-r--r--tests/snapshots/library/pin.regex1
-rw-r--r--tests/snapshots/library/place.regex1
-rw-r--r--tests/snapshots/library/plate.regex1
-rw-r--r--tests/snapshots/library/plus.regex1
-rw-r--r--tests/snapshots/library/pmc.regex1
-rw-r--r--tests/snapshots/library/pmid.regex1
-rw-r--r--tests/snapshots/library/port.regex1
-rw-r--r--tests/snapshots/library/postal.regex1
-rw-r--r--tests/snapshots/library/pptx.regex1
-rw-r--r--tests/snapshots/library/printable.regexbin0 -> 18 bytes
-rw-r--r--tests/snapshots/library/protobuf.regex1
-rw-r--r--tests/snapshots/library/ptr.regex1
-rw-r--r--tests/snapshots/library/ratio.regex1
-rw-r--r--tests/snapshots/library/readme.regex1
-rw-r--r--tests/snapshots/library/ref.regex1
-rw-r--r--tests/snapshots/library/refresh.regex1
-rw-r--r--tests/snapshots/library/regex.regex1
-rw-r--r--tests/snapshots/library/robots.regex1
-rw-r--r--tests/snapshots/library/roman.regex1
-rw-r--r--tests/snapshots/library/routing.regex1
-rw-r--r--tests/snapshots/library/rss.regex1
-rw-r--r--tests/snapshots/library/rtf.regex1
-rw-r--r--tests/snapshots/library/saml.regex1
-rw-r--r--tests/snapshots/library/scientific.regex1
-rw-r--r--tests/snapshots/library/script.regex1
-rw-r--r--tests/snapshots/library/secret.regex1
-rw-r--r--tests/snapshots/library/sedol.regex1
-rw-r--r--tests/snapshots/library/semver.regex1
-rw-r--r--tests/snapshots/library/session.regex1
-rw-r--r--tests/snapshots/library/shebang.regex1
-rw-r--r--tests/snapshots/library/signature.regex1
-rw-r--r--tests/snapshots/library/signing.regex1
-rw-r--r--tests/snapshots/library/sitemap.regex1
-rw-r--r--tests/snapshots/library/sku.regex1
-rw-r--r--tests/snapshots/library/slug.regex1
-rw-r--r--tests/snapshots/library/soap.regex1
-rw-r--r--tests/snapshots/library/socket.regex1
-rw-r--r--tests/snapshots/library/sortcode.regex1
-rw-r--r--tests/snapshots/library/ssh.regex1
-rw-r--r--tests/snapshots/library/ssn.regex1
-rw-r--r--tests/snapshots/library/sso.regex1
-rw-r--r--tests/snapshots/library/subdomain.regex1
-rw-r--r--tests/snapshots/library/subnet.regex1
-rw-r--r--tests/snapshots/library/svg.regex1
-rw-r--r--tests/snapshots/library/swagger.regex1
-rw-r--r--tests/snapshots/library/swatch.regex1
-rw-r--r--tests/snapshots/library/tex.regex1
-rw-r--r--tests/snapshots/library/time.regex1
-rw-r--r--tests/snapshots/library/timestamp.regex1
-rw-r--r--tests/snapshots/library/timezone.regex1
-rw-r--r--tests/snapshots/library/tin.regex1
-rw-r--r--tests/snapshots/library/tld.regex1
-rw-r--r--tests/snapshots/library/token.regex1
-rw-r--r--tests/snapshots/library/toml.regex1
-rw-r--r--tests/snapshots/library/tsv.regex1
-rw-r--r--tests/snapshots/library/unicode.regexbin0 -> 18 bytes
-rw-r--r--tests/snapshots/library/uri.regex1
-rw-r--r--tests/snapshots/library/url.regex1
-rw-r--r--tests/snapshots/library/useragent.regex1
-rw-r--r--tests/snapshots/library/username.regex1
-rw-r--r--tests/snapshots/library/uuid.regex1
-rw-r--r--tests/snapshots/library/vat.regex1
-rw-r--r--tests/snapshots/library/vehicle.regex1
-rw-r--r--tests/snapshots/library/version.regex1
-rw-r--r--tests/snapshots/library/vin.regex1
-rw-r--r--tests/snapshots/library/wallet.regex1
-rw-r--r--tests/snapshots/library/webauthn.regex1
-rw-r--r--tests/snapshots/library/webhook.regex1
-rw-r--r--tests/snapshots/library/word.regex1
-rw-r--r--tests/snapshots/library/x509.regex1
-rw-r--r--tests/snapshots/library/xlsx.regex1
-rw-r--r--tests/snapshots/library/xml.regex1
-rw-r--r--tests/snapshots/library/yaml.regex1
-rw-r--r--tests/snapshots/library/year.regex1
-rw-r--r--tests/testing/snapshots.test.py90
307 files changed, 1096 insertions, 7 deletions
diff --git a/tests/builder/builder.test.py b/tests/builder/builder.test.py
index 82aa1cf..5f96b5c 100644
--- a/tests/builder/builder.test.py
+++ b/tests/builder/builder.test.py
@@ -404,14 +404,14 @@ def test_end_of_input():
def test_any_of_chars():
expr = RegexBuilder().any_of_chars("aeiou.-")
- regex_equality("[aeiou\\.\\-]", expr)
- regex_compilation("[aeiou\\.\\-]", expr)
+ regex_equality("[aeiou.-]", expr)
+ regex_compilation("[aeiou.-]", expr)
def test_anything_but_chars():
expr = RegexBuilder().anything_but_chars("aeiou.-")
- regex_equality("[^aeiou\\.\\-]", expr)
- regex_compilation("[^aeiou\\.\\-]", expr)
+ regex_equality("[^aeiou.-]", expr)
+ regex_compilation("[^aeiou.-]", expr)
def test_anything_but_string():
diff --git a/tests/builder/insertion_order.test.py b/tests/builder/insertion_order.test.py
new file mode 100644
index 0000000..4feae65
--- /dev/null
+++ b/tests/builder/insertion_order.test.py
@@ -0,0 +1,27 @@
+"""Pinned property: character-class members preserve insertion order across calls."""
+
+from edify import RegexBuilder
+
+
+def test_any_of_chars_preserves_the_exact_insertion_order_across_runs():
+ first = RegexBuilder().any_of_chars("zyxabc321").to_regex_string()
+ second = RegexBuilder().any_of_chars("zyxabc321").to_regex_string()
+ third = RegexBuilder().any_of_chars("zyxabc321").to_regex_string()
+ assert first == second == third == "[zyxabc321]"
+
+
+def test_anything_but_chars_preserves_the_exact_insertion_order_across_runs():
+ first = RegexBuilder().anything_but_chars("zyxabc321").to_regex_string()
+ second = RegexBuilder().anything_but_chars("zyxabc321").to_regex_string()
+ assert first == second == "[^zyxabc321]"
+
+
+def test_any_of_chars_preserves_duplicated_members_in_declaration_order():
+ emitted = RegexBuilder().any_of_chars("aabbcc").to_regex_string()
+ assert emitted == "[aabbcc]"
+
+
+def test_any_of_chars_never_alphabetically_sorts_its_members():
+ emitted = RegexBuilder().any_of_chars("cba").to_regex_string()
+ assert emitted == "[cba]"
+ assert emitted != "[abc]"
diff --git a/tests/compile/escape.test.py b/tests/compile/escape.test.py
new file mode 100644
index 0000000..e9ab657
--- /dev/null
+++ b/tests/compile/escape.test.py
@@ -0,0 +1,115 @@
+"""Tests for the character-class escape normalization.
+
+The minimal-correct form escapes only ``\\``, ``]``, first-position ``^``,
+and interior ``-`` — everything else passes through as a literal. Match
+behavior against a representative corpus must stay identical to the
+previous over-escaped form.
+"""
+
+import re
+
+import pytest
+
+from edify import RegexBuilder
+
+_METACHARS_UNRELATED_TO_CLASS = "#?!@$%^&*"
+_MIXED_CORPUS = [
+ "",
+ "a",
+ "z",
+ "#",
+ "?",
+ "!",
+ "@",
+ "$",
+ "%",
+ "^",
+ "&",
+ "*",
+ "-",
+ ".",
+ "abc",
+ "a#b",
+ "1-2",
+ " ",
+ "foo.bar",
+ "hello world",
+ "a^b",
+ "]",
+ "[a]",
+ "\\",
+]
+
+
+def test_any_of_chars_emits_minimal_form_for_class_safe_metachars():
+ emitted = RegexBuilder().any_of_chars(_METACHARS_UNRELATED_TO_CLASS).to_regex_string()
+ assert emitted == "[#?!@$%^&*]"
+
+
+def test_any_of_chars_escapes_backslash_and_closing_bracket_only():
+ emitted = RegexBuilder().any_of_chars("a\\b]c").to_regex_string()
+ assert emitted == "[a\\\\b\\]c]"
+
+
+def test_any_of_chars_escapes_first_position_caret_only():
+ first_caret = RegexBuilder().any_of_chars("^abc").to_regex_string()
+ later_caret = RegexBuilder().any_of_chars("a^bc").to_regex_string()
+ assert first_caret == "[\\^abc]"
+ assert later_caret == "[a^bc]"
+
+
+def test_any_of_chars_escapes_interior_dash_only():
+ edge_dashes = RegexBuilder().any_of_chars("-a-").to_regex_string()
+ interior_dash = RegexBuilder().any_of_chars("a-b").to_regex_string()
+ assert edge_dashes == "[-a-]"
+ assert interior_dash == "[a\\-b]"
+
+
+def test_any_of_chars_leaves_dot_and_asterisk_unescaped_inside_a_class():
+ emitted = RegexBuilder().any_of_chars(".*").to_regex_string()
+ assert emitted == "[.*]"
+
+
+def test_anything_but_chars_leading_position_of_body_is_not_caret():
+ emitted = RegexBuilder().anything_but_chars("^abc").to_regex_string()
+ assert emitted == "[^\\^abc]"
+
+
+def test_anything_but_chars_normalizes_the_same_way_as_any_of_chars():
+ emitted = RegexBuilder().anything_but_chars("#?!@$%^&*").to_regex_string()
+ assert emitted == "[^#?!@$%^&*]"
+
+
+def test_string_terminal_still_uses_full_escape_outside_the_class():
+ emitted = RegexBuilder().string("a.b").to_regex_string()
+ assert emitted == "a\\.b"
+
+
+def test_char_terminal_still_uses_full_escape_outside_the_class():
+ emitted = RegexBuilder().char(".").to_regex_string()
+ assert emitted == "\\."
+
+
[email protected]("candidate", _MIXED_CORPUS)
+def test_normalized_and_over_escaped_char_classes_match_the_same_inputs(candidate):
+ normalized_pattern = (
+ RegexBuilder().any_of_chars(_METACHARS_UNRELATED_TO_CLASS).to_regex_string()
+ )
+ over_escaped_pattern = f"[{re.escape(_METACHARS_UNRELATED_TO_CLASS)}]"
+ normalized_hits = re.findall(normalized_pattern, candidate)
+ over_escaped_hits = re.findall(over_escaped_pattern, candidate)
+ assert normalized_hits == over_escaped_hits
+
+
[email protected]("candidate", _MIXED_CORPUS)
+def test_normalized_and_over_escaped_negated_classes_match_the_same_inputs(candidate):
+ normalized_pattern = RegexBuilder().anything_but_chars("aeiou.-").to_regex_string()
+ over_escaped_pattern = "[^aeiou\\.\\-]"
+ normalized_hits = re.findall(normalized_pattern, candidate)
+ over_escaped_hits = re.findall(over_escaped_pattern, candidate)
+ assert normalized_hits == over_escaped_hits
+
+
+def test_any_of_chars_empty_class_still_renders_empty():
+ emitted = RegexBuilder().any_of_chars("").to_regex_string()
+ assert emitted == "[]"
diff --git a/tests/compile/redos.test.py b/tests/compile/redos.test.py
new file mode 100644
index 0000000..c4e1ba9
--- /dev/null
+++ b/tests/compile/redos.test.py
@@ -0,0 +1,78 @@
+"""Tests for the build-time ReDoS detector."""
+
+import pytest
+
+from edify import RegexBuilder
+from edify.compile.redos import ReDoSWarning
+
+
+def _classic_redos_builder():
+ return RegexBuilder().one_or_more().group().one_or_more().digit().end()
+
+
+def test_classic_nested_unbounded_quantifier_triggers_the_warning():
+ with pytest.warns(ReDoSWarning, match="nested unbounded quantifier"):
+ _classic_redos_builder().to_regex()
+
+
+def test_zero_or_more_wrapping_one_or_more_triggers_the_warning():
+ builder = RegexBuilder().zero_or_more().group().one_or_more().digit().end()
+ with pytest.warns(ReDoSWarning):
+ builder.to_regex()
+
+
+def test_at_least_wrapping_zero_or_more_triggers_the_warning():
+ builder = RegexBuilder().at_least(2).group().zero_or_more().word().end()
+ with pytest.warns(ReDoSWarning):
+ builder.to_regex()
+
+
+def test_lazy_variants_also_trigger_the_warning():
+ builder = RegexBuilder().one_or_more_lazy().group().zero_or_more_lazy().digit().end()
+ with pytest.warns(ReDoSWarning):
+ builder.to_regex()
+
+
+def test_warning_message_names_both_quantifiers():
+ with pytest.warns(ReDoSWarning) as record:
+ _classic_redos_builder().to_regex()
+ message_text = str(record[0].message)
+ assert "one_or_more()" in message_text
+ assert "engine='regex'" in message_text
+
+
+def test_bounded_quantifier_does_not_trigger_the_warning(recwarn):
+ builder = RegexBuilder().exactly(3).group().exactly(2).digit().end()
+ builder.to_regex()
+ assert not any(isinstance(warning.message, ReDoSWarning) for warning in recwarn)
+
+
+def test_grouped_quantifier_over_a_composite_group_does_not_trigger(recwarn):
+ builder = RegexBuilder().one_or_more().group().digit().letter().end()
+ builder.to_regex()
+ assert not any(isinstance(warning.message, ReDoSWarning) for warning in recwarn)
+
+
+def test_grouped_quantifier_over_a_single_non_quantifier_child_does_not_trigger(recwarn):
+ builder = RegexBuilder().one_or_more().group().digit().end()
+ builder.to_regex()
+ assert not any(isinstance(warning.message, ReDoSWarning) for warning in recwarn)
+
+
+def test_sequential_unbounded_quantifiers_do_not_trigger_the_warning(recwarn):
+ builder = RegexBuilder().one_or_more().digit().one_or_more().letter()
+ builder.to_regex()
+ assert not any(isinstance(warning.message, ReDoSWarning) for warning in recwarn)
+
+
+def test_warning_only_fires_once_per_construct_within_a_single_terminal_call():
+ with pytest.warns(ReDoSWarning) as record:
+ _classic_redos_builder().to_regex()
+ redos_records = [w for w in record if isinstance(w.message, ReDoSWarning)]
+ assert len(redos_records) == 1
+
+
+def test_terminal_still_returns_the_compiled_regex_after_warning():
+ with pytest.warns(ReDoSWarning):
+ compiled = _classic_redos_builder().to_regex()
+ assert compiled.source == "(?:\\d+)+"
diff --git a/tests/discipline/orphans.test.py b/tests/discipline/orphans.test.py
new file mode 100644
index 0000000..a0a3150
--- /dev/null
+++ b/tests/discipline/orphans.test.py
@@ -0,0 +1,131 @@
+"""Orphan detection — every committed snapshot must trace back to a live source.
+
+Runs three checks, one per snapshot root:
+
+* ``tests/snapshots/library/<name>.regex`` — must correspond to a
+ :class:`edify.Pattern` exported from :mod:`edify.library`.
+* ``tests/snapshots/fixtures/<name>.regex`` — must correspond to a fixture
+ registered in ``tests/fixtures/edge_cases.test.py``.
+* ``tests/snapshots/docs/<file>/<block>.regex`` — must correspond to a
+ ``.. code-block:: python`` block at that line in that RST.
+
+The reverse direction (a source without a snapshot) is caught by
+``SnapshotMissingError`` inside the per-source test; adding a new source
+without generating its snapshot fails the direct test, not this one.
+"""
+
+from pathlib import Path
+
+import edify.library as library_module
+from edify import Pattern
+
+_REPO_ROOT = Path(__file__).parent.parent.parent
+_SNAPSHOT_ROOT = _REPO_ROOT / "tests" / "snapshots"
+_LIBRARY_SNAPSHOT_ROOT = _SNAPSHOT_ROOT / "library"
+_FIXTURES_SNAPSHOT_ROOT = _SNAPSHOT_ROOT / "fixtures"
+_DOCS_SNAPSHOT_ROOT = _SNAPSHOT_ROOT / "docs"
+_DOCS_SOURCE_ROOT = _REPO_ROOT / "docs"
+_CODE_BLOCK_HEADER = ".. code-block:: python"
+
+
+def test_every_library_snapshot_has_a_registered_pattern():
+ snapshot_stems = {path.stem for path in _LIBRARY_SNAPSHOT_ROOT.glob("*.regex")}
+ pattern_names = _library_pattern_names()
+ orphaned_snapshots = snapshot_stems - pattern_names
+ assert orphaned_snapshots == set(), (
+ f"library snapshot files without a registered Pattern in edify.library: "
+ f"{sorted(orphaned_snapshots)}"
+ )
+
+
+def test_every_fixture_snapshot_has_a_registered_fixture():
+ snapshot_stems = {path.stem for path in _FIXTURES_SNAPSHOT_ROOT.glob("*.regex")}
+ fixture_names = _registered_fixture_names()
+ orphaned_snapshots = snapshot_stems - fixture_names
+ assert orphaned_snapshots == set(), (
+ f"fixture snapshot files without a registered fixture in "
+ f"tests/fixtures/edge_cases.test.py: {sorted(orphaned_snapshots)}"
+ )
+
+
+def test_every_doc_snapshot_has_a_matching_rst_code_block():
+ snapshot_pairs = _collect_doc_snapshot_pairs()
+ doc_block_pairs = _collect_doc_code_block_pairs()
+ orphaned_snapshots = snapshot_pairs - doc_block_pairs
+ assert orphaned_snapshots == set(), (
+ f"doc snapshot files without a matching .. code-block:: python: "
+ f"{sorted(orphaned_snapshots)}"
+ )
+
+
+def _library_pattern_names() -> set[str]:
+ names: set[str] = set()
+ for export_name in dir(library_module):
+ if export_name.startswith("_"):
+ continue
+ value = getattr(library_module, export_name)
+ if isinstance(value, Pattern):
+ names.add(export_name)
+ return names
+
+
+def _registered_fixture_names() -> set[str]:
+ fixture_source = (_REPO_ROOT / "tests" / "fixtures" / "edge_cases.test.py").read_text()
+ return _fixture_names_from_source(fixture_source)
+
+
+def _fixture_names_from_source(source: str) -> set[str]:
+ names: set[str] = set()
+ for line in source.splitlines():
+ stripped = line.strip()
+ if not stripped.startswith('("'):
+ continue
+ closing_quote_index = stripped.find('"', 2)
+ if closing_quote_index == -1:
+ continue
+ names.add(stripped[2:closing_quote_index])
+ return names
+
+
+def _collect_doc_snapshot_pairs() -> set[tuple[str, int]]:
+ pairs: set[tuple[str, int]] = set()
+ for snapshot in _DOCS_SNAPSHOT_ROOT.rglob("*.regex"):
+ relative_stem = snapshot.parent.relative_to(_DOCS_SNAPSHOT_ROOT).as_posix()
+ block_start = int(snapshot.stem)
+ pairs.add((relative_stem, block_start))
+ return pairs
+
+
+def _collect_doc_code_block_pairs() -> set[tuple[str, int]]:
+ pairs: set[tuple[str, int]] = set()
+ for rst_path in _DOCS_SOURCE_ROOT.rglob("*.rst"):
+ relative_stem = rst_path.relative_to(_DOCS_SOURCE_ROOT).with_suffix("").as_posix()
+ for block_start in _code_block_start_lines(rst_path):
+ pairs.add((relative_stem, block_start))
+ return pairs
+
+
+def _code_block_start_lines(rst_path: Path) -> list[int]:
+ starts: list[int] = []
+ lines = rst_path.read_text().splitlines()
+ line_index = 0
+ while line_index < len(lines):
+ if lines[line_index].strip() != _CODE_BLOCK_HEADER:
+ line_index += 1
+ continue
+ body_start = _first_content_line(lines, line_index + 1)
+ if body_start is not None:
+ starts.append(body_start)
+ line_index += 1
+ return starts
+
+
+def _first_content_line(lines: list[str], from_index: int) -> int | None:
+ line_index = from_index
+ while line_index < len(lines) and lines[line_index].strip() == "":
+ line_index += 1
+ if line_index >= len(lines):
+ return None
+ if lines[line_index].startswith(" ") or lines[line_index].startswith("\t"):
+ return line_index
+ return None
diff --git a/tests/docs/snapshots.test.py b/tests/docs/snapshots.test.py
new file mode 100644
index 0000000..75523ee
--- /dev/null
+++ b/tests/docs/snapshots.test.py
@@ -0,0 +1,143 @@
+"""Snapshot suite for every ``.. code-block:: python`` block shipped in the docs.
+
+Each block is executed in a fresh edify-populated namespace; any ``Regex``
+or ``Pattern`` bound to a top-level name has its ``.to_regex_string()``
+snapshotted under ``tests/snapshots/docs/<file-stem>/<block-index>.regex``.
+The snapshot pins the docs against silent drift: an intentional compile-path
+change surfaces every doc example the change touches so their prose stays
+truthful.
+"""
+
+import re
+import sys
+from pathlib import Path
+
+import pytest
+
+import edify
+import edify.library as edify_library
+from edify import Pattern
+from edify.result import Regex
+from edify.testing import assert_snapshot
+
+_ON_PYPY = hasattr(sys, "pypy_version_info")
+
+_REPO_ROOT = Path(__file__).parent.parent.parent
+_DOCS_ROOT = _REPO_ROOT / "docs"
+_SNAPSHOT_ROOT = _REPO_ROOT / "tests" / "snapshots" / "docs"
+_CODE_BLOCK_HEADER = ".. code-block:: python"
+
+
+def _collect_python_code_blocks(rst_path: Path):
+ lines = rst_path.read_text().splitlines()
+ line_index = 0
+ while line_index < len(lines):
+ stripped = lines[line_index].strip()
+ if stripped != _CODE_BLOCK_HEADER:
+ line_index += 1
+ continue
+ block_start, block_lines = _consume_indented_block(lines, line_index + 1)
+ if block_lines:
+ yield block_start, "\n".join(block_lines)
+ line_index = block_start + len(block_lines)
+
+
+def _consume_indented_block(lines, start_index):
+ block_lines: list[str] = []
+ line_index = start_index
+ while line_index < len(lines) and lines[line_index].strip() == "":
+ line_index += 1
+ if line_index >= len(lines):
+ return line_index, block_lines
+ indent_prefix = lines[line_index][: len(lines[line_index]) - len(lines[line_index].lstrip())]
+ if indent_prefix == "":
+ return line_index, block_lines
+ while line_index < len(lines):
+ current_line = lines[line_index]
+ if current_line.strip() == "":
+ block_lines.append("")
+ line_index += 1
+ continue
+ if not current_line.startswith(indent_prefix):
+ break
+ block_lines.append(current_line[len(indent_prefix) :])
+ line_index += 1
+ return line_index - len(block_lines), block_lines
+
+
+def _discover_blocks():
+ for rst_path in sorted(_DOCS_ROOT.rglob("*.rst")):
+ for block_start, block_source in _collect_python_code_blocks(rst_path):
+ relative_stem = rst_path.relative_to(_DOCS_ROOT).with_suffix("")
+ yield rst_path, block_start, block_source, relative_stem
+
+
+def _snapshot_bodies_for_block(namespace: dict) -> str:
+ interesting_pairs = []
+ for identifier, value in namespace.items():
+ if identifier.startswith("_") or identifier in {"edify", "Pattern", "Regex"}:
+ continue
+ if isinstance(value, Regex):
+ interesting_pairs.append((identifier, "regex", value.source))
+ elif isinstance(value, Pattern):
+ interesting_pairs.append((identifier, "pattern", value.to_regex_string()))
+ interesting_pairs.sort()
+ rendered_lines = [f"{name} :: {kind} :: {body}" for name, kind, body in interesting_pairs]
+ return "\n".join(rendered_lines) + ("\n" if rendered_lines else "")
+
+
+DISCOVERED_BLOCKS = list(_discover_blocks())
+
+_BLOCKS_DEFERRED_TO_DOCS_REWRITE = frozenset(
+ {
+ ("built-in/index", 46),
+ ("built-in/index", 83),
+ ("built-in/index", 98),
+ ("built-in/index", 142),
+ ("built-in/index", 175),
+ ("built-in/index", 184),
+ ("built-in/index", 936),
+ ("regex-builder/builder/index", 396),
+ }
+)
+
+_BLOCKS_SKIPPED_ON_PYPY = frozenset(
+ {
+ ("regex-builder/flags/index", 38),
+ }
+)
+
+
+ ("rst_path", "block_start", "block_source", "relative_stem"),
+ DISCOVERED_BLOCKS,
+ ids=[
+ f"{relative_stem}:{block_start}" for _, block_start, _, relative_stem in DISCOVERED_BLOCKS
+ ],
+)
+def test_doc_code_block_produces_the_snapshotted_regex(
+ rst_path, block_start, block_source, relative_stem
+):
+ stem_string = str(relative_stem)
+ if (stem_string, block_start) in _BLOCKS_DEFERRED_TO_DOCS_REWRITE:
+ pytest.skip("doc block references validators / kwargs slated for the docs rewrite")
+ if _ON_PYPY and (stem_string, block_start) in _BLOCKS_SKIPPED_ON_PYPY:
+ pytest.skip("doc block hits PyPy's re.DEBUG upstream disassembler bug")
+ namespace = _prepared_exec_namespace()
+ exec(compile(block_source, str(rst_path), "exec"), namespace)
+ rendered = _snapshot_bodies_for_block(namespace)
+ snapshot_path = _SNAPSHOT_ROOT / stem_string / f"{block_start:04d}.regex"
+ assert_snapshot(rendered, snapshot_path)
+
+
+def _prepared_exec_namespace() -> dict[str, object]:
+ namespace: dict[str, object] = {"edify": edify, "Pattern": Pattern, "Regex": Regex, "re": re}
+ for edify_export in dir(edify):
+ if edify_export.startswith("_"):
+ continue
+ namespace[edify_export] = getattr(edify, edify_export)
+ for library_export in dir(edify_library):
+ if library_export.startswith("_"):
+ continue
+ namespace[library_export] = getattr(edify_library, library_export)
+ return namespace
diff --git a/tests/errors/errors.test.py b/tests/errors/errors.test.py
index 632045d..82eb8fe 100644
--- a/tests/errors/errors.test.py
+++ b/tests/errors/errors.test.py
@@ -24,6 +24,7 @@ from edify.errors.naming import (
from edify.errors.structure import (
CannotCallSubexpressionError,
CannotEndWhileBuildingRootExpressionError,
+ OpenFrameInfo,
)
@@ -176,10 +177,26 @@ def test_cannot_end_while_building_root_expression():
def test_cannot_call_subexpression():
- error = CannotCallSubexpressionError("capture")
+ frames = (OpenFrameInfo(kind="capture", opened_at=None),)
+ error = CannotCallSubexpressionError(frames)
text = str(error)
assert "cannot merge a subexpression that has an unclosed frame" in text
- assert "capture" in text
+ assert "1 open frame" in text
+ assert ".capture()" in text
+
+
+def test_cannot_call_subexpression_lists_every_open_frame_in_the_stack():
+ frames = (
+ OpenFrameInfo(kind="capture", opened_at=None),
+ OpenFrameInfo(kind="assert_ahead", opened_at=None),
+ OpenFrameInfo(kind="group", opened_at=None),
+ )
+ error = CannotCallSubexpressionError(frames)
+ text = str(error)
+ assert "3 open frame" in text
+ assert "1. .capture()" in text
+ assert "2. .assert_ahead()" in text
+ assert "3. .group()" in text
def test_every_annotated_error_message_starts_with_error_prefix():
@@ -202,7 +219,7 @@ def test_every_annotated_error_message_starts_with_error_prefix():
CannotCreateDuplicateNamedGroupError("x"),
NamedGroupDoesNotExistError("x"),
CannotEndWhileBuildingRootExpressionError(),
- CannotCallSubexpressionError("capture"),
+ CannotCallSubexpressionError((OpenFrameInfo(kind="capture", opened_at=None),)),
]
for error in errors:
text = str(error)
diff --git a/tests/errors/frames.test.py b/tests/errors/frames.test.py
new file mode 100644
index 0000000..b4077ba
--- /dev/null
+++ b/tests/errors/frames.test.py
@@ -0,0 +1,69 @@
+"""Tests for the structured open-frame stack surfaced by CannotCallSubexpressionError."""
+
+import pytest
+
+from edify import RegexBuilder
+from edify.errors.structure import CannotCallSubexpressionError
+
+
+def test_single_open_capture_frame_is_named_in_the_error_message():
+ unfinished = RegexBuilder().capture().digit()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ unfinished.to_regex_string()
+ text = str(excinfo.value)
+ assert "1 open frame" in text
+ assert "1. .capture()" in text
+
+
+def test_nested_open_frames_are_listed_innermost_first():
+ unfinished = RegexBuilder().capture().group().assert_ahead().digit()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ unfinished.to_regex_string()
+ text = str(excinfo.value)
+ assert "3 open frame" in text
+ innermost_index = text.index("1. .assert_ahead()")
+ middle_index = text.index("2. .group()")
+ outermost_index = text.index("3. .capture()")
+ assert innermost_index < middle_index < outermost_index
+
+
+def test_named_capture_frame_reports_the_declared_group_name():
+ unfinished = RegexBuilder().named_capture("username").letter()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ unfinished.to_regex_string()
+ text = str(excinfo.value)
+ assert '.named_capture("username")' in text
+
+
+def test_open_frames_carry_a_file_line_column_pointer_when_the_context_is_captured():
+ unfinished = RegexBuilder().assert_behind().digit()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ unfinished.to_regex_string()
+ text = str(excinfo.value)
+ assert "opened at" in text
+ assert __file__ in text or "frames.test.py" in text
+
+
+def test_subexpression_lists_open_frames_from_the_argument_not_the_receiver():
+ unfinished = RegexBuilder().named_capture("group").digit()
+ parent = RegexBuilder()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ parent.subexpression(unfinished)
+ assert 'named_capture("group")' in str(excinfo.value)
+
+
+def test_dangling_quantifier_reports_the_specific_pending_quantifier_name():
+ from edify.errors.quantifier import DanglingQuantifierError
+
+ with pytest.raises(DanglingQuantifierError) as excinfo:
+ RegexBuilder().exactly(3).to_regex_string()
+ text = str(excinfo.value)
+ assert ".exactly(3)" in text
+
+
+def test_dangling_quantifier_falls_back_to_generic_summary_when_name_unknown():
+ from edify.errors.quantifier import DanglingQuantifierError
+
+ error = DanglingQuantifierError()
+ text = str(error)
+ assert "dangling quantifier" in text
diff --git a/tests/fixtures/edge_cases.test.py b/tests/fixtures/edge_cases.test.py
new file mode 100644
index 0000000..18200b6
--- /dev/null
+++ b/tests/fixtures/edge_cases.test.py
@@ -0,0 +1,130 @@
+"""Snapshot suite for the compile-path edge-case fixtures.
+
+Each fixture builds a small, intentionally-tricky pattern and pins the
+emitted regex string. The fixtures are the ones most likely to silently
+drift when the compile path is refactored: deeply nested alternation,
+combined lookaround, item-4 named backreferences, and constructs that
+:mod:`edify` flags for ReDoS.
+"""
+
+from pathlib import Path
+
+import pytest
+
+from edify import Pattern, RegexBuilder
+from edify.testing import assert_snapshot
+
+_SNAPSHOT_ROOT = Path(__file__).parent.parent / "snapshots" / "fixtures"
+
+
+def _deeply_nested_alternation():
+ inner_alt = RegexBuilder().any_of("abc", "def", "ghi")
+ outer_alt = RegexBuilder().any_of("first", "second", "third")
+ return (
+ RegexBuilder()
+ .start_of_input()
+ .subexpression(inner_alt)
+ .string("-")
+ .subexpression(outer_alt)
+ .end_of_input()
+ )
+
+
+def _combined_lookaround_positive_and_negative():
+ return (
+ RegexBuilder()
+ .assert_ahead()
+ .digit()
+ .end()
+ .assert_not_ahead()
+ .string("0")
+ .end()
+ .assert_behind()
+ .string("$")
+ .end()
+ .digit()
+ .digit()
+ .digit()
+ )
+
+
+def _named_backreference_pair():
+ return (
+ RegexBuilder()
+ .named_capture("open")
+ .any_of("[", "(", "{")
+ .end()
+ .zero_or_more()
+ .word()
+ .named_back_reference("open")
+ )
+
+
+def _nested_unbounded_quantifier_redos():
+ return RegexBuilder().one_or_more().group().one_or_more().digit().end()
+
+
+def _alternation_with_overlapping_prefixes_redos():
+ return RegexBuilder().one_or_more().any_of("aa", "aaa", "aaaa")
+
+
+def _hex_color_pattern():
+ return (
+ RegexBuilder()
+ .start_of_input()
+ .string("#")
+ .any_of_chars("0123456789abcdefABCDEF")
+ .exactly(6)
+ .word()
+ .end_of_input()
+ )
+
+
+def _email_shape_pattern():
+ return (
+ RegexBuilder()
+ .start_of_input()
+ .one_or_more()
+ .any_of_chars("a-zA-Z0-9._%+-")
+ .string("@")
+ .one_or_more()
+ .any_of_chars("a-zA-Z0-9.-")
+ .string(".")
+ .at_least(2)
+ .letter()
+ .end_of_input()
+ )
+
+
+def _pattern_composition_via_use():
+ reusable = Pattern().at_least(2).letter()
+ return RegexBuilder().start_of_input().subexpression(reusable).end_of_input()
+
+
+_FIXTURES = [
+ ("deeply_nested_alternation", _deeply_nested_alternation),
+ ("combined_lookaround", _combined_lookaround_positive_and_negative),
+ ("named_backreference_pair", _named_backreference_pair),
+ ("nested_unbounded_quantifier_redos", _nested_unbounded_quantifier_redos),
+ ("alternation_with_overlapping_prefixes_redos", _alternation_with_overlapping_prefixes_redos),
+ ("hex_color_pattern", _hex_color_pattern),
+ ("email_shape_pattern", _email_shape_pattern),
+ ("pattern_composition_via_use", _pattern_composition_via_use),
+]
+
+
+ ("fixture_name", "builder_factory"), _FIXTURES, ids=[name for name, _ in _FIXTURES]
+)
+def test_edge_case_fixture_emits_the_snapshotted_regex(fixture_name, builder_factory):
+ builder = builder_factory()
+ emitted = builder.to_regex_string()
+ snapshot_path = _SNAPSHOT_ROOT / f"{fixture_name}.regex"
+ assert_snapshot(emitted, snapshot_path)
+
+
+def test_nested_unbounded_quantifier_fixture_raises_the_redos_warning():
+ from edify.compile.redos import ReDoSWarning
+
+ with pytest.warns(ReDoSWarning):
+ _nested_unbounded_quantifier_redos().to_regex()
diff --git a/tests/library/invariants.test.py b/tests/library/invariants.test.py
new file mode 100644
index 0000000..a153d2f
--- /dev/null
+++ b/tests/library/invariants.test.py
@@ -0,0 +1,28 @@
+"""Cross-cutting invariants that must hold for every registered library Pattern."""
+
+import pytest
+
+import edify.library as library_module
+from edify import Pattern
+
+
+def _registered_patterns():
+ for name in sorted(dir(library_module)):
+ if name.startswith("_"):
+ continue
+ value = getattr(library_module, name)
+ if isinstance(value, Pattern):
+ yield name, value
+
+
+REGISTERED_PATTERNS = list(_registered_patterns())
+
+
[email protected](("name", "pattern"), REGISTERED_PATTERNS, ids=lambda item: item)
+def test_to_regex_string_matches_the_compiled_pattern_source(name, pattern):
+ emitted_source = pattern.to_regex_string()
+ compiled_source = pattern.to_regex().source
+ assert emitted_source == compiled_source, (
+ f"library pattern {name!r} emits {emitted_source!r} but its compiled Regex reports "
+ f"{compiled_source!r} — the terminals have drifted apart."
+ )
diff --git a/tests/library/snapshots.test.py b/tests/library/snapshots.test.py
new file mode 100644
index 0000000..05ff721
--- /dev/null
+++ b/tests/library/snapshots.test.py
@@ -0,0 +1,36 @@
+"""Snapshot suite for every public library validator.
+
+Each :class:`edify.Pattern` exposed by :mod:`edify.library` has a committed
+``.to_regex_string()`` snapshot under ``tests/snapshots/library/<name>.regex``.
+Set ``EDIFY_UPDATE_SNAPSHOTS=1`` to regenerate the corpus in one pytest run
+after an intentional compile-path change.
+"""
+
+from pathlib import Path
+
+import pytest
+
+import edify.library as library_module
+from edify import Pattern
+from edify.testing import assert_snapshot
+
+_SNAPSHOT_ROOT = Path(__file__).parent.parent / "snapshots" / "library"
+
+
+def _registered_patterns():
+ for name in sorted(dir(library_module)):
+ if name.startswith("_"):
+ continue
+ value = getattr(library_module, name)
+ if isinstance(value, Pattern):
+ yield name, value
+
+
+REGISTERED_PATTERNS = list(_registered_patterns())
+
+
[email protected](("name", "pattern"), REGISTERED_PATTERNS, ids=lambda item: item)
+def test_library_pattern_emits_the_snapshotted_regex(name, pattern):
+ snapshot_path = _SNAPSHOT_ROOT / f"{name}.regex"
+ emitted = pattern.to_regex_string()
+ assert_snapshot(emitted, snapshot_path)
diff --git a/tests/snapshots/docs/built-in/index/0028.regex b/tests/snapshots/docs/built-in/index/0028.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/0028.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/0034.regex b/tests/snapshots/docs/built-in/index/0034.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/0034.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/0064.regex b/tests/snapshots/docs/built-in/index/0064.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/0064.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/0115.regex b/tests/snapshots/docs/built-in/index/0115.regex
new file mode 100644
index 0000000..bfea61d
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/0115.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/0128.regex b/tests/snapshots/docs/built-in/index/0128.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/0128.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/0160.regex b/tests/snapshots/docs/built-in/index/0160.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/0160.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/0199.regex b/tests/snapshots/docs/built-in/index/0199.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/0199.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/0957.regex b/tests/snapshots/docs/built-in/index/0957.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/0957.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/0983.regex b/tests/snapshots/docs/built-in/index/0983.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/0983.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/1001.regex b/tests/snapshots/docs/built-in/index/1001.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/1001.regex
Binary files differ
diff --git a/tests/snapshots/docs/built-in/index/1017.regex b/tests/snapshots/docs/built-in/index/1017.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/built-in/index/1017.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0019.regex b/tests/snapshots/docs/regex-builder/builder/index/0019.regex
new file mode 100644
index 0000000..3a6abd5
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0019.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0044.regex b/tests/snapshots/docs/regex-builder/builder/index/0044.regex
new file mode 100644
index 0000000..fd99ebb
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0044.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0059.regex b/tests/snapshots/docs/regex-builder/builder/index/0059.regex
new file mode 100644
index 0000000..8f6a9c7
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0059.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0080.regex b/tests/snapshots/docs/regex-builder/builder/index/0080.regex
new file mode 100644
index 0000000..f8428de
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0080.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0103.regex b/tests/snapshots/docs/regex-builder/builder/index/0103.regex
new file mode 100644
index 0000000..1829d24
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0103.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0120.regex b/tests/snapshots/docs/regex-builder/builder/index/0120.regex
new file mode 100644
index 0000000..9cd58c7
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0120.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0139.regex b/tests/snapshots/docs/regex-builder/builder/index/0139.regex
new file mode 100644
index 0000000..df37fb5
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0139.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0156.regex b/tests/snapshots/docs/regex-builder/builder/index/0156.regex
new file mode 100644
index 0000000..b460d45
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0156.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0175.regex b/tests/snapshots/docs/regex-builder/builder/index/0175.regex
new file mode 100644
index 0000000..0c33843
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0175.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0189.regex b/tests/snapshots/docs/regex-builder/builder/index/0189.regex
new file mode 100644
index 0000000..9186a50
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0189.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0201.regex b/tests/snapshots/docs/regex-builder/builder/index/0201.regex
new file mode 100644
index 0000000..3734389
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0201.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0216.regex b/tests/snapshots/docs/regex-builder/builder/index/0216.regex
new file mode 100644
index 0000000..75e1c54
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0216.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0232.regex b/tests/snapshots/docs/regex-builder/builder/index/0232.regex
new file mode 100644
index 0000000..495a4a2
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0232.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0248.regex b/tests/snapshots/docs/regex-builder/builder/index/0248.regex
new file mode 100644
index 0000000..d51fcf6
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0248.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0265.regex b/tests/snapshots/docs/regex-builder/builder/index/0265.regex
new file mode 100644
index 0000000..d62f468
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0265.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0293.regex b/tests/snapshots/docs/regex-builder/builder/index/0293.regex
new file mode 100644
index 0000000..a7833cd
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0293.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0318.regex b/tests/snapshots/docs/regex-builder/builder/index/0318.regex
new file mode 100644
index 0000000..7b80739
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0318.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0347.regex b/tests/snapshots/docs/regex-builder/builder/index/0347.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0347.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0371.regex b/tests/snapshots/docs/regex-builder/builder/index/0371.regex
new file mode 100644
index 0000000..c8c8e22
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0371.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0421.regex b/tests/snapshots/docs/regex-builder/builder/index/0421.regex
new file mode 100644
index 0000000..d16376a
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0421.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0445.regex b/tests/snapshots/docs/regex-builder/builder/index/0445.regex
new file mode 100644
index 0000000..3a62450
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0445.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0469.regex b/tests/snapshots/docs/regex-builder/builder/index/0469.regex
new file mode 100644
index 0000000..7d9fa46
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0469.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0493.regex b/tests/snapshots/docs/regex-builder/builder/index/0493.regex
new file mode 100644
index 0000000..470123c
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0493.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0514.regex b/tests/snapshots/docs/regex-builder/builder/index/0514.regex
new file mode 100644
index 0000000..ad1e9ea
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0514.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0535.regex b/tests/snapshots/docs/regex-builder/builder/index/0535.regex
new file mode 100644
index 0000000..8b71c9d
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0535.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0554.regex b/tests/snapshots/docs/regex-builder/builder/index/0554.regex
new file mode 100644
index 0000000..7098f9c
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0554.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0573.regex b/tests/snapshots/docs/regex-builder/builder/index/0573.regex
new file mode 100644
index 0000000..b77a507
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0573.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0592.regex b/tests/snapshots/docs/regex-builder/builder/index/0592.regex
new file mode 100644
index 0000000..29e3ccc
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0592.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0611.regex b/tests/snapshots/docs/regex-builder/builder/index/0611.regex
new file mode 100644
index 0000000..d115de0
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0611.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0630.regex b/tests/snapshots/docs/regex-builder/builder/index/0630.regex
new file mode 100644
index 0000000..14e843f
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0630.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0649.regex b/tests/snapshots/docs/regex-builder/builder/index/0649.regex
new file mode 100644
index 0000000..5dbd654
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0649.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0668.regex b/tests/snapshots/docs/regex-builder/builder/index/0668.regex
new file mode 100644
index 0000000..1526551
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0668.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0687.regex b/tests/snapshots/docs/regex-builder/builder/index/0687.regex
new file mode 100644
index 0000000..51fc73e
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0687.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0706.regex b/tests/snapshots/docs/regex-builder/builder/index/0706.regex
new file mode 100644
index 0000000..5a1cd83
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0706.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0725.regex b/tests/snapshots/docs/regex-builder/builder/index/0725.regex
new file mode 100644
index 0000000..ffec79c
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0725.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0744.regex b/tests/snapshots/docs/regex-builder/builder/index/0744.regex
new file mode 100644
index 0000000..9321e99
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0744.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0762.regex b/tests/snapshots/docs/regex-builder/builder/index/0762.regex
new file mode 100644
index 0000000..3c348f5
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0762.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0780.regex b/tests/snapshots/docs/regex-builder/builder/index/0780.regex
new file mode 100644
index 0000000..b76ca12
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0780.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0798.regex b/tests/snapshots/docs/regex-builder/builder/index/0798.regex
new file mode 100644
index 0000000..09c76e6
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0798.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0816.regex b/tests/snapshots/docs/regex-builder/builder/index/0816.regex
new file mode 100644
index 0000000..2404b1d
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0816.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0834.regex b/tests/snapshots/docs/regex-builder/builder/index/0834.regex
new file mode 100644
index 0000000..cd4745e
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0834.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0852.regex b/tests/snapshots/docs/regex-builder/builder/index/0852.regex
new file mode 100644
index 0000000..cd83a68
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0852.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0892.regex b/tests/snapshots/docs/regex-builder/builder/index/0892.regex
new file mode 100644
index 0000000..57c8680
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0892.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0913.regex b/tests/snapshots/docs/regex-builder/builder/index/0913.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0913.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/builder/index/0942.regex b/tests/snapshots/docs/regex-builder/builder/index/0942.regex
new file mode 100644
index 0000000..a75fda3
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/builder/index/0942.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/flags/index/0026.regex b/tests/snapshots/docs/regex-builder/flags/index/0026.regex
new file mode 100644
index 0000000..2404b1d
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/flags/index/0026.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/flags/index/0038.regex b/tests/snapshots/docs/regex-builder/flags/index/0038.regex
new file mode 100644
index 0000000..2404b1d
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/flags/index/0038.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/flags/index/0051.regex b/tests/snapshots/docs/regex-builder/flags/index/0051.regex
new file mode 100644
index 0000000..2404b1d
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/flags/index/0051.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/flags/index/0065.regex b/tests/snapshots/docs/regex-builder/flags/index/0065.regex
new file mode 100644
index 0000000..2404b1d
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/flags/index/0065.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/flags/index/0080.regex b/tests/snapshots/docs/regex-builder/flags/index/0080.regex
new file mode 100644
index 0000000..2404b1d
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/flags/index/0080.regex
Binary files differ
diff --git a/tests/snapshots/docs/regex-builder/flags/index/0094.regex b/tests/snapshots/docs/regex-builder/flags/index/0094.regex
new file mode 100644
index 0000000..2404b1d
--- /dev/null
+++ b/tests/snapshots/docs/regex-builder/flags/index/0094.regex
Binary files differ
diff --git a/tests/snapshots/docs/upgrading/0.3-to-1.0/0018.regex b/tests/snapshots/docs/upgrading/0.3-to-1.0/0018.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/upgrading/0.3-to-1.0/0018.regex
Binary files differ
diff --git a/tests/snapshots/docs/upgrading/0.3-to-1.0/0037.regex b/tests/snapshots/docs/upgrading/0.3-to-1.0/0037.regex
new file mode 100644
index 0000000..4ba98bc
--- /dev/null
+++ b/tests/snapshots/docs/upgrading/0.3-to-1.0/0037.regex
Binary files differ
diff --git a/tests/snapshots/fixtures/alternation_with_overlapping_prefixes_redos.regex b/tests/snapshots/fixtures/alternation_with_overlapping_prefixes_redos.regex
new file mode 100644
index 0000000..8d3f7b5
--- /dev/null
+++ b/tests/snapshots/fixtures/alternation_with_overlapping_prefixes_redos.regex
@@ -0,0 +1 @@
+(?:aa|aaa|aaaa)+ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/combined_lookaround.regex b/tests/snapshots/fixtures/combined_lookaround.regex
new file mode 100644
index 0000000..baa7e8f
--- /dev/null
+++ b/tests/snapshots/fixtures/combined_lookaround.regex
@@ -0,0 +1 @@
+(?=\d)(?!0)(?<=\$)\d\d\d \ No newline at end of file
diff --git a/tests/snapshots/fixtures/deeply_nested_alternation.regex b/tests/snapshots/fixtures/deeply_nested_alternation.regex
new file mode 100644
index 0000000..0f2c26b
--- /dev/null
+++ b/tests/snapshots/fixtures/deeply_nested_alternation.regex
@@ -0,0 +1 @@
+^(?:abc|def|ghi)\-(?:first|second|third)$ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/email_shape_pattern.regex b/tests/snapshots/fixtures/email_shape_pattern.regex
new file mode 100644
index 0000000..bd17d4d
--- /dev/null
+++ b/tests/snapshots/fixtures/email_shape_pattern.regex
@@ -0,0 +1 @@
+^[a\-zA\-Z0\-9._%+-]+@[a\-zA\-Z0\-9.-]+\.[a-zA-Z]{2,}$ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/hex_color_pattern.regex b/tests/snapshots/fixtures/hex_color_pattern.regex
new file mode 100644
index 0000000..c2d63c0
--- /dev/null
+++ b/tests/snapshots/fixtures/hex_color_pattern.regex
@@ -0,0 +1 @@
+^\#[0123456789abcdefABCDEF]\w{6}$ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/named_backreference_pair.regex b/tests/snapshots/fixtures/named_backreference_pair.regex
new file mode 100644
index 0000000..1e3fe4a
--- /dev/null
+++ b/tests/snapshots/fixtures/named_backreference_pair.regex
@@ -0,0 +1 @@
+(?P<open>[\[\(\{])\w*(?P=open) \ No newline at end of file
diff --git a/tests/snapshots/fixtures/nested_unbounded_quantifier_redos.regex b/tests/snapshots/fixtures/nested_unbounded_quantifier_redos.regex
new file mode 100644
index 0000000..e5aaf8b
--- /dev/null
+++ b/tests/snapshots/fixtures/nested_unbounded_quantifier_redos.regex
@@ -0,0 +1 @@
+(?:\d+)+ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/pattern_composition_via_use.regex b/tests/snapshots/fixtures/pattern_composition_via_use.regex
new file mode 100644
index 0000000..70b3c7c
--- /dev/null
+++ b/tests/snapshots/fixtures/pattern_composition_via_use.regex
@@ -0,0 +1 @@
+^[a-zA-Z]{2,}$ \ No newline at end of file
diff --git a/tests/snapshots/library/abnf.regex b/tests/snapshots/library/abnf.regex
new file mode 100644
index 0000000..5603337
--- /dev/null
+++ b/tests/snapshots/library/abnf.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9_\-<>:=\|\*\+\?\(\)\[\]\{\}\.'"/;,]){4,65536}$ \ No newline at end of file
diff --git a/tests/snapshots/library/address.regex b/tests/snapshots/library/address.regex
new file mode 100644
index 0000000..53dbd11
--- /dev/null
+++ b/tests/snapshots/library/address.regex
@@ -0,0 +1 @@
+^\d+\s+(?:\s|[A-Za-z0-9.,'\-#/])+$ \ No newline at end of file
diff --git a/tests/snapshots/library/age.regex b/tests/snapshots/library/age.regex
new file mode 100644
index 0000000..e2638b9
--- /dev/null
+++ b/tests/snapshots/library/age.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9\+/=_\-\.:]){16,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/aircraft.regex b/tests/snapshots/library/aircraft.regex
new file mode 100644
index 0000000..61da29a
--- /dev/null
+++ b/tests/snapshots/library/aircraft.regex
@@ -0,0 +1 @@
+^[A-Z]{1,2}\-?[A-Z0-9]{1,5}$ \ No newline at end of file
diff --git a/tests/snapshots/library/alpha.regex b/tests/snapshots/library/alpha.regex
new file mode 100644
index 0000000..a1e4509
--- /dev/null
+++ b/tests/snapshots/library/alpha.regex
@@ -0,0 +1 @@
+^[a-zA-Z]+$ \ No newline at end of file
diff --git a/tests/snapshots/library/alphanumeric.regex b/tests/snapshots/library/alphanumeric.regex
new file mode 100644
index 0000000..6cf8d66
--- /dev/null
+++ b/tests/snapshots/library/alphanumeric.regex
@@ -0,0 +1 @@
+^[a-zA-Z0-9]+$ \ No newline at end of file
diff --git a/tests/snapshots/library/altitude.regex b/tests/snapshots/library/altitude.regex
new file mode 100644
index 0000000..c2a29b2
--- /dev/null
+++ b/tests/snapshots/library/altitude.regex
@@ -0,0 +1 @@
+^\-?\d+(?:\.\d+)?\s?(?:ft|km|mi|[m])?$ \ No newline at end of file
diff --git a/tests/snapshots/library/antlr.regex b/tests/snapshots/library/antlr.regex
new file mode 100644
index 0000000..e9b265e
--- /dev/null
+++ b/tests/snapshots/library/antlr.regex
@@ -0,0 +1 @@
+^grammar\s+[a-zA-Z][a-zA-Z0-9_]*;.*$ \ No newline at end of file
diff --git a/tests/snapshots/library/apache.regex b/tests/snapshots/library/apache.regex
new file mode 100644
index 0000000..19ae6bb
--- /dev/null
+++ b/tests/snapshots/library/apache.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+=\?\&\#:%\~]{2,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/apikey.regex b/tests/snapshots/library/apikey.regex
new file mode 100644
index 0000000..94b0e63
--- /dev/null
+++ b/tests/snapshots/library/apikey.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_-]{20,128}$ \ No newline at end of file
diff --git a/tests/snapshots/library/arn.regex b/tests/snapshots/library/arn.regex
new file mode 100644
index 0000000..cefdc2a
--- /dev/null
+++ b/tests/snapshots/library/arn.regex
@@ -0,0 +1 @@
+^arn:[a-z\-]+:[a-z0-9\-]+:[a-z0-9\-]*:\d*:.+$ \ No newline at end of file
diff --git a/tests/snapshots/library/arxiv.regex b/tests/snapshots/library/arxiv.regex
new file mode 100644
index 0000000..2d52c1a
--- /dev/null
+++ b/tests/snapshots/library/arxiv.regex
@@ -0,0 +1 @@
+^(?:\d{4}\.\d{4,5}(?:v\d+)?|[a-z]{2,10}(?:\.[A-Z]{2})?/\d{7}(?:v\d+)?)$ \ No newline at end of file
diff --git a/tests/snapshots/library/ascii.regex b/tests/snapshots/library/ascii.regex
new file mode 100644
index 0000000..6730c0b
--- /dev/null
+++ b/tests/snapshots/library/ascii.regex
@@ -0,0 +1 @@
+^[ -~]+$ \ No newline at end of file
diff --git a/tests/snapshots/library/asin.regex b/tests/snapshots/library/asin.regex
new file mode 100644
index 0000000..4b34df5
--- /dev/null
+++ b/tests/snapshots/library/asin.regex
@@ -0,0 +1 @@
+^[A-Z0-9]{10}$ \ No newline at end of file
diff --git a/tests/snapshots/library/atom.regex b/tests/snapshots/library/atom.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/atom.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/avro.regex b/tests/snapshots/library/avro.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/avro.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/barcode.regex b/tests/snapshots/library/barcode.regex
new file mode 100644
index 0000000..11fe7e0
--- /dev/null
+++ b/tests/snapshots/library/barcode.regex
@@ -0,0 +1 @@
+^[A-Z0-9]{6,48}$ \ No newline at end of file
diff --git a/tests/snapshots/library/base.regex b/tests/snapshots/library/base.regex
new file mode 100644
index 0000000..57871a4
--- /dev/null
+++ b/tests/snapshots/library/base.regex
@@ -0,0 +1 @@
+^(?:[0-9A-Fa-f]+|[A-Z2-7]+=*|[1-9A-HJ-NP-Za-km-z]+|[A-Za-z0-9\+/]+=*|[A-Za-z0-9_\-]+)$ \ No newline at end of file
diff --git a/tests/snapshots/library/bearer.regex b/tests/snapshots/library/bearer.regex
new file mode 100644
index 0000000..56cce08
--- /dev/null
+++ b/tests/snapshots/library/bearer.regex
@@ -0,0 +1 @@
+^Bearer [A-Za-z0-9._-]+$ \ No newline at end of file
diff --git a/tests/snapshots/library/bearing.regex b/tests/snapshots/library/bearing.regex
new file mode 100644
index 0000000..eede435
--- /dev/null
+++ b/tests/snapshots/library/bearing.regex
@@ -0,0 +1 @@
+^(?:360(?:\.0+)?|(?:3[0-5]\d|[1-2]\d\d|\d{1,2})(?:\.\d+)?)°?$ \ No newline at end of file
diff --git a/tests/snapshots/library/bic.regex b/tests/snapshots/library/bic.regex
new file mode 100644
index 0000000..5fea907
--- /dev/null
+++ b/tests/snapshots/library/bic.regex
@@ -0,0 +1 @@
+^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?$ \ No newline at end of file
diff --git a/tests/snapshots/library/blood.regex b/tests/snapshots/library/blood.regex
new file mode 100644
index 0000000..148cbbc
--- /dev/null
+++ b/tests/snapshots/library/blood.regex
@@ -0,0 +1 @@
+^(?:(?:AB|[ABO]))[+-]$ \ No newline at end of file
diff --git a/tests/snapshots/library/bnf.regex b/tests/snapshots/library/bnf.regex
new file mode 100644
index 0000000..5603337
--- /dev/null
+++ b/tests/snapshots/library/bnf.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9_\-<>:=\|\*\+\?\(\)\[\]\{\}\.'"/;,]){4,65536}$ \ No newline at end of file
diff --git a/tests/snapshots/library/bump.regex b/tests/snapshots/library/bump.regex
new file mode 100644
index 0000000..7fe8bdd
--- /dev/null
+++ b/tests/snapshots/library/bump.regex
@@ -0,0 +1 @@
+^(?:major|minor|patch|premajor|preminor|prepatch|prerelease|release)$ \ No newline at end of file
diff --git a/tests/snapshots/library/captcha.regex b/tests/snapshots/library/captcha.regex
new file mode 100644
index 0000000..19ae6bb
--- /dev/null
+++ b/tests/snapshots/library/captcha.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+=\?\&\#:%\~]{2,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/card.regex b/tests/snapshots/library/card.regex
new file mode 100644
index 0000000..6313318
--- /dev/null
+++ b/tests/snapshots/library/card.regex
@@ -0,0 +1 @@
+^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{1,7}$ \ No newline at end of file
diff --git a/tests/snapshots/library/cargo.regex b/tests/snapshots/library/cargo.regex
new file mode 100644
index 0000000..dc907b1
--- /dev/null
+++ b/tests/snapshots/library/cargo.regex
@@ -0,0 +1 @@
+^[a-zA-Z][a-zA-Z0-9_\-]{0,63}(?:@\d+(?:\.\d+){0,3}(?:[-.+][a-zA-Z0-9\.\-]+)?)?$ \ No newline at end of file
diff --git a/tests/snapshots/library/certificate.regex b/tests/snapshots/library/certificate.regex
new file mode 100644
index 0000000..e2638b9
--- /dev/null
+++ b/tests/snapshots/library/certificate.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9\+/=_\-\.:]){16,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/challenge.regex b/tests/snapshots/library/challenge.regex
new file mode 100644
index 0000000..b7faca2
--- /dev/null
+++ b/tests/snapshots/library/challenge.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_-]{16,128}$ \ No newline at end of file
diff --git a/tests/snapshots/library/charset.regex b/tests/snapshots/library/charset.regex
new file mode 100644
index 0000000..fe88a5c
--- /dev/null
+++ b/tests/snapshots/library/charset.regex
@@ -0,0 +1 @@
+^[a-zA-Z][a-zA-Z0-9_\+\.\-]{1,39}$ \ No newline at end of file
diff --git a/tests/snapshots/library/checksum.regex b/tests/snapshots/library/checksum.regex
new file mode 100644
index 0000000..9f6aed9
--- /dev/null
+++ b/tests/snapshots/library/checksum.regex
@@ -0,0 +1 @@
+^[a-fA-F0-9]{8,128}$ \ No newline at end of file
diff --git a/tests/snapshots/library/cidr.regex b/tests/snapshots/library/cidr.regex
new file mode 100644
index 0000000..21e454b
--- /dev/null
+++ b/tests/snapshots/library/cidr.regex
@@ -0,0 +1 @@
+^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}/(?:3[0-2]|[12]?\d)|(?:[0-9a-fA-F]{1,4}:){0,7}[0-9a-fA-F]{1,4}/(?:12[0-8]|1[01]\d|[1-9]?\d))$ \ No newline at end of file
diff --git a/tests/snapshots/library/codec.regex b/tests/snapshots/library/codec.regex
new file mode 100644
index 0000000..aeafca3
--- /dev/null
+++ b/tests/snapshots/library/codec.regex
@@ -0,0 +1 @@
+^[a-zA-Z][a-zA-Z0-9_\.\-]{1,29}$ \ No newline at end of file
diff --git a/tests/snapshots/library/color.regex b/tests/snapshots/library/color.regex
new file mode 100644
index 0000000..1a78673
--- /dev/null
+++ b/tests/snapshots/library/color.regex
@@ -0,0 +1 @@
+(?:^\#(?:[0-9A-Fa-f]{3,4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$|^rgba?\(\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?(?:\s*,\s*(?:\d|[\.])+)?\s*\)$|^hsla?\(\s*\d{1,3}(?:deg)?\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%(?:\s*,\s*(?:\d|[\.])+)?\s*\)$|^[a-zA-Z]{3,20}$) \ No newline at end of file
diff --git a/tests/snapshots/library/component.regex b/tests/snapshots/library/component.regex
new file mode 100644
index 0000000..99bdf80
--- /dev/null
+++ b/tests/snapshots/library/component.regex
@@ -0,0 +1 @@
+^(?:@[a-z0-9][a-z0-9\-]*/)?[a-z0-9][a-z0-9\._\-]{0,213}@\d+(?:\.\d+){0,3}(?:[-.+][a-zA-Z0-9\.\-]+)?$ \ No newline at end of file
diff --git a/tests/snapshots/library/cookie.regex b/tests/snapshots/library/cookie.regex
new file mode 100644
index 0000000..3c9e1ab
--- /dev/null
+++ b/tests/snapshots/library/cookie.regex
@@ -0,0 +1 @@
+^[a-zA-Z0-9_\-]+=(?:(?!(?:\s|[;])).)+$ \ No newline at end of file
diff --git a/tests/snapshots/library/coordinate.regex b/tests/snapshots/library/coordinate.regex
new file mode 100644
index 0000000..7a49f7a
--- /dev/null
+++ b/tests/snapshots/library/coordinate.regex
@@ -0,0 +1 @@
+^\-?(?:90(?:\.0+)?|[0-8]?\d(?:\.\d+)?)\s*,\s*\-?(?:180(?:\.0+)?|(?:1[0-7]\d|[0-9]?\d)(?:\.\d+)?)$ \ No newline at end of file
diff --git a/tests/snapshots/library/cron.regex b/tests/snapshots/library/cron.regex
new file mode 100644
index 0000000..80b24e1
--- /dev/null
+++ b/tests/snapshots/library/cron.regex
@@ -0,0 +1 @@
+^(?:@(?:(?:annually|yearly|monthly|weekly|daily|hourly|reboot))|(?:[\*\?0-9/,\-]+\s+){4,5}[\*\?0-9/,\-]+)$ \ No newline at end of file
diff --git a/tests/snapshots/library/crypto.regex b/tests/snapshots/library/crypto.regex
new file mode 100644
index 0000000..2b8beea
--- /dev/null
+++ b/tests/snapshots/library/crypto.regex
@@ -0,0 +1 @@
+^[A-Z0-9]{3,10}$ \ No newline at end of file
diff --git a/tests/snapshots/library/csp.regex b/tests/snapshots/library/csp.regex
new file mode 100644
index 0000000..dad51a5
--- /dev/null
+++ b/tests/snapshots/library/csp.regex
@@ -0,0 +1 @@
+^[a-z\-]+\s+(?:(?!;).)+(?:;\s*[a-z\-]+\s+(?:(?!;).)+)*;?$ \ No newline at end of file
diff --git a/tests/snapshots/library/csr.regex b/tests/snapshots/library/csr.regex
new file mode 100644
index 0000000..e2638b9
--- /dev/null
+++ b/tests/snapshots/library/csr.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9\+/=_\-\.:]){16,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/csrf.regex b/tests/snapshots/library/csrf.regex
new file mode 100644
index 0000000..5314f5b
--- /dev/null
+++ b/tests/snapshots/library/csrf.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_-]{32,128}$ \ No newline at end of file
diff --git a/tests/snapshots/library/csv.regex b/tests/snapshots/library/csv.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/csv.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/currency.regex b/tests/snapshots/library/currency.regex
new file mode 100644
index 0000000..c13f934
--- /dev/null
+++ b/tests/snapshots/library/currency.regex
@@ -0,0 +1 @@
+^[A-Z]{3}$ \ No newline at end of file
diff --git a/tests/snapshots/library/cusip.regex b/tests/snapshots/library/cusip.regex
new file mode 100644
index 0000000..6b9cb42
--- /dev/null
+++ b/tests/snapshots/library/cusip.regex
@@ -0,0 +1 @@
+^[A-Z0-9]{9}$ \ No newline at end of file
diff --git a/tests/snapshots/library/date.regex b/tests/snapshots/library/date.regex
new file mode 100644
index 0000000..dcdaa2d
--- /dev/null
+++ b/tests/snapshots/library/date.regex
@@ -0,0 +1 @@
+^(?:\d{1,2}/\d{1,2}/\d{4}|\d{4}\-\d{2}\-\d{2}|\d{2}\-\d{2}\-\d{4}|\d{4}/\d{2}/\d{2}|\d{1,2}\.\d{1,2}\.\d{4}|\d{4}\.\d{2}\.\d{2}|\d{8})$ \ No newline at end of file
diff --git a/tests/snapshots/library/datetime.regex b/tests/snapshots/library/datetime.regex
new file mode 100644
index 0000000..decfe4c
--- /dev/null
+++ b/tests/snapshots/library/datetime.regex
@@ -0,0 +1 @@
+^(?:\d{4}\-\d{2}\-\d{2}[Tt ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:(?:[+-]\d{2}:?\d{2}|[Zz]))?|\d{4}\d{2}\d{2}[Tt]\d{2}\d{2}\d{2}(?:(?:[+-]\d{4}|[Zz]))?)$ \ No newline at end of file
diff --git a/tests/snapshots/library/der.regex b/tests/snapshots/library/der.regex
new file mode 100644
index 0000000..e2638b9
--- /dev/null
+++ b/tests/snapshots/library/der.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9\+/=_\-\.:]){16,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/dicom.regex b/tests/snapshots/library/dicom.regex
new file mode 100644
index 0000000..3e88b2c
--- /dev/null
+++ b/tests/snapshots/library/dicom.regex
@@ -0,0 +1 @@
+^\d+(?:\.\d+)+$ \ No newline at end of file
diff --git a/tests/snapshots/library/did.regex b/tests/snapshots/library/did.regex
new file mode 100644
index 0000000..e62c684
--- /dev/null
+++ b/tests/snapshots/library/did.regex
@@ -0,0 +1 @@
+^did:[a-z0-9]+:.+$ \ No newline at end of file
diff --git a/tests/snapshots/library/digest.regex b/tests/snapshots/library/digest.regex
new file mode 100644
index 0000000..bc8a892
--- /dev/null
+++ b/tests/snapshots/library/digest.regex
@@ -0,0 +1 @@
+^(?:(?:sha256|sha512|sha1|md5|blake2[bs]?))[:-][a-fA-F0-9]{32,128}$ \ No newline at end of file
diff --git a/tests/snapshots/library/docker.regex b/tests/snapshots/library/docker.regex
new file mode 100644
index 0000000..ad5905e
--- /dev/null
+++ b/tests/snapshots/library/docker.regex
@@ -0,0 +1 @@
+^(?:(?:[a-z0-9\.\-]+(?::\d+)?/)?[a-z0-9]+(?:[._-][a-z0-9]+)*)(?:/[a-z0-9]+(?:[._-][a-z0-9]+)*)*(?::[a-zA-Z0-9_][a-zA-Z0-9\._\-]{0,127})?(?:@sha256:[a-f0-9]{64})?$ \ No newline at end of file
diff --git a/tests/snapshots/library/docx.regex b/tests/snapshots/library/docx.regex
new file mode 100644
index 0000000..10c8c17
--- /dev/null
+++ b/tests/snapshots/library/docx.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/]{1,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/doi.regex b/tests/snapshots/library/doi.regex
new file mode 100644
index 0000000..f999d1c
--- /dev/null
+++ b/tests/snapshots/library/doi.regex
@@ -0,0 +1 @@
+^10\.\d{4,9}/[\-\._;\(\)/:A-Za-z0-9]+$ \ No newline at end of file
diff --git a/tests/snapshots/library/domain.regex b/tests/snapshots/library/domain.regex
new file mode 100644
index 0000000..fac164f
--- /dev/null
+++ b/tests/snapshots/library/domain.regex
@@ -0,0 +1 @@
+^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$ \ No newline at end of file
diff --git a/tests/snapshots/library/dosage.regex b/tests/snapshots/library/dosage.regex
new file mode 100644
index 0000000..1e20a36
--- /dev/null
+++ b/tests/snapshots/library/dosage.regex
@@ -0,0 +1 @@
+^\d+(?:\.\d+)?\s?(?:mg|kg|ml|mcg|iu|[gl])(?:/(?:kg|day|dose))?$ \ No newline at end of file
diff --git a/tests/snapshots/library/duration.regex b/tests/snapshots/library/duration.regex
new file mode 100644
index 0000000..3fee4d7
--- /dev/null
+++ b/tests/snapshots/library/duration.regex
@@ -0,0 +1 @@
+^P(?=.)(?:\d+(?:\.\d+)?Y)?(?:\d+(?:\.\d+)?M)?(?:\d+(?:\.\d+)?W)?(?:\d+(?:\.\d+)?D)?(?:T(?=\d)(?:\d+(?:\.\d+)?H)?(?:\d+(?:\.\d+)?M)?(?:\d+(?:\.\d+)?S)?)?$ \ No newline at end of file
diff --git a/tests/snapshots/library/ebnf.regex b/tests/snapshots/library/ebnf.regex
new file mode 100644
index 0000000..5603337
--- /dev/null
+++ b/tests/snapshots/library/ebnf.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9_\-<>:=\|\*\+\?\(\)\[\]\{\}\.'"/;,]){4,65536}$ \ No newline at end of file
diff --git a/tests/snapshots/library/ein.regex b/tests/snapshots/library/ein.regex
new file mode 100644
index 0000000..954dcdd
--- /dev/null
+++ b/tests/snapshots/library/ein.regex
@@ -0,0 +1 @@
+^\d{2}\-\d{7}$ \ No newline at end of file
diff --git a/tests/snapshots/library/email.regex b/tests/snapshots/library/email.regex
new file mode 100644
index 0000000..9de9ad1
--- /dev/null
+++ b/tests/snapshots/library/email.regex
@@ -0,0 +1 @@
+^(?:(?:[a-z0-9!\#\$%\&'\*\+/=\?\^_`\{\|\}\~\-])+(?:\.(?:[a-z0-9!\#\$%\&'\*\+/=\?\^_`\{\|\}\~\-])+)*@(?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?|(?:(?:[a-z0-9!\#\$%\&'\*\+/=\?\^_`\{\|\}\~\-])+(?:\.(?:[a-z0-9!\#\$%\&'\*\+/=\?\^_`\{\|\}\~\-])+)*|"(?:(?:[-\ \ -!#-[]-]|\\[- \ \ -]))*")@(?:(?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|[a-z0-9\-]*[a-z0-9]:(?:(?:[-\ \ -!-ZS-]|\\[- \ \ -]))+)\]))$ \ No newline at end of file
diff --git a/tests/snapshots/library/emoji.regex b/tests/snapshots/library/emoji.regex
new file mode 100644
index 0000000..d6fee58
--- /dev/null
+++ b/tests/snapshots/library/emoji.regex
@@ -0,0 +1 @@
+^[🌀-🫿☀-➿]+$ \ No newline at end of file
diff --git a/tests/snapshots/library/encoding.regex b/tests/snapshots/library/encoding.regex
new file mode 100644
index 0000000..fe88a5c
--- /dev/null
+++ b/tests/snapshots/library/encoding.regex
@@ -0,0 +1 @@
+^[a-zA-Z][a-zA-Z0-9_\+\.\-]{1,39}$ \ No newline at end of file
diff --git a/tests/snapshots/library/epoch.regex b/tests/snapshots/library/epoch.regex
new file mode 100644
index 0000000..a812e41
--- /dev/null
+++ b/tests/snapshots/library/epoch.regex
@@ -0,0 +1 @@
+^\-?\d{1,10}$ \ No newline at end of file
diff --git a/tests/snapshots/library/epub.regex b/tests/snapshots/library/epub.regex
new file mode 100644
index 0000000..10c8c17
--- /dev/null
+++ b/tests/snapshots/library/epub.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/]{1,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/extension.regex b/tests/snapshots/library/extension.regex
new file mode 100644
index 0000000..98eba7a
--- /dev/null
+++ b/tests/snapshots/library/extension.regex
@@ -0,0 +1 @@
+^\.[a-zA-Z0-9]{1,10}$ \ No newline at end of file
diff --git a/tests/snapshots/library/favicon.regex b/tests/snapshots/library/favicon.regex
new file mode 100644
index 0000000..120fe27
--- /dev/null
+++ b/tests/snapshots/library/favicon.regex
Binary files differ
diff --git a/tests/snapshots/library/fax.regex b/tests/snapshots/library/fax.regex
new file mode 100644
index 0000000..c7664dd
--- /dev/null
+++ b/tests/snapshots/library/fax.regex
@@ -0,0 +1 @@
+^\+?\d{1,4}?(?:\s|[\-\.])?\(?\d{1,3}?\)?(?:\s|[\-\.])?\d{1,4}(?:\s|[\-\.])?\d{1,4}(?:\s|[\-\.])?\d{1,9}$ \ No newline at end of file
diff --git a/tests/snapshots/library/filename.regex b/tests/snapshots/library/filename.regex
new file mode 100644
index 0000000..41a024d
--- /dev/null
+++ b/tests/snapshots/library/filename.regex
Binary files differ
diff --git a/tests/snapshots/library/filter.regex b/tests/snapshots/library/filter.regex
new file mode 100644
index 0000000..c44a86b
--- /dev/null
+++ b/tests/snapshots/library/filter.regex
@@ -0,0 +1 @@
+^(?:blur|brightness|contrast|grayscale|hue\-rotate|invert|opacity|saturate|sepia|drop\-shadow)\([^)]+\)$ \ No newline at end of file
diff --git a/tests/snapshots/library/flight.regex b/tests/snapshots/library/flight.regex
new file mode 100644
index 0000000..4cb8094
--- /dev/null
+++ b/tests/snapshots/library/flight.regex
@@ -0,0 +1 @@
+^[A-Z]{2}\d{1,4}[A-Z]?$ \ No newline at end of file
diff --git a/tests/snapshots/library/fraction.regex b/tests/snapshots/library/fraction.regex
new file mode 100644
index 0000000..7b8bb4a
--- /dev/null
+++ b/tests/snapshots/library/fraction.regex
@@ -0,0 +1 @@
+^\-?(?:\d+\s+)?\d+/\d+$ \ No newline at end of file
diff --git a/tests/snapshots/library/geohash.regex b/tests/snapshots/library/geohash.regex
new file mode 100644
index 0000000..8bffb62
--- /dev/null
+++ b/tests/snapshots/library/geohash.regex
@@ -0,0 +1 @@
+^[0-9bcdefghjkmnpqrstuvwxyz]{1,12}$ \ No newline at end of file
diff --git a/tests/snapshots/library/git.regex b/tests/snapshots/library/git.regex
new file mode 100644
index 0000000..e01570c
--- /dev/null
+++ b/tests/snapshots/library/git.regex
@@ -0,0 +1 @@
+^[a-f0-9]{7,40}$ \ No newline at end of file
diff --git a/tests/snapshots/library/glob.regex b/tests/snapshots/library/glob.regex
new file mode 100644
index 0000000..07f0321
--- /dev/null
+++ b/tests/snapshots/library/glob.regex
Binary files differ
diff --git a/tests/snapshots/library/gradient.regex b/tests/snapshots/library/gradient.regex
new file mode 100644
index 0000000..fbf69ac
--- /dev/null
+++ b/tests/snapshots/library/gradient.regex
@@ -0,0 +1 @@
+^(?:linear|radial|conic)\-gradient\([^()]*(?:\([^()]*\)[^()]*)*\)$ \ No newline at end of file
diff --git a/tests/snapshots/library/graphql.regex b/tests/snapshots/library/graphql.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/graphql.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/gtin.regex b/tests/snapshots/library/gtin.regex
new file mode 100644
index 0000000..49ce8e0
--- /dev/null
+++ b/tests/snapshots/library/gtin.regex
@@ -0,0 +1 @@
+^(?:\d{8}|\d{12}|\d{13}|\d{14})$ \ No newline at end of file
diff --git a/tests/snapshots/library/guid.regex b/tests/snapshots/library/guid.regex
new file mode 100644
index 0000000..b3b223f
--- /dev/null
+++ b/tests/snapshots/library/guid.regex
@@ -0,0 +1 @@
+^\{?[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\}?$ \ No newline at end of file
diff --git a/tests/snapshots/library/hal.regex b/tests/snapshots/library/hal.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/hal.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/handle.regex b/tests/snapshots/library/handle.regex
new file mode 100644
index 0000000..66aad02
--- /dev/null
+++ b/tests/snapshots/library/handle.regex
@@ -0,0 +1 @@
+^@[a-zA-Z0-9_]{1,30}$ \ No newline at end of file
diff --git a/tests/snapshots/library/hash.regex b/tests/snapshots/library/hash.regex
new file mode 100644
index 0000000..9f6aed9
--- /dev/null
+++ b/tests/snapshots/library/hash.regex
@@ -0,0 +1 @@
+^[a-fA-F0-9]{8,128}$ \ No newline at end of file
diff --git a/tests/snapshots/library/hdf5.regex b/tests/snapshots/library/hdf5.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/hdf5.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/hmac.regex b/tests/snapshots/library/hmac.regex
new file mode 100644
index 0000000..eca0ba1
--- /dev/null
+++ b/tests/snapshots/library/hmac.regex
@@ -0,0 +1 @@
+^[0-9a-fA-F]{32,128}$ \ No newline at end of file
diff --git a/tests/snapshots/library/hostname.regex b/tests/snapshots/library/hostname.regex
new file mode 100644
index 0000000..0404c94
--- /dev/null
+++ b/tests/snapshots/library/hostname.regex
@@ -0,0 +1 @@
+^(?=.{1,253}$)(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$ \ No newline at end of file
diff --git a/tests/snapshots/library/htaccess.regex b/tests/snapshots/library/htaccess.regex
new file mode 100644
index 0000000..19ae6bb
--- /dev/null
+++ b/tests/snapshots/library/htaccess.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+=\?\&\#:%\~]{2,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/html.regex b/tests/snapshots/library/html.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/html.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/humans.regex b/tests/snapshots/library/humans.regex
new file mode 100644
index 0000000..19ae6bb
--- /dev/null
+++ b/tests/snapshots/library/humans.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+=\?\&\#:%\~]{2,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/iata.regex b/tests/snapshots/library/iata.regex
new file mode 100644
index 0000000..128cc16
--- /dev/null
+++ b/tests/snapshots/library/iata.regex
@@ -0,0 +1 @@
+^[A-Z]{2,3}$ \ No newline at end of file
diff --git a/tests/snapshots/library/iban.regex b/tests/snapshots/library/iban.regex
new file mode 100644
index 0000000..11efbae
--- /dev/null
+++ b/tests/snapshots/library/iban.regex
@@ -0,0 +1 @@
+^[A-Z]{2}\d{2}[A-Z0-9]{1,30}$ \ No newline at end of file
diff --git a/tests/snapshots/library/icao.regex b/tests/snapshots/library/icao.regex
new file mode 100644
index 0000000..e602cdb
--- /dev/null
+++ b/tests/snapshots/library/icao.regex
@@ -0,0 +1 @@
+^[A-Z]{3,4}$ \ No newline at end of file
diff --git a/tests/snapshots/library/iccid.regex b/tests/snapshots/library/iccid.regex
new file mode 100644
index 0000000..0ba451b
--- /dev/null
+++ b/tests/snapshots/library/iccid.regex
@@ -0,0 +1 @@
+^\d{19,22}$ \ No newline at end of file
diff --git a/tests/snapshots/library/image.regex b/tests/snapshots/library/image.regex
new file mode 100644
index 0000000..ad5905e
--- /dev/null
+++ b/tests/snapshots/library/image.regex
@@ -0,0 +1 @@
+^(?:(?:[a-z0-9\.\-]+(?::\d+)?/)?[a-z0-9]+(?:[._-][a-z0-9]+)*)(?:/[a-z0-9]+(?:[._-][a-z0-9]+)*)*(?::[a-zA-Z0-9_][a-zA-Z0-9\._\-]{0,127})?(?:@sha256:[a-f0-9]{64})?$ \ No newline at end of file
diff --git a/tests/snapshots/library/imei.regex b/tests/snapshots/library/imei.regex
new file mode 100644
index 0000000..698dfa9
--- /dev/null
+++ b/tests/snapshots/library/imei.regex
@@ -0,0 +1 @@
+^\d{15}$ \ No newline at end of file
diff --git a/tests/snapshots/library/imo.regex b/tests/snapshots/library/imo.regex
new file mode 100644
index 0000000..813a08d
--- /dev/null
+++ b/tests/snapshots/library/imo.regex
@@ -0,0 +1 @@
+^IMO\d{7}$ \ No newline at end of file
diff --git a/tests/snapshots/library/ini.regex b/tests/snapshots/library/ini.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/ini.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/integer.regex b/tests/snapshots/library/integer.regex
new file mode 100644
index 0000000..41efab1
--- /dev/null
+++ b/tests/snapshots/library/integer.regex
@@ -0,0 +1 @@
+^[+-]?\d+$ \ No newline at end of file
diff --git a/tests/snapshots/library/interval.regex b/tests/snapshots/library/interval.regex
new file mode 100644
index 0000000..421dee4
--- /dev/null
+++ b/tests/snapshots/library/interval.regex
@@ -0,0 +1 @@
+^\d{4}\-\d{2}\-\d{2}[Tt ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:(?:[+-]\d{2}:?\d{2}|[Zz]))?/(?:\d{4}\-\d{2}\-\d{2}[Tt ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:(?:[+-]\d{2}:?\d{2}|[Zz]))?|P(?=.)(?:\d+(?:\.\d+)?Y)?(?:\d+(?:\.\d+)?M)?(?:\d+(?:\.\d+)?W)?(?:\d+(?:\.\d+)?D)?(?:T(?=\d)(?:\d+(?:\.\d+)?H)?(?:\d+(?:\.\d+)?M)?(?:\d+(?:\.\d+)?S)?)?)$ \ No newline at end of file
diff --git a/tests/snapshots/library/ip.regex b/tests/snapshots/library/ip.regex
new file mode 100644
index 0000000..8b242c2
--- /dev/null
+++ b/tests/snapshots/library/ip.regex
@@ -0,0 +1 @@
+^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?:(?:(?:[0-9a-fA-F]){1,4}:){7}(?:[0-9a-fA-F]){1,4}|(?:(?:[0-9a-fA-F]){1,4}:){1,7}:|(?:(?:[0-9a-fA-F]){1,4}:){1,6}:(?:[0-9a-fA-F]){1,4}|(?:(?:[0-9a-fA-F]){1,4}:){1,5}(?::(?:[0-9a-fA-F]){1,4}){1,2}|(?:(?:[0-9a-fA-F]){1,4}:){1,4}(?::(?:[0-9a-fA-F]){1,4}){1,3}|(?:(?:[0-9a-fA-F]){1,4}:){1,3}(?::(?:[0-9a-fA-F]){1,4}){1,4}|(?:(?:[0-9a-fA-F]){1,4}:){1,2}(?::(?:[0-9a-fA-F]){1,4}){1,5}|(?:[0-9a-fA-F]){1,4}:(?:(?::(?:[0-9a-fA-F]){1,4}){1,6})|:(?:(?:(?::(?:[0-9a-fA-F]){1,4}){1,7}|[:]))|fe80:(?::(?:[0-9a-fA-F]){0,4}){0,4}%[0-9a-zA-Z]+|::(?:ffff(?::0{1,4})?:)?(?:(?:25[0-5]|(?:(?:2[0-4]|1?\d))?\d)\.){3}(?:25[0-5]|(?:(?:2[0-4]|1?\d))?\d)|(?:(?:[0-9a-fA-F]){1,4}:){1,4}:(?:(?:25[0-5]|(?:(?:2[0-4]|1?\d))?\d)\.){3}(?:25[0-5]|(?:(?:2[0-4]|1?\d))?\d)))$ \ No newline at end of file
diff --git a/tests/snapshots/library/isbn.regex b/tests/snapshots/library/isbn.regex
new file mode 100644
index 0000000..9f4075c
--- /dev/null
+++ b/tests/snapshots/library/isbn.regex
@@ -0,0 +1 @@
+(?:^(?:\d[- ]?){9}(?:\d|[Xx])$|^(?:\d[- ]?){12}\d$) \ No newline at end of file
diff --git a/tests/snapshots/library/isin.regex b/tests/snapshots/library/isin.regex
new file mode 100644
index 0000000..a3c7b42
--- /dev/null
+++ b/tests/snapshots/library/isin.regex
@@ -0,0 +1 @@
+^[A-Z]{2}[A-Z0-9]{9}\d$ \ No newline at end of file
diff --git a/tests/snapshots/library/issn.regex b/tests/snapshots/library/issn.regex
new file mode 100644
index 0000000..98898d4
--- /dev/null
+++ b/tests/snapshots/library/issn.regex
@@ -0,0 +1 @@
+^\d{4}\-\d{3}(?:\d|[Xx])$ \ No newline at end of file
diff --git a/tests/snapshots/library/itin.regex b/tests/snapshots/library/itin.regex
new file mode 100644
index 0000000..3d025b6
--- /dev/null
+++ b/tests/snapshots/library/itin.regex
@@ -0,0 +1 @@
+^9\d{2}\-(?:5\d|6[0-5]|7\d|8[0-8]|9[0-2]|9[4-9])\-\d{4}$ \ No newline at end of file
diff --git a/tests/snapshots/library/json.regex b/tests/snapshots/library/json.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/json.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/jsonapi.regex b/tests/snapshots/library/jsonapi.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/jsonapi.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/jwt.regex b/tests/snapshots/library/jwt.regex
new file mode 100644
index 0000000..1cb5316
--- /dev/null
+++ b/tests/snapshots/library/jwt.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$ \ No newline at end of file
diff --git a/tests/snapshots/library/keyring.regex b/tests/snapshots/library/keyring.regex
new file mode 100644
index 0000000..e2638b9
--- /dev/null
+++ b/tests/snapshots/library/keyring.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9\+/=_\-\.:]){16,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/lei.regex b/tests/snapshots/library/lei.regex
new file mode 100644
index 0000000..5af6936
--- /dev/null
+++ b/tests/snapshots/library/lei.regex
@@ -0,0 +1 @@
+^[A-Z0-9]{20}$ \ No newline at end of file
diff --git a/tests/snapshots/library/locale.regex b/tests/snapshots/library/locale.regex
new file mode 100644
index 0000000..fbb7dbb
--- /dev/null
+++ b/tests/snapshots/library/locale.regex
@@ -0,0 +1 @@
+^[a-z]{2,3}(?:[_-][A-Z]{2})?(?:\.[a-zA-Z0-9\-]+)?(?:@[a-zA-Z0-9]+)?$ \ No newline at end of file
diff --git a/tests/snapshots/library/mac.regex b/tests/snapshots/library/mac.regex
new file mode 100644
index 0000000..cd424ba
--- /dev/null
+++ b/tests/snapshots/library/mac.regex
@@ -0,0 +1 @@
+^(?:[0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$ \ No newline at end of file
diff --git a/tests/snapshots/library/makefile.regex b/tests/snapshots/library/makefile.regex
new file mode 100644
index 0000000..b4b27c3
--- /dev/null
+++ b/tests/snapshots/library/makefile.regex
@@ -0,0 +1 @@
+^\.?[a-zA-Z][a-zA-Z0-9\._\-]*(?:\s+[a-zA-Z][a-zA-Z0-9\._\-]*)*\s*:.*$ \ No newline at end of file
diff --git a/tests/snapshots/library/manifest.regex b/tests/snapshots/library/manifest.regex
new file mode 100644
index 0000000..19ae6bb
--- /dev/null
+++ b/tests/snapshots/library/manifest.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+=\?\&\#:%\~]{2,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/medical.regex b/tests/snapshots/library/medical.regex
new file mode 100644
index 0000000..baf2b6d
--- /dev/null
+++ b/tests/snapshots/library/medical.regex
@@ -0,0 +1 @@
+^(?:\d{6,18}|[A-TV-Z]\d[A-Z0-9](?:\.[A-Z0-9]{1,4})?|\d{10}|\d{1,7}\-\d)$ \ No newline at end of file
diff --git a/tests/snapshots/library/meid.regex b/tests/snapshots/library/meid.regex
new file mode 100644
index 0000000..2301792
--- /dev/null
+++ b/tests/snapshots/library/meid.regex
@@ -0,0 +1 @@
+^[0-9A-F]{14}$ \ No newline at end of file
diff --git a/tests/snapshots/library/mfa.regex b/tests/snapshots/library/mfa.regex
new file mode 100644
index 0000000..e9f4fe8
--- /dev/null
+++ b/tests/snapshots/library/mfa.regex
@@ -0,0 +1 @@
+^\d{6,8}$ \ No newline at end of file
diff --git a/tests/snapshots/library/mgrs.regex b/tests/snapshots/library/mgrs.regex
new file mode 100644
index 0000000..da7bba1
--- /dev/null
+++ b/tests/snapshots/library/mgrs.regex
@@ -0,0 +1 @@
+^\d{1,2}[C-HJ-NP-X][A-Z]{2}(?:\d{2}|\d{4}|\d{6}|\d{8}|\d{10})$ \ No newline at end of file
diff --git a/tests/snapshots/library/mimetype.regex b/tests/snapshots/library/mimetype.regex
new file mode 100644
index 0000000..757298f
--- /dev/null
+++ b/tests/snapshots/library/mimetype.regex
@@ -0,0 +1 @@
+^[a-zA-Z][a-zA-Z0-9!\#\$\&\-\^_\.\+]*/[a-zA-Z][a-zA-Z0-9!\#\$\&\-\^_\.\+]*$ \ No newline at end of file
diff --git a/tests/snapshots/library/mmsi.regex b/tests/snapshots/library/mmsi.regex
new file mode 100644
index 0000000..cb08452
--- /dev/null
+++ b/tests/snapshots/library/mmsi.regex
@@ -0,0 +1 @@
+^\d{9}$ \ No newline at end of file
diff --git a/tests/snapshots/library/mnemonic.regex b/tests/snapshots/library/mnemonic.regex
new file mode 100644
index 0000000..6c20a6d
--- /dev/null
+++ b/tests/snapshots/library/mnemonic.regex
@@ -0,0 +1 @@
+^(?:[a-z]+ ){11,23}[a-z]+$ \ No newline at end of file
diff --git a/tests/snapshots/library/mobi.regex b/tests/snapshots/library/mobi.regex
new file mode 100644
index 0000000..10c8c17
--- /dev/null
+++ b/tests/snapshots/library/mobi.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/]{1,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/mpn.regex b/tests/snapshots/library/mpn.regex
new file mode 100644
index 0000000..db8b18d
--- /dev/null
+++ b/tests/snapshots/library/mpn.regex
@@ -0,0 +1 @@
+^[A-Z0-9][A-Z0-9\-_\.]{1,63}$ \ No newline at end of file
diff --git a/tests/snapshots/library/msgpack.regex b/tests/snapshots/library/msgpack.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/msgpack.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/natural.regex b/tests/snapshots/library/natural.regex
new file mode 100644
index 0000000..41df73d
--- /dev/null
+++ b/tests/snapshots/library/natural.regex
@@ -0,0 +1 @@
+^[1-9]\d*$ \ No newline at end of file
diff --git a/tests/snapshots/library/nginx.regex b/tests/snapshots/library/nginx.regex
new file mode 100644
index 0000000..19ae6bb
--- /dev/null
+++ b/tests/snapshots/library/nginx.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+=\?\&\#:%\~]{2,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/nonce.regex b/tests/snapshots/library/nonce.regex
new file mode 100644
index 0000000..6f12a7c
--- /dev/null
+++ b/tests/snapshots/library/nonce.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9\+/=_\-]{16,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/number.regex b/tests/snapshots/library/number.regex
new file mode 100644
index 0000000..743f037
--- /dev/null
+++ b/tests/snapshots/library/number.regex
@@ -0,0 +1 @@
+^(?:[+-]?\d+|[+-]?\d+\.\d+|[+-]?\.\d+|[+-]?\d+\.\d*[eE][+-]?\d+|[+-]?\d+[eE][+-]?\d+|0[xX][0-9a-fA-F]+|0[oO][0-7]+|0[bB][01]+|[+-]?\d+(?:\.\d+)?[+-]\d+(?:\.\d+)?[jJi])$ \ No newline at end of file
diff --git a/tests/snapshots/library/numeric.regex b/tests/snapshots/library/numeric.regex
new file mode 100644
index 0000000..665d92c
--- /dev/null
+++ b/tests/snapshots/library/numeric.regex
@@ -0,0 +1 @@
+^\d+$ \ No newline at end of file
diff --git a/tests/snapshots/library/oauth.regex b/tests/snapshots/library/oauth.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/oauth.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/odt.regex b/tests/snapshots/library/odt.regex
new file mode 100644
index 0000000..10c8c17
--- /dev/null
+++ b/tests/snapshots/library/odt.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/]{1,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/offset.regex b/tests/snapshots/library/offset.regex
new file mode 100644
index 0000000..0b7cc67
--- /dev/null
+++ b/tests/snapshots/library/offset.regex
@@ -0,0 +1 @@
+^(?:[+-](?:0\d|1[0-4]):?[0-5]\d|[Z])$ \ No newline at end of file
diff --git a/tests/snapshots/library/openapi.regex b/tests/snapshots/library/openapi.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/openapi.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/openid.regex b/tests/snapshots/library/openid.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/openid.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/orc.regex b/tests/snapshots/library/orc.regex
new file mode 100644
index 0000000..6e06a79
--- /dev/null
+++ b/tests/snapshots/library/orc.regex
@@ -0,0 +1 @@
+^ORC.*$ \ No newline at end of file
diff --git a/tests/snapshots/library/orcid.regex b/tests/snapshots/library/orcid.regex
new file mode 100644
index 0000000..b80cabc
--- /dev/null
+++ b/tests/snapshots/library/orcid.regex
@@ -0,0 +1 @@
+^\d{4}\-\d{4}\-\d{4}\-\d{3}(?:\d|[X])$ \ No newline at end of file
diff --git a/tests/snapshots/library/ordinal.regex b/tests/snapshots/library/ordinal.regex
new file mode 100644
index 0000000..3e573ff
--- /dev/null
+++ b/tests/snapshots/library/ordinal.regex
@@ -0,0 +1 @@
+^\d+(?:(?:st|nd|rd|th))$ \ No newline at end of file
diff --git a/tests/snapshots/library/otp.regex b/tests/snapshots/library/otp.regex
new file mode 100644
index 0000000..2310308
--- /dev/null
+++ b/tests/snapshots/library/otp.regex
@@ -0,0 +1 @@
+(?:^\d{6,8}$|^[A-Z0-9]{6,8}$) \ No newline at end of file
diff --git a/tests/snapshots/library/package.regex b/tests/snapshots/library/package.regex
new file mode 100644
index 0000000..a7dc1f6
--- /dev/null
+++ b/tests/snapshots/library/package.regex
@@ -0,0 +1 @@
+^(?:@[a-z0-9][a-z0-9\-]*/)?[a-z0-9][a-z0-9\._\-]{0,213}$ \ No newline at end of file
diff --git a/tests/snapshots/library/pager.regex b/tests/snapshots/library/pager.regex
new file mode 100644
index 0000000..504218d
--- /dev/null
+++ b/tests/snapshots/library/pager.regex
@@ -0,0 +1 @@
+^\d{4,10}$ \ No newline at end of file
diff --git a/tests/snapshots/library/palette.regex b/tests/snapshots/library/palette.regex
new file mode 100644
index 0000000..a011f9d
--- /dev/null
+++ b/tests/snapshots/library/palette.regex
@@ -0,0 +1 @@
+^(?:(?:\#[0-9A-Fa-f]{3,8})|[a-zA-Z]{3,20})(?:\s*,\s*(?:(?:\#[0-9A-Fa-f]{3,8})|[a-zA-Z]{3,20})){1,15}$ \ No newline at end of file
diff --git a/tests/snapshots/library/parquet.regex b/tests/snapshots/library/parquet.regex
new file mode 100644
index 0000000..5f6b0b3
--- /dev/null
+++ b/tests/snapshots/library/parquet.regex
@@ -0,0 +1 @@
+^PAR1.*$ \ No newline at end of file
diff --git a/tests/snapshots/library/passkey.regex b/tests/snapshots/library/passkey.regex
new file mode 100644
index 0000000..5da3fc6
--- /dev/null
+++ b/tests/snapshots/library/passkey.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_-]{22,512}$ \ No newline at end of file
diff --git a/tests/snapshots/library/password.regex b/tests/snapshots/library/password.regex
new file mode 100644
index 0000000..d1c63d3
--- /dev/null
+++ b/tests/snapshots/library/password.regex
@@ -0,0 +1 @@
+(?:) \ No newline at end of file
diff --git a/tests/snapshots/library/path.regex b/tests/snapshots/library/path.regex
new file mode 100644
index 0000000..41b80f8
--- /dev/null
+++ b/tests/snapshots/library/path.regex
Binary files differ
diff --git a/tests/snapshots/library/pdf.regex b/tests/snapshots/library/pdf.regex
new file mode 100644
index 0000000..10c8c17
--- /dev/null
+++ b/tests/snapshots/library/pdf.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/]{1,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/peg.regex b/tests/snapshots/library/peg.regex
new file mode 100644
index 0000000..5603337
--- /dev/null
+++ b/tests/snapshots/library/peg.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9_\-<>:=\|\*\+\?\(\)\[\]\{\}\.'"/;,]){4,65536}$ \ No newline at end of file
diff --git a/tests/snapshots/library/pem.regex b/tests/snapshots/library/pem.regex
new file mode 100644
index 0000000..e2638b9
--- /dev/null
+++ b/tests/snapshots/library/pem.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9\+/=_\-\.:]){16,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/percentage.regex b/tests/snapshots/library/percentage.regex
new file mode 100644
index 0000000..7389873
--- /dev/null
+++ b/tests/snapshots/library/percentage.regex
@@ -0,0 +1 @@
+^\-?\d+(?:\.\d+)?\s?%$ \ No newline at end of file
diff --git a/tests/snapshots/library/pest.regex b/tests/snapshots/library/pest.regex
new file mode 100644
index 0000000..5603337
--- /dev/null
+++ b/tests/snapshots/library/pest.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9_\-<>:=\|\*\+\?\(\)\[\]\{\}\.'"/;,]){4,65536}$ \ No newline at end of file
diff --git a/tests/snapshots/library/pgp.regex b/tests/snapshots/library/pgp.regex
new file mode 100644
index 0000000..e2638b9
--- /dev/null
+++ b/tests/snapshots/library/pgp.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9\+/=_\-\.:]){16,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/phone.regex b/tests/snapshots/library/phone.regex
new file mode 100644
index 0000000..609f322
--- /dev/null
+++ b/tests/snapshots/library/phone.regex
@@ -0,0 +1 @@
+^(?:\+?\d{1,4}?(?:\s|[\-\.])?\(?\d{1,3}?\)?(?:\s|[\-\.])?\d{1,4}(?:\s|[\-\.])?\d{1,4}(?:\s|[\-\.])?\d{1,9}|\d{2,4})$ \ No newline at end of file
diff --git a/tests/snapshots/library/pin.regex b/tests/snapshots/library/pin.regex
new file mode 100644
index 0000000..2aadeb2
--- /dev/null
+++ b/tests/snapshots/library/pin.regex
@@ -0,0 +1 @@
+^\d{4,12}$ \ No newline at end of file
diff --git a/tests/snapshots/library/place.regex b/tests/snapshots/library/place.regex
new file mode 100644
index 0000000..2388960
--- /dev/null
+++ b/tests/snapshots/library/place.regex
@@ -0,0 +1 @@
+^[a-zA-Z][A-Za-z \.,'\-]{1,99}$ \ No newline at end of file
diff --git a/tests/snapshots/library/plate.regex b/tests/snapshots/library/plate.regex
new file mode 100644
index 0000000..5e7ced9
--- /dev/null
+++ b/tests/snapshots/library/plate.regex
@@ -0,0 +1 @@
+^[A-Z0-9]{1,3}[- ]?[A-Z0-9]{1,4}$ \ No newline at end of file
diff --git a/tests/snapshots/library/plus.regex b/tests/snapshots/library/plus.regex
new file mode 100644
index 0000000..1ff22f5
--- /dev/null
+++ b/tests/snapshots/library/plus.regex
@@ -0,0 +1 @@
+^[23456789CFGHJMPQRVWX]{2,8}\+[23456789CFGHJMPQRVWX]{2,3}(?:\s+.+)?$ \ No newline at end of file
diff --git a/tests/snapshots/library/pmc.regex b/tests/snapshots/library/pmc.regex
new file mode 100644
index 0000000..054d7c6
--- /dev/null
+++ b/tests/snapshots/library/pmc.regex
@@ -0,0 +1 @@
+^PMC\d{1,9}$ \ No newline at end of file
diff --git a/tests/snapshots/library/pmid.regex b/tests/snapshots/library/pmid.regex
new file mode 100644
index 0000000..11dcb2a
--- /dev/null
+++ b/tests/snapshots/library/pmid.regex
@@ -0,0 +1 @@
+^\d{1,8}$ \ No newline at end of file
diff --git a/tests/snapshots/library/port.regex b/tests/snapshots/library/port.regex
new file mode 100644
index 0000000..31f4234
--- /dev/null
+++ b/tests/snapshots/library/port.regex
@@ -0,0 +1 @@
+(?:^6553[0-5]$|^655[0-2]\d$|^65[0-4]\d{2}$|^6[0-4]\d{3}$|^[1-5]\d{4}$|^[1-9]\d{0,3}$|^0$) \ No newline at end of file
diff --git a/tests/snapshots/library/postal.regex b/tests/snapshots/library/postal.regex
new file mode 100644
index 0000000..fde788d
--- /dev/null
+++ b/tests/snapshots/library/postal.regex
@@ -0,0 +1 @@
+^(?:(?:(?:120|122))\d{2}|(?:NL\-)?\d{4}\s*[A-Z]{2}|(?:(?:[AC-FHKNPRTV-Y]\d{2}|D6W))[ -]?[0-9AC-FHKNPRTV-Y]{4}|(?:GIR 0AA|(?:[A-Za-z]\d{1,2}|[A-Za-z][A-HJ-Ya-hj-y]\d{1,2}|[A-Za-z]\d[A-Za-z]|[A-Za-z][A-HJ-Ya-hj-y]\d(?:[A-Za-z])?)\s?\d(?:[A-Za-z]){2})|96950|971\d{2}|972\d{2}|973\d{2}|974\d{2}|976\d{2}|980\d{2}|987\d{2}|988\d{2}|AI\-2640|BB\d{5}|FIQQ 1ZZ|GX11 1AA|LT\-\d{5}|LV\-\d{4}|MD\-?\d{4}|MSR \d{4}|STHL 1ZZ|TKCA 1ZZ|VC\d{4}|VG\d{4}|WS\d{4}|\d{2}\-\d{3}|\d{3}\s\d{2}|\d{3}|\d{3}(?:\-\d{2})?|\d{3}\-\d{4}|\d{4}\s\d{4}|\d{4}|\d{4}(?:\-[A-Z])?|\d{4}\-\d{3}|\d{5}|\d{5}(?:\-\d{4})?|\d{5}\-\d{3}|(?:\d{5}|\d{7})|\d{6}|\d{7}|[A-Z]\d[A-Z]\s?\d[A-Z]\d|[A-Z]\d{3}|[A-Z]\d{4}[A-Z]{3}|[A-Z]{2}\s\d{5}|[A-Z]{2}\d\-\d{4}|[A-Z]{2}\d{2}\s\d{3}|[A-Z]{2}\d{2}|[A-Z]{2}\d{4}|[A-Z]{3}\s\d{4}|\d{5}(?:[-](?:\s|[\-])\d{4})?|(?!0)\d{6})$ \ No newline at end of file
diff --git a/tests/snapshots/library/pptx.regex b/tests/snapshots/library/pptx.regex
new file mode 100644
index 0000000..10c8c17
--- /dev/null
+++ b/tests/snapshots/library/pptx.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/]{1,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/printable.regex b/tests/snapshots/library/printable.regex
new file mode 100644
index 0000000..1a32025
--- /dev/null
+++ b/tests/snapshots/library/printable.regex
Binary files differ
diff --git a/tests/snapshots/library/protobuf.regex b/tests/snapshots/library/protobuf.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/protobuf.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/ptr.regex b/tests/snapshots/library/ptr.regex
new file mode 100644
index 0000000..2fe090e
--- /dev/null
+++ b/tests/snapshots/library/ptr.regex
@@ -0,0 +1 @@
+^(?:(?:\d{1,3}\.){4}in\-addr\.arpa\.?|(?:[0-9a-fA-F]\.){32}ip6\.arpa\.?)$ \ No newline at end of file
diff --git a/tests/snapshots/library/ratio.regex b/tests/snapshots/library/ratio.regex
new file mode 100644
index 0000000..e6de6b8
--- /dev/null
+++ b/tests/snapshots/library/ratio.regex
@@ -0,0 +1 @@
+^\d+:\d+$ \ No newline at end of file
diff --git a/tests/snapshots/library/readme.regex b/tests/snapshots/library/readme.regex
new file mode 100644
index 0000000..10c8c17
--- /dev/null
+++ b/tests/snapshots/library/readme.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/]{1,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/ref.regex b/tests/snapshots/library/ref.regex
new file mode 100644
index 0000000..ccc006c
--- /dev/null
+++ b/tests/snapshots/library/ref.regex
@@ -0,0 +1 @@
+^(?:[a-f0-9]{7,40}|refs/(?:(?:heads|tags|remotes))/(?:(?!(?:\s|[~^:?*[\\])).)+|(?!(?:\s|[~^:?*[\\/])).(?:(?!(?:\s|[~^:?*[\\])).){0,127})$ \ No newline at end of file
diff --git a/tests/snapshots/library/refresh.regex b/tests/snapshots/library/refresh.regex
new file mode 100644
index 0000000..aadc171
--- /dev/null
+++ b/tests/snapshots/library/refresh.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9._\-~+/=]{32,512}$ \ No newline at end of file
diff --git a/tests/snapshots/library/regex.regex b/tests/snapshots/library/regex.regex
new file mode 100644
index 0000000..d1c63d3
--- /dev/null
+++ b/tests/snapshots/library/regex.regex
@@ -0,0 +1 @@
+(?:) \ No newline at end of file
diff --git a/tests/snapshots/library/robots.regex b/tests/snapshots/library/robots.regex
new file mode 100644
index 0000000..19ae6bb
--- /dev/null
+++ b/tests/snapshots/library/robots.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+=\?\&\#:%\~]{2,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/roman.regex b/tests/snapshots/library/roman.regex
new file mode 100644
index 0000000..71b7922
--- /dev/null
+++ b/tests/snapshots/library/roman.regex
@@ -0,0 +1 @@
+^M{0,3}(?:(?:CM|CD|D?C{0,3}))(?:(?:XC|XL|L?X{0,3}))(?:(?:IX|IV|V?I{0,3}))$ \ No newline at end of file
diff --git a/tests/snapshots/library/routing.regex b/tests/snapshots/library/routing.regex
new file mode 100644
index 0000000..cb08452
--- /dev/null
+++ b/tests/snapshots/library/routing.regex
@@ -0,0 +1 @@
+^\d{9}$ \ No newline at end of file
diff --git a/tests/snapshots/library/rss.regex b/tests/snapshots/library/rss.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/rss.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/rtf.regex b/tests/snapshots/library/rtf.regex
new file mode 100644
index 0000000..76fca38
--- /dev/null
+++ b/tests/snapshots/library/rtf.regex
@@ -0,0 +1 @@
+^\{\\rtf\d?.*$ \ No newline at end of file
diff --git a/tests/snapshots/library/saml.regex b/tests/snapshots/library/saml.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/saml.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/scientific.regex b/tests/snapshots/library/scientific.regex
new file mode 100644
index 0000000..9083fb5
--- /dev/null
+++ b/tests/snapshots/library/scientific.regex
@@ -0,0 +1 @@
+^[+-]?\d+(?:\.\d+)?[eE][+-]?\d+$ \ No newline at end of file
diff --git a/tests/snapshots/library/script.regex b/tests/snapshots/library/script.regex
new file mode 100644
index 0000000..63c114f
--- /dev/null
+++ b/tests/snapshots/library/script.regex
@@ -0,0 +1 @@
+^(?:[A-Za-zÀ-ɏ]+|[Ѐ-ӿ]+|[Ͱ-Ͽ]+|[一-鿿぀-ヿ가-힯]+|[؀-ۿ]+|[֐-׿]+|[ऀ-ॿ]+)$ \ No newline at end of file
diff --git a/tests/snapshots/library/secret.regex b/tests/snapshots/library/secret.regex
new file mode 100644
index 0000000..4e05bdc
--- /dev/null
+++ b/tests/snapshots/library/secret.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_-]{16,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/sedol.regex b/tests/snapshots/library/sedol.regex
new file mode 100644
index 0000000..83999af
--- /dev/null
+++ b/tests/snapshots/library/sedol.regex
@@ -0,0 +1 @@
+^[B-DF-HJ-NP-TV-XY-Z0-9]{6}\d$ \ No newline at end of file
diff --git a/tests/snapshots/library/semver.regex b/tests/snapshots/library/semver.regex
new file mode 100644
index 0000000..b878d26
--- /dev/null
+++ b/tests/snapshots/library/semver.regex
@@ -0,0 +1 @@
+^(?P<major>(?:[1-9]\d*|[0]))\.(?P<minor>(?:[1-9]\d*|[0]))\.(?P<patch>(?:[1-9]\d*|[0]))(?:\-(?P<prerelease>(?:[1-9]\d*|\d*[a-zA-Z\-][0-9a-zA-Z\-]*|[0])(?:\.(?:[1-9]\d*|\d*[a-zA-Z\-][0-9a-zA-Z\-]*|[0]))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z\-]+(?:\.[0-9a-zA-Z\-]+)*))?$ \ No newline at end of file
diff --git a/tests/snapshots/library/session.regex b/tests/snapshots/library/session.regex
new file mode 100644
index 0000000..b7faca2
--- /dev/null
+++ b/tests/snapshots/library/session.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_-]{16,128}$ \ No newline at end of file
diff --git a/tests/snapshots/library/shebang.regex b/tests/snapshots/library/shebang.regex
new file mode 100644
index 0000000..899503e
--- /dev/null
+++ b/tests/snapshots/library/shebang.regex
@@ -0,0 +1 @@
+^\#!/(?:usr/)?(?:(?:bin|sbin|local))/(?:env\s+)?[a-zA-Z0-9\._\+/\-]+$ \ No newline at end of file
diff --git a/tests/snapshots/library/signature.regex b/tests/snapshots/library/signature.regex
new file mode 100644
index 0000000..5953cda
--- /dev/null
+++ b/tests/snapshots/library/signature.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9\+/=_\-]{64,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/signing.regex b/tests/snapshots/library/signing.regex
new file mode 100644
index 0000000..db3063b
--- /dev/null
+++ b/tests/snapshots/library/signing.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9+/=_-]{32,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/sitemap.regex b/tests/snapshots/library/sitemap.regex
new file mode 100644
index 0000000..19ae6bb
--- /dev/null
+++ b/tests/snapshots/library/sitemap.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+=\?\&\#:%\~]{2,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/sku.regex b/tests/snapshots/library/sku.regex
new file mode 100644
index 0000000..4fdd21e
--- /dev/null
+++ b/tests/snapshots/library/sku.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9-_./]{4,20}$ \ No newline at end of file
diff --git a/tests/snapshots/library/slug.regex b/tests/snapshots/library/slug.regex
new file mode 100644
index 0000000..8f483d5
--- /dev/null
+++ b/tests/snapshots/library/slug.regex
@@ -0,0 +1 @@
+^[a-z0-9]+(?:\-[a-z0-9]+)*$ \ No newline at end of file
diff --git a/tests/snapshots/library/soap.regex b/tests/snapshots/library/soap.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/soap.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/socket.regex b/tests/snapshots/library/socket.regex
new file mode 100644
index 0000000..36ef08c
--- /dev/null
+++ b/tests/snapshots/library/socket.regex
@@ -0,0 +1 @@
+^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|\[[0-9a-fA-F:]+\]|[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*):\d{1,5}$ \ No newline at end of file
diff --git a/tests/snapshots/library/sortcode.regex b/tests/snapshots/library/sortcode.regex
new file mode 100644
index 0000000..e2c3aaf
--- /dev/null
+++ b/tests/snapshots/library/sortcode.regex
@@ -0,0 +1 @@
+(?:^\d{2}\-\d{2}\-\d{2}$|^\d{6}$) \ No newline at end of file
diff --git a/tests/snapshots/library/ssh.regex b/tests/snapshots/library/ssh.regex
new file mode 100644
index 0000000..e2638b9
--- /dev/null
+++ b/tests/snapshots/library/ssh.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9\+/=_\-\.:]){16,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/ssn.regex b/tests/snapshots/library/ssn.regex
new file mode 100644
index 0000000..45dabbc
--- /dev/null
+++ b/tests/snapshots/library/ssn.regex
@@ -0,0 +1 @@
+^(?!(?:666|000|9\d{2}))\d{3}\-(?!00)\d{2}\-(?!0{4})\d{4}$ \ No newline at end of file
diff --git a/tests/snapshots/library/sso.regex b/tests/snapshots/library/sso.regex
new file mode 100644
index 0000000..dff8aed
--- /dev/null
+++ b/tests/snapshots/library/sso.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9+/=_\-.]{20,2048}$ \ No newline at end of file
diff --git a/tests/snapshots/library/subdomain.regex b/tests/snapshots/library/subdomain.regex
new file mode 100644
index 0000000..b51e39e
--- /dev/null
+++ b/tests/snapshots/library/subdomain.regex
@@ -0,0 +1 @@
+^[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]$ \ No newline at end of file
diff --git a/tests/snapshots/library/subnet.regex b/tests/snapshots/library/subnet.regex
new file mode 100644
index 0000000..284d1f0
--- /dev/null
+++ b/tests/snapshots/library/subnet.regex
@@ -0,0 +1 @@
+^(?:255|254|252|248|240|224|192|128|[0])\.(?:255|254|252|248|240|224|192|128|[0])\.(?:255|254|252|248|240|224|192|128|[0])\.(?:255|254|252|248|240|224|192|128|[0])$ \ No newline at end of file
diff --git a/tests/snapshots/library/svg.regex b/tests/snapshots/library/svg.regex
new file mode 100644
index 0000000..10c8c17
--- /dev/null
+++ b/tests/snapshots/library/svg.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/]{1,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/swagger.regex b/tests/snapshots/library/swagger.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/swagger.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/swatch.regex b/tests/snapshots/library/swatch.regex
new file mode 100644
index 0000000..d3d4f11
--- /dev/null
+++ b/tests/snapshots/library/swatch.regex
@@ -0,0 +1 @@
+^(?:(?:\#[0-9A-Fa-f]{3,8})|[a-zA-Z]{3,20})$ \ No newline at end of file
diff --git a/tests/snapshots/library/tex.regex b/tests/snapshots/library/tex.regex
new file mode 100644
index 0000000..818db67
--- /dev/null
+++ b/tests/snapshots/library/tex.regex
@@ -0,0 +1 @@
+^\\documentclass(?:\[[^\]]*\])?\{[^}]+\}.*$ \ No newline at end of file
diff --git a/tests/snapshots/library/time.regex b/tests/snapshots/library/time.regex
new file mode 100644
index 0000000..753d9d4
--- /dev/null
+++ b/tests/snapshots/library/time.regex
@@ -0,0 +1 @@
+^(?:(?:2[0-3]|[01]?\d):[0-5]\d(?::[0-5]\d(?:\.\d{1,6})?)?|(?:1[0-2]|0?[1-9]):[0-5]\d(?::[0-5]\d)?\s?[AaPp][Mm])$ \ No newline at end of file
diff --git a/tests/snapshots/library/timestamp.regex b/tests/snapshots/library/timestamp.regex
new file mode 100644
index 0000000..17e8f86
--- /dev/null
+++ b/tests/snapshots/library/timestamp.regex
@@ -0,0 +1 @@
+^\-?\d{10,13}$ \ No newline at end of file
diff --git a/tests/snapshots/library/timezone.regex b/tests/snapshots/library/timezone.regex
new file mode 100644
index 0000000..ee45ed2
--- /dev/null
+++ b/tests/snapshots/library/timezone.regex
@@ -0,0 +1 @@
+^(?:[A-Z][a-zA-Z_\+\-]+(?:/[A-Z][a-zA-Z_\+\-]+)+|(?:UTC|GMT|UT|[Z])|[A-Z]{2,5})$ \ No newline at end of file
diff --git a/tests/snapshots/library/tin.regex b/tests/snapshots/library/tin.regex
new file mode 100644
index 0000000..a2a44f9
--- /dev/null
+++ b/tests/snapshots/library/tin.regex
@@ -0,0 +1 @@
+(?:^(?!(?:666|000|9\d{2}))\d{3}\-(?!00)\d{2}\-(?!0{4})\d{4}$|^\d{2}\-\d{7}$|^9\d{2}\-(?:5\d|6[0-5]|7\d|8[0-8]|9[0-2]|9[4-9])\-\d{4}$) \ No newline at end of file
diff --git a/tests/snapshots/library/tld.regex b/tests/snapshots/library/tld.regex
new file mode 100644
index 0000000..7b44a44
--- /dev/null
+++ b/tests/snapshots/library/tld.regex
@@ -0,0 +1 @@
+^[a-zA-Z]{2,63}$ \ No newline at end of file
diff --git a/tests/snapshots/library/token.regex b/tests/snapshots/library/token.regex
new file mode 100644
index 0000000..b43fe98
--- /dev/null
+++ b/tests/snapshots/library/token.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\-.]{24,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/toml.regex b/tests/snapshots/library/toml.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/toml.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/tsv.regex b/tests/snapshots/library/tsv.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/tsv.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/unicode.regex b/tests/snapshots/library/unicode.regex
new file mode 100644
index 0000000..1a32025
--- /dev/null
+++ b/tests/snapshots/library/unicode.regex
Binary files differ
diff --git a/tests/snapshots/library/uri.regex b/tests/snapshots/library/uri.regex
new file mode 100644
index 0000000..1870d58
--- /dev/null
+++ b/tests/snapshots/library/uri.regex
@@ -0,0 +1 @@
+^[a-zA-Z][a-zA-Z0-9\+\.\-]*:\S+$ \ No newline at end of file
diff --git a/tests/snapshots/library/url.regex b/tests/snapshots/library/url.regex
new file mode 100644
index 0000000..e672564
--- /dev/null
+++ b/tests/snapshots/library/url.regex
@@ -0,0 +1 @@
+^(?:https?://)?(?:www\.)?[\-a-zA-Z0-9@:%\._\+\~\#=]{1,256}\.[a-zA-Z0-9\(\)]{1,6}\b[\-a-zA-Z0-9\(\)@:%_\+\.\~\#\?\&/=]*$ \ No newline at end of file
diff --git a/tests/snapshots/library/useragent.regex b/tests/snapshots/library/useragent.regex
new file mode 100644
index 0000000..ce6825b
--- /dev/null
+++ b/tests/snapshots/library/useragent.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9/\.\-\(\) ;\+_,:]{4,1024}$ \ No newline at end of file
diff --git a/tests/snapshots/library/username.regex b/tests/snapshots/library/username.regex
new file mode 100644
index 0000000..4e5c432
--- /dev/null
+++ b/tests/snapshots/library/username.regex
@@ -0,0 +1 @@
+^[a-zA-Z0-9][a-zA-Z0-9_.-]{2,29}$ \ No newline at end of file
diff --git a/tests/snapshots/library/uuid.regex b/tests/snapshots/library/uuid.regex
new file mode 100644
index 0000000..7697f0f
--- /dev/null
+++ b/tests/snapshots/library/uuid.regex
@@ -0,0 +1 @@
+^[0-9a-f]{8}\-[0-9a-f]{4}\-[0-5][0-9a-f]{3}\-[089ab][0-9a-f]{3}\-[0-9a-f]{12}$ \ No newline at end of file
diff --git a/tests/snapshots/library/vat.regex b/tests/snapshots/library/vat.regex
new file mode 100644
index 0000000..b05c315
--- /dev/null
+++ b/tests/snapshots/library/vat.regex
@@ -0,0 +1 @@
+^[A-Z]{2}\d{6,12}$ \ No newline at end of file
diff --git a/tests/snapshots/library/vehicle.regex b/tests/snapshots/library/vehicle.regex
new file mode 100644
index 0000000..22d7bab
--- /dev/null
+++ b/tests/snapshots/library/vehicle.regex
@@ -0,0 +1 @@
+^[A-Z0-9][A-Z0-9\- ]{3,17}$ \ No newline at end of file
diff --git a/tests/snapshots/library/version.regex b/tests/snapshots/library/version.regex
new file mode 100644
index 0000000..1276869
--- /dev/null
+++ b/tests/snapshots/library/version.regex
@@ -0,0 +1 @@
+^v?\d+(?:\.\d+){0,3}(?:[-.+][a-zA-Z0-9\.\-]+)?$ \ No newline at end of file
diff --git a/tests/snapshots/library/vin.regex b/tests/snapshots/library/vin.regex
new file mode 100644
index 0000000..602b901
--- /dev/null
+++ b/tests/snapshots/library/vin.regex
@@ -0,0 +1 @@
+^[A-HJ-NPR-Z0-9]{17}$ \ No newline at end of file
diff --git a/tests/snapshots/library/wallet.regex b/tests/snapshots/library/wallet.regex
new file mode 100644
index 0000000..76cba2c
--- /dev/null
+++ b/tests/snapshots/library/wallet.regex
@@ -0,0 +1 @@
+^(?:[13][a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-z0-9]{25,89}|0x[a-fA-F0-9]{40}|[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}|D[5-9A-HJ-NP-U][1-9A-HJ-NP-Za-km-z]{32}|X[1-9A-HJ-NP-Za-km-z]{33})$ \ No newline at end of file
diff --git a/tests/snapshots/library/webauthn.regex b/tests/snapshots/library/webauthn.regex
new file mode 100644
index 0000000..95d1108
--- /dev/null
+++ b/tests/snapshots/library/webauthn.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_-]{43,512}$ \ No newline at end of file
diff --git a/tests/snapshots/library/webhook.regex b/tests/snapshots/library/webhook.regex
new file mode 100644
index 0000000..5ce9b78
--- /dev/null
+++ b/tests/snapshots/library/webhook.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{3,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/word.regex b/tests/snapshots/library/word.regex
new file mode 100644
index 0000000..dd924e9
--- /dev/null
+++ b/tests/snapshots/library/word.regex
@@ -0,0 +1 @@
+^\w+$ \ No newline at end of file
diff --git a/tests/snapshots/library/x509.regex b/tests/snapshots/library/x509.regex
new file mode 100644
index 0000000..e2638b9
--- /dev/null
+++ b/tests/snapshots/library/x509.regex
@@ -0,0 +1 @@
+^(?:\s|[A-Za-z0-9\+/=_\-\.:]){16,4096}$ \ No newline at end of file
diff --git a/tests/snapshots/library/xlsx.regex b/tests/snapshots/library/xlsx.regex
new file mode 100644
index 0000000..10c8c17
--- /dev/null
+++ b/tests/snapshots/library/xlsx.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/]{1,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/xml.regex b/tests/snapshots/library/xml.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/xml.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/yaml.regex b/tests/snapshots/library/yaml.regex
new file mode 100644
index 0000000..d9c50db
--- /dev/null
+++ b/tests/snapshots/library/yaml.regex
@@ -0,0 +1 @@
+^[A-Za-z0-9_\.\-/\+]{2,256}$ \ No newline at end of file
diff --git a/tests/snapshots/library/year.regex b/tests/snapshots/library/year.regex
new file mode 100644
index 0000000..e7d130f
--- /dev/null
+++ b/tests/snapshots/library/year.regex
@@ -0,0 +1 @@
+^\d{4}$ \ No newline at end of file
diff --git a/tests/testing/snapshots.test.py b/tests/testing/snapshots.test.py
new file mode 100644
index 0000000..c2d8676
--- /dev/null
+++ b/tests/testing/snapshots.test.py
@@ -0,0 +1,90 @@
+"""Tests for the snapshot helper in :mod:`edify.testing.snapshots`."""
+
+from pathlib import Path
+
+import pytest
+
+from edify.testing import SnapshotMismatchError, SnapshotMissingError, assert_snapshot
+
+_UPDATE_ENVIRONMENT_VARIABLE = "EDIFY_UPDATE_SNAPSHOTS"
+
+
+def test_assert_snapshot_passes_when_actual_matches_committed_reference(tmp_path, monkeypatch):
+ monkeypatch.delenv(_UPDATE_ENVIRONMENT_VARIABLE, raising=False)
+ snapshot_path = tmp_path / "identical.snapshot"
+ snapshot_path.write_text("hello\n")
+ assert_snapshot("hello\n", snapshot_path)
+
+
+def test_assert_snapshot_raises_snapshot_mismatch_when_content_differs(tmp_path, monkeypatch):
+ monkeypatch.delenv(_UPDATE_ENVIRONMENT_VARIABLE, raising=False)
+ snapshot_path = tmp_path / "drift.snapshot"
+ snapshot_path.write_text("expected\n")
+ with pytest.raises(SnapshotMismatchError) as excinfo:
+ assert_snapshot("actual\n", snapshot_path)
+ text = str(excinfo.value)
+ assert "snapshot mismatch" in text
+ assert "-expected" in text
+ assert "+actual" in text
+ assert "EDIFY_UPDATE_SNAPSHOTS=1" in text
+
+
+def test_assert_snapshot_raises_snapshot_missing_when_reference_absent(tmp_path, monkeypatch):
+ monkeypatch.delenv(_UPDATE_ENVIRONMENT_VARIABLE, raising=False)
+ absent_snapshot = tmp_path / "never_written.snapshot"
+ with pytest.raises(SnapshotMissingError, match="snapshot file missing"):
+ assert_snapshot("any actual value", absent_snapshot)
+
+
+def test_update_mode_writes_the_snapshot_when_the_file_is_missing(tmp_path, monkeypatch):
+ monkeypatch.setenv(_UPDATE_ENVIRONMENT_VARIABLE, "1")
+ snapshot_path = tmp_path / "created.snapshot"
+ assert_snapshot("fresh content\n", snapshot_path)
+ assert snapshot_path.read_text() == "fresh content\n"
+
+
+def test_update_mode_overwrites_the_snapshot_on_drift(tmp_path, monkeypatch):
+ monkeypatch.setenv(_UPDATE_ENVIRONMENT_VARIABLE, "1")
+ snapshot_path = tmp_path / "regenerated.snapshot"
+ snapshot_path.write_text("stale\n")
+ assert_snapshot("current\n", snapshot_path)
+ assert snapshot_path.read_text() == "current\n"
+
+
+def test_update_mode_is_off_when_env_variable_is_set_to_other_value(tmp_path, monkeypatch):
+ monkeypatch.setenv(_UPDATE_ENVIRONMENT_VARIABLE, "0")
+ snapshot_path = tmp_path / "no_write.snapshot"
+ with pytest.raises(SnapshotMissingError):
+ assert_snapshot("actual", snapshot_path)
+
+
+def test_update_mode_creates_missing_parent_directories(tmp_path, monkeypatch):
+ monkeypatch.setenv(_UPDATE_ENVIRONMENT_VARIABLE, "1")
+ snapshot_path = tmp_path / "nested" / "dir" / "leaf.snapshot"
+ assert_snapshot("value", snapshot_path)
+ assert snapshot_path.read_text() == "value"
+
+
+def test_snapshot_mismatch_error_names_the_snapshot_path(tmp_path, monkeypatch):
+ monkeypatch.delenv(_UPDATE_ENVIRONMENT_VARIABLE, raising=False)
+ snapshot_path = tmp_path / "named.snapshot"
+ snapshot_path.write_text("was")
+ with pytest.raises(SnapshotMismatchError) as excinfo:
+ assert_snapshot("is", snapshot_path)
+ assert str(snapshot_path) in str(excinfo.value)
+
+
+def test_snapshot_missing_error_is_an_assertion_error_subclass():
+ assert issubclass(SnapshotMissingError, AssertionError)
+
+
+def test_snapshot_mismatch_error_is_an_assertion_error_subclass():
+ assert issubclass(SnapshotMismatchError, AssertionError)
+
+
+def test_snapshot_helper_is_reachable_via_edify_testing_namespace(tmp_path):
+ from edify import testing
+
+ snapshot_path: Path = tmp_path / "namespace.snapshot"
+ snapshot_path.write_text("ok\n")
+ testing.assert_snapshot("ok\n", snapshot_path)