diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 478c722..7a399e9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,8 @@ -version: 2, +version: 2 updates: - - package-ecosystem: "gradle" + - package-ecosystem: maven directory: "/" schedule: - interval: "weekly" - day: "friday" - assignees: - - "jackmatt2" - labels: - - "dependencies" \ No newline at end of file + interval: daily + time: "21:00" + open-pull-requests-limit: 10 \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 19ea542..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,64 +0,0 @@ -# This workflow will build a Java project with Gradle -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle - -name: build - -on: - push: - branches: - - master - - 'release/**' - pull_request: - branches: - - master - - 'release/**' - workflow_dispatch: - -jobs: - build: - name: Build - runs-on: ${{ matrix.os }} - strategy: - matrix: - java_version: [ '8' ] - os: [ ubuntu-latest, windows-latest, macOS-latest ] - steps: - - uses: actions/checkout@v2 - - name: Set up JDK ${{ matrix.java_version }} - uses: actions/setup-java@v1 - with: - java-version: ${{ matrix.java_version }} - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Build with Gradle - run: ./gradlew build - deploy: - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - name: Deploy SNAPSHOT - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 8 - uses: actions/setup-java@v1 - with: - java-version: 8 - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Jar - run: ./gradlew shadowJar - - name: Generate tag version - uses: anothrNick/github-tag-action@v1 - id: tag_version_dry_run - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WITH_V: false - DRY_RUN: true - - name: publish - env: - SNAPSHOT_VERSION: ${{ steps.tag_version_dry_run.outputs.tag }}-SNAPSHOT - ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY_ASCII_ARMOR }} - ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_KEY_PASSPHRASE }} - MAVEN_CENTRAL_TOKEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_TOKEN_USERNAME }} - MAVEN_CENTRAL_TOKEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN_PASSWORD }} - run: ./gradlew signArchives uploadArchives -Pversion=$SNAPSHOT_VERSION -PossrhUsername=${MAVEN_CENTRAL_TOKEN_USERNAME} -PossrhPassword=${MAVEN_CENTRAL_TOKEN_PASSWORD} -Psign=true - diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 0c39799..0000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,74 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ "master" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "master" ] - schedule: - - cron: '34 22 * * 1' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'java' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..47ad2fb --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,41 @@ +name: Dependabot auto-merge + +on: + pull_request: + types: + - opened + - reopened + - synchronize + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v1 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Check if PR is mergeable + run: | + MERGEABLE=$(gh pr view "$PR_URL" --json mergeable -q .mergeable) + echo "Mergeable: $MERGEABLE" + if [ "$MERGEABLE" != "MERGEABLE" ]; then + echo "PR not mergeable. Skipping auto-merge." + exit 0 + fi + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Enable auto-merge for Dependabot PRs + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..9dd42b2 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,62 @@ +name: Publish to Maven Central + +on: + release: + types: [published] + +jobs: + publish: + name: Publish Maven package + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + cache: 'maven' + server-id: central + server-username: OSSRH_USERNAME + server-password: OSSRH_PASSWORD + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + + # Set reproducible build timestamp + - name: Set build timestamp + run: | + # Use the commit timestamp for reproducibility + COMMIT_TIMESTAMP=$(git log -1 --format=%ct) + BUILD_TIMESTAMP=$(date -u -d @${COMMIT_TIMESTAMP} +"%Y-%m-%dT%H:%M:%SZ") + echo "BUILD_TIMESTAMP=$BUILD_TIMESTAMP" >> $GITHUB_ENV + echo "Setting build timestamp to: $BUILD_TIMESTAMP" + + - name: Build and Test + run: mvn clean verify --batch-mode -Dproject.build.outputTimestamp="${BUILD_TIMESTAMP}" + + - name: Determine version from tag + id: version + run: | + RAW_TAG="${GITHUB_REF##*/}" + VERSION="${RAW_TAG#v}" + + if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then + VERSION="${VERSION}-SNAPSHOT" + fi + + echo "Resolved version: $VERSION" + echo "VERSION=$VERSION" >> $GITHUB_ENV + + - name: Set Maven version + run: mvn versions:set -DnewVersion=${VERSION} -DgenerateBackupPoms=false + + - name: Publish to Maven Central + run: mvn deploy --batch-mode -Dproject.build.outputTimestamp="${BUILD_TIMESTAMP}" + env: + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 91958ef..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: release - -on: - workflow_dispatch: - -jobs: - release: - name: Release to Maven Central, Tag & Release - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Generate tag version - uses: anothrNick/github-tag-action@v1 - id: tag_version_dry_run - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WITH_V: false - DRY_RUN: true - - name: Set up JDK 8 - uses: actions/setup-java@v1 - with: - java-version: 8 - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Jar - env: - RELEASE_VERSION: ${{ steps.tag_version_dry_run.outputs.tag }} - run: ./gradlew shadowJar -Pversion=$RELEASE_VERSION - - name: Publish to Maven Central - env: - ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY_ASCII_ARMOR }} - ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_KEY_PASSPHRASE }} - MAVEN_CENTRAL_TOKEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_TOKEN_USERNAME }} - MAVEN_CENTRAL_TOKEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN_PASSWORD }} - RELEASE_VERSION: ${{ steps.tag_version_dry_run.outputs.tag }} - run: ./gradlew -Pversion=$RELEASE_VERSION signArchives uploadArchives -PossrhUsername=${MAVEN_CENTRAL_TOKEN_USERNAME} -PossrhPassword=${MAVEN_CENTRAL_TOKEN_PASSWORD} -Psign=true - - name: Close & Release Staging Repository - env: - MAVEN_CENTRAL_TOKEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_TOKEN_USERNAME }} - MAVEN_CENTRAL_TOKEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN_PASSWORD }} - run: ./gradlew closeAndReleaseRepository -PossrhUsername=${MAVEN_CENTRAL_TOKEN_USERNAME} -PossrhPassword=${MAVEN_CENTRAL_TOKEN_PASSWORD} - - name: Push new tag - uses: anothrNick/github-tag-action@v1 - id: tag_version - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WITH_V: false - DRY_RUN: false - - name: Build Changelog - id: github_release - uses: mikepenz/release-changelog-builder-action@v3 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create Github Release - uses: softprops/action-gh-release@v1 - with: - body: ${{steps.github_release.outputs.changelog}} - tag_name: ${{ steps.tag_version.outputs.tag }} \ No newline at end of file diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 0000000..820c9fe --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,29 @@ +name: Verify + +on: + pull_request: + branches: + - '**' +jobs: + build: + name: Verify + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + cache: 'maven' + server-id: central + server-username: OSSRH_USERNAME + server-password: OSSRH_PASSWORD + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + + - name: Execute the verify step + run: mvn -B verify diff --git a/.gitignore b/.gitignore index 2b04bac..212a6d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,124 @@ -.gradle/ -build/ +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +TODO.md +.flattened-pom.xml -# Ignore Gradle GUI config -gradle-app.setting +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. + .idea/artifacts + .idea/compiler.xml + .idea/jarRepositories.xml + .idea/modules.xml + .idea/*.iml + .idea/modules -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar +# CMake +cmake-build-*/ -# Cache of project -.gradletasknamecache +# mpeltonen/sbt-idea plugin +.idea_modules/ -# IDE -.idea +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Windows template +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +.idea/ +target/ diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 0000000..9b30a2c --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1,4 @@ +--global-settings +.mvn/settings.xml +--settings +.mvn/settings.xml \ No newline at end of file diff --git a/.mvn/settings.xml b/.mvn/settings.xml new file mode 100644 index 0000000..169f6ac --- /dev/null +++ b/.mvn/settings.xml @@ -0,0 +1,12 @@ + + + + + central + ${env.OSSRH_USERNAME} + ${env.OSSRH_PASSWORD} + + + \ No newline at end of file diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..4f15c4d --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 020d4d1..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,89 +0,0 @@ -# Contributing - -We welcome contributions to this project by both internal and external parties - -## How to contribute - -1. Fork the repository into your own github account (external contributors) or create a new branch (internal - contributions) -1. Make your code changes -1. Ensure you commit message is descriptive as it acts as the changelog. Mark any breaking changes with `BREAKING`. - Include a rectification strategy if you introduce a `BREAKING` change. -1. Ensure `README.md` is updated if needed. All code samples in the README should have corresponding (actual sames) in the various docs/ folders -1. Submit a pull request back to `master` branch (or the branch you are contributing to) -1. Ensure Github Actions build passes -1. Await reviews -1. Once merged into `master` a `SNAPSHOT` build will be available for consumption - immediately [here](https://oss.sonatype.org/content/repositories/snapshots/io/github/origin-energy/). Note that - snapshots change regularly and cannot be relied upon. -1. Hard Releases will be made once enough features have been added. - -## Building - -```java -./gradlew spotlessApply shadowJar -``` - -## Deploying locally and deploying to `.m2/` - -``` -./gradlew publishToMavenLocal -``` - -Ensure you add `mavenLocal()` to your consuming project and the dependency verison matches that in your local `.m2` -folder - -# Uploading to maven central -see `.github/workflows/release.yml` - -# Uploading to maven central (manually) - -Gradle release plugin is not currently working so this is a manual process at the moment. - -# Setup GPG on your machine - -1. copy the GPG Private key into a file `private.key` -1. run - -``` -gpg --import private.key -cd ~/.gnupg -gpg -k -gpg --export-secret-key YOUR_KEY_ID > ~/.gnupg/secring.gpg -``` - -## Preparing - -1. Create a tag `X.X.X` -1. Update `gradle.properties` and remove `-SNAPSHOT` from the version number -1. Check this file into version control and push the branch to the remote -1. run - -``` -export SONAR_USERNAME=? -export SONAR_PASSWORD=? -export GPG_KEY_ID=? -export GPG_KEY_PASSPHRASE=? -export PATH_TO_SECRING_GPG=~/.gnupg/secring.gpg - -# I found shadowed classes are not included if you don't separate the gradle operations -./gradlew clean shadowJar -./gradlew signArchives uploadArchives -PossrhUsername=${SONAR_USERNAME} -PossrhPassword=${SONAR_PASSWORD} -Psigning.keyId=${GPG_KEY_ID} -Psigning.password=${GPG_KEY_PASSPHRASE} -Psigning.secretKeyRingFile=${PATH_TO_SECRING_GPG} -``` - -## Releasing [Full Tutorial](https://central.sonatype.org/pages/ossrh-guide.html) - -1. Login to SONAR (https://oss.sonatype.org) -1. Click 'Staging Repositories' and locate the 'iogithuborigin-energy' bundle -1. Review artifacts are correct in the 'Content' tab -1. Press the 'Close' and give a reason such as "Jack Matthews - Confirmed artifacts are OK" -1. Wait for about 1 min and press the 'Refresh button', if all sanity checks have passed the 'Release' button will be - visible -1. Press the 'Release' button and give a reason for releasing -1. Objects should be available in about 10 min (Longer for search.maven.org) - -## Cleanup - -1. Checkout master branch -1. Increment version number in `gradle.properties` -1. Create pull request for merge \ No newline at end of file diff --git a/LATEST_SNAPSHOT.md b/LATEST_SNAPSHOT.md deleted file mode 100644 index 0968795..0000000 --- a/LATEST_SNAPSHOT.md +++ /dev/null @@ -1,20 +0,0 @@ -# Using and testing the latest SNAPSHOT (excuse the pun) from maven central - -Gradle - -``` -repositories { - // ... - maven { - url "https://oss.sonatype.org/content/repositories/snapshots" - } -} - -dependencies { - // ... - - // Replace {FRAMEWORK} with you testing framework - // Replace {X.X.X} with the version number from `/gradle.properties` - testCompile "io.github.origin-energy:java-snapshot-testing-{FRAMEWORK}:{X.X.X}-SNAPSHOT" -} -``` \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 3b85842..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 André Bonna - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 04c057f..f024a49 100644 --- a/README.md +++ b/README.md @@ -1,813 +1,4 @@ -[![Build Status](https://github.com/origin-energy/java-snapshot-testing/workflows/build/badge.svg)](https://github.com/origin-energy/java-snapshot-testing/actions) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.github.origin-energy/java-snapshot-testing-core/badge.svg)](https://search.maven.org/artifact/io.github.origin-energy/java-snapshot-testing-core/3.2.7/jar) - # Java Snapshot Testing -- Inspired by [facebook's Jest framework](https://facebook.github.io/jest/docs/en/snapshot-testing.html) - -🎉 4.0.0 is out - -## Upgrading -- Upgrade guide from 3.X to 4.X [here](https://github.com/origin-energy/java-snapshot-testing/discussions/94) -- Upgrade guide from 2.X to 3.X [here](https://github.com/origin-energy/java-snapshot-testing/discussions/73) -- Upgrade guide from 2.X-BETA to 2.X [here](https://github.com/origin-energy/java-snapshot-testing/discussions/58) - -## The testing framework loved by ~~lazy~~ __productive__ devs - -- Tired of needing to `assertThat(foo).isEqualTo("bar")` again & again? -- Are you just wanting to ensure you don't break - for example - REST interfaces -- Are you manually saving text files for verification in your tests? - -**Want a better way?** -Then java-snapshot-testing might just be what you are looking for! - -## Quick Start (Junit5 + Gradle example) - -1. Add test dependencies - -```groovy -// In this case we are using the JUnit5 testing framework -testImplementation 'io.github.origin-energy:java-snapshot-testing-junit5:4.+' - -// slf4j logging implementation if you don't already have one -testImplementation("org.slf4j:slf4j-simple:2.0.0-alpha0") - -// Optional: Many will want to serialize into JSON. In this case you should also add the Jackson plugin -testImplementation 'io.github.origin-energy:java-snapshot-testing-plugin-jackson:4.+' -testImplementation 'com.fasterxml.jackson.core:jackson-core:2.11.3' -testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.11.3' - -// Optional: If you want Jackson to serialize Java 8 date/time types or Optionals you should also add the following dependencies -testRuntimeOnly 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.3' -testRuntimeOnly 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.3' -``` - -2. Create `snapshot.properties` and configure your global settings. Be sure to set `output-dir` appropriately for your - JVM language. - -- /src/test/resources/snapshot.properties - - ```text -serializer=au.com.origin.snapshots.serializers.v1.ToStringSnapshotSerializer -serializer.base64=au.com.origin.snapshots.serializers.v1.Base64SnapshotSerializer -serializer.json=au.com.origin.snapshots.jackson.serializers.v1.JacksonSnapshotSerializer -serializer.orderedJson=au.com.origin.snapshots.jackson.serializers.v1.DeterministicJacksonSnapshotSerializer -comparator=au.com.origin.snapshots.comparators.v1.PlainTextEqualsComparator -reporters=au.com.origin.snapshots.reporters.v1.PlainTextSnapshotReporter -snapshot-dir=__snapshots__ -output-dir=src/test/java -ci-env-var=CI -update-snapshot=none -``` - -3. Enable snapshot testing and write your first test - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.junit5.SnapshotExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import java.util.HashMap; -import java.util.Map; - -@ExtendWith({SnapshotExtension.class}) -public class MyFirstSnapshotTest { - - private Expect expect; - - @SnapshotName("i_can_give_custom_names_to_my_snapshots") - @Test - public void toStringSerializationTest() { - expect.toMatchSnapshot("Hello World"); - } - - @Test - public void jsonSerializationTest() { - Map map = new HashMap<>(); - map.put("name", "John Doe"); - map.put("age", 40); - - expect - .serializer("json") - .toMatchSnapshot(map); - } - -} -``` - -4. Run your test - -Bingo - you should now see your snapshot in the `__snapshots__` folder created next to your test. Try -changing `"Hello World"` to `"Hello Universe"` and watch it fail with a `.debug` file. - -```text -au.com.origin.snapshots.docs.MyFirstSnapshotTest.jsonSerializationTest=[ - { - "age": 40, - "name": "John Doe" - } -] - - -i_can_give_custom_names_to_my_snapshots=[ -Hello World -] -``` - -## Advantages of Snapshot Testing - -- Great for testing JSON interfaces ensuring you don't break clients -- Fast and easy to test -- Will implicitly test areas of your code you did not think about -- Great of testing dynamic objects - -You're responsible for making sure your generated snapshots do not include platform specific or other non-deterministic -data. - -## Disadvantages of Snapshot Testing - -- You need to ensure your test is deterministic for all fields (there are ways to ignore things like dates) -- Does not give great insight to why the snapshot failed -- Can be difficult to troll though large snapshot changes where you might only be interested in a small set of fields - -## Installation [Maven](https://search.maven.org/search?q=java-snapshot-testing) - -These docs are for the latest `-SNAPSHOT` version published to maven central. Select the tag `X.X.X` matching your maven -dependency to get correct documentation for your version. - -Only if you want to integrate with an unsupported framework. [Show me how!](#using-an-unsupported-framework) - -- [Core](https://search.maven.org/search?q=a:java-snapshot-testing-core) - -We currently support: - -- [JUnit4](https://search.maven.org/search?q=a:java-snapshot-testing-junit4) -- [JUnit5](https://search.maven.org/search?q=a:java-snapshot-testing-junit5) -- [Spock](https://search.maven.org/search?q=a:java-snapshot-testing-spock) - -Plugins - -- [Jackson for JSON serialization](https://search.maven.org/search?q=a:java-snapshot-testing-plugin-jackson) - - You need jackson on your classpath (Gradle example) - ```groovy - // Required java-snapshot-testing peer dependencies - testImplementation 'com.fasterxml.jackson.core:jackson-core:2.11.3' - testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.11.3' - // Optional java-snapshot-testing peer dependencies - testRuntimeOnly 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.3' - testRuntimeOnly 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.3' - ``` - -## How does it work? - -1. When a test runs for the first time, a `.snap` file is created in a `__snapshots__` sub-directory -1. On subsequent test runs, the `.snap` file is compared with the one produced by the test -1. If they don't match, the test fails and a `.snap.debug` with the conflict is created -1. It is then your job to decide if you have introduced a regression or intentionally changed the output (Use your IDE - file comparison tools to compare the two files or refer to the terminal output) -1. If you have introduced a regression you will need to fix your code -1. If you have intentionally changed the output you can manually modify the `.snap` file to make it pass or delete it - and it will be generated again from scratch -1. Once you fix the test, the `*.snap.debug` file will get deleted - -## What is a Snapshot? - -A text representation of your java object (toString() or JSON). - -**String snapshot example** - -```java -expect.toMatchSnapshot("Hello World"); -``` - -```text -au.com.example.company.HelloWorldTest.helloWorld=[ -Hello world -] -``` - -**JSON Snapshot Example** - -```java -expect.serializer("json").toMatchSnapshot(userDto); -``` - -```text -au.com.example.company.UserEndpointTest.shouldReturnCustomerData=[ - { - "id": "1", - "firstName": "John", - "lastName": "Smith", - "age": 34 - } -] -``` - -# Usage Examples - -All frameworks allow injection of the `Expect expect` via instance variable or method argument. In cases where -parameterised tests are used, it's often better to use an instance variable in order to avoid conflicts with -the underlying data table. - -Note: Due to the above restriction, method argument injection is destined for removal in future versions. - -## [JUnit 5](https://junit.org/junit5) - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.junit5.SnapshotExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import au.com.origin.snapshots.Expect; - -// Ensure you extend your test class with the SnapshotExtension -@ExtendWith({SnapshotExtension.class}) -public class JUnit5Example { - - // Option 1: inject Expect as an instance variable - private Expect expect; - - @Test - public void myTest1() { - // Verify your snapshot - expect.toMatchSnapshot("Hello World"); - } - - // Option 2: inject Expect into the method signature - @Test - public void myTest2(Expect expect) { - expect.toMatchSnapshot("Hello World Again"); - } -} -``` - -## [JUnit 4](https://junit.org/junit4) - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.junit4.SnapshotRunner; -import au.com.origin.snapshots.Expect; -import org.junit.Test; -import org.junit.runner.RunWith; - -// Ensure you RunWith the SnapshotRunner -@RunWith(SnapshotRunner.class) -public class JUnit4Example { - - // Option 1: inject Expect as an instance variable - private Expect expect; - - @SnapshotName("my first test") - @Test - public void myTest1() { - // Verify your snapshot - expect.toMatchSnapshot("Hello World"); - } - - @SnapshotName("my second test") - @Test - // Option 2: inject Expect into the method signature - public void myTest2(Expect expect) { - expect.toMatchSnapshot("Hello World Again"); - } -} -``` - -In order to run alongside another JUnit4 test runner such as `@RunWith(Parameterized.class)`, you need to use the -Rule based configuration instead. - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.junit4.SnapshotClassRule; -import au.com.origin.snapshots.junit4.SnapshotRule; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; - -public class JUnit4RulesExample { - - @ClassRule - public static SnapshotClassRule snapshotClassRule = new SnapshotClassRule(); - - @Rule - public SnapshotRule snapshotRule = new SnapshotRule(snapshotClassRule); - - private Expect expect; - - @SnapshotName("my first test") - @Test - public void myTest1() { - expect.toMatchSnapshot("Hello World"); - } -} -``` - -See the [ParameterizedTest](https://github.com/origin-energy/java-snapshot-testing/blob/master/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/ParameterizedTest.java) for an example implementation - -## [Spock](http://spockframework.org/) - -```groovy -package au.com.origin.snapshots.docs - -import au.com.origin.snapshots.annotations.SnapshotName -import au.com.origin.snapshots.spock.EnableSnapshots -import spock.lang.Specification - -import au.com.origin.snapshots.Expect - -// Ensure you enable snapshot testing support -@EnableSnapshots -class SpockExample extends Specification { - - // Option 1: inject Expect as an instance variable - private Expect expect - - // With spock tests you should always use @SnapshotName - otherwise they become coupled to test order - @SnapshotName("should_use_extension") - def "Should use extension"() { - when: - expect.toMatchSnapshot("Hello World") - - then: - true - } - - @SnapshotName("should_use_extension_as_method_argument") - // Option 2: inject Expect into the method signature - def "Should use extension as method argument"(Expect expect) { - when: - expect.toMatchSnapshot("Hello World") - - then: - true - } -} -``` - -# Using an unsupported framework - -This library is in no way restricted to JUnit4, Junit5 or Spock. - -Any framework can support the library as long as it follows the following rules: - -1. Before all the tests in a single file execute (once only) - ```java - SnapshotVerifier snapshotVerifier = new SnapshotVerifier(new YourFrameworkSnapshotConfig(), testClass, failOnOrphans); - ``` -1. After all the tests in a single file execute (once only) - ```java - snapshotVerifier.validateSnapshots(); - ``` -1. For each test class, setup your expectations - ```java - Expect expect = Expect.of(snapshotVerifier, testMethod); - expect.toMatchSnapshot("Something"); - ``` - -Here is a JUnit5 example that does not use the JUnit5 extension - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.SnapshotVerifier; -import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -// Notice we aren't using any framework extensions -public class CustomFrameworkExample { - - private static SnapshotVerifier snapshotVerifier; - - @BeforeAll - static void beforeAll() { - snapshotVerifier = new SnapshotVerifier(new PropertyResolvingSnapshotConfig(), CustomFrameworkExample.class); - } - - @AfterAll - static void afterAll() { - snapshotVerifier.validateSnapshots(); - } - - @Test - void shouldMatchSnapshotOne(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("Hello World"); - } - -} -``` -## Supplying your own snapshot name via @SnapshotName -By default, snapshots use the full method name as the identifier -For example -```text -au.com.origin.snapshots.docs.MyFirstSnapshotTest.helloWorldTest=[ -Hello World -] -``` - -This strategy has a number of problems -- it's long and unwieldy -- if the method name or class name changes, your tests become orphans -- The Spock framework tests use a generated method name that is based on index (Spock tests should always use `@SnapshotName`) - -You can supply a more meaningful name to your snapshot using `@SnapshotName("your_custom_name")` -This will generate as follows -``` -your_custom_name=[ -Hello World -] -``` - -Much more concise and not affected by class name or method name refactoring. - -## Resolving conflicting snapshot comparison via `*.snap.debug` - -Often your IDE has an excellent file comparison tool. - -- A `*.snap.debug` file will be created alongside your `*.snap` file when a conflict occurs. -- You can then use your IDE tooling to compare the two files. -- `*.snap.debug` is deleted automatically once the test passes. - -**Note:** `*.snap.debug` files should never be checked into version control so consider adding it to your `.gitignore` - -## snapshot.properties (required as of v2.4.0) - -This file allows you to conveniently setup global defaults - -| key | Description | -|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| -|serializer | Class name of the [serializer](#supplying-a-custom-snapshotserializer), default serializer | -|serializer.{name} | Class name of the [serializer](#supplying-a-custom-snapshotserializer), accessible via `.serializer("{name}")` | -|comparator | Class name of the [comparator](#supplying-a-custom-snapshotcomparator) | -|comparator.{name} | Class name of the [comparator](#supplying-a-custom-snapshotcomparator), accessible via `.comparator("{name}")` | -|reporters | Comma separated list of class names to use as [reporters](#supplying-a-custom-snapshotreporter) | -|reporters.{name} | Comma separated list of class names to use as [reporters](#supplying-a-custom-snapshotreporter), accessible via `.reporters("{name}")` | -|snapshot-dir | Name of sub-folder holding your snapshots | -|output-dir | Base directory of your test files (although it can be a different directory if you want) | -|ci-env-var | Name of environment variable used to detect if we are running on a Build Server | -|update-snapshot | Similar to `--updateSnapshot` in [Jest](https://jestjs.io/docs/en/snapshot-testing#updating-snapshots)
[all]=update all snapshots
[none]=update no snapshots
[MyTest1,MyTest2]=update snapshots in these classes only | - -For example: - - ```text -serializer=au.com.origin.snapshots.serializers.v1.ToStringSnapshotSerializer -serializer.base64=au.com.origin.snapshots.serializers.v1.Base64SnapshotSerializer -serializer.json=au.com.origin.snapshots.jackson.serializers.v1.JacksonSnapshotSerializer -serializer.orderedJson=au.com.origin.snapshots.jackson.serializers.v1.DeterministicJacksonSnapshotSerializer -comparator=au.com.origin.snapshots.comparators.v1.PlainTextEqualsComparator -reporters=au.com.origin.snapshots.reporters.v1.PlainTextSnapshotReporter -snapshot-dir=__snapshots__ -output-dir=src/test/java -ci-env-var=CI -update-snapshot=none -``` - -## Parameterized tests - -In cases where the same test runs multiple times with different parameters you need to set the `scenario` and it must be -unique for each run - -```java -expect.scenario(params).toMatchSnapshot("Something"); -``` - -## Scenario Example - -```groovy -package au.com.origin.snapshots.docs - -import au.com.origin.snapshots.Expect -import au.com.origin.snapshots.annotations.SnapshotName -import au.com.origin.snapshots.spock.EnableSnapshots -import spock.lang.Specification - -@EnableSnapshots -class SpockWithParametersExample extends Specification { - - private Expect expect - - @SnapshotName("convert_to_uppercase") - def 'Convert #scenario to uppercase'() { - when: 'I convert to uppercase' - String result = value.toUpperCase(); - then: 'Should convert letters to uppercase' - // Check you snapshot against your output using a unique scenario - expect.scenario(scenario).toMatchSnapshot(result) - where: - scenario | value - 'letter' | 'a' - 'number' | '1' - } -} -``` - -## Supplying a custom SnapshotSerializer - -The serializer determines how a class gets converted into a string. - -Serializers are pluggable, so you can write you own by implementing the `SnapshotSerializer` interface. - -Currently, we support the following serializers. - -### Shipped with core - -| Serializer | Description | -|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------| -| ToStringSnapshotSerializer | uses the `toString()` method | -| Base64SnapshotSerializer | use for images or other binary sources that output a `byte[]`. The output is encoded to Base64 | - -### Shipped with Jackson plugin - -| Serializer | Description | -|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------| -| JacksonSnapshotSerializer | uses [jackson](https://github.com/FasterXML/jackson) to convert a class to a snapshot | -| DeterministicJacksonSnapshotSerializer | extension of JacksonSnapshotSerializer that also orders Collections for situations where the order changes on multiple runs | - -Serializers are resolved in the following order. - -- (method level) explicitly `expect.serializer(ToStringSerializer.class).toMatchSnapshot(...);` or via property - file `expect.serializer("json").toMatchSnapshot(...);` -- (class level) explicitly `@UseSnapshotConfig` which gets read from the `getSerializer()` method -- (properties) implicitly via `snapshot.properties` - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.annotations.UseSnapshotConfig; -import au.com.origin.snapshots.junit5.SnapshotExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith(SnapshotExtension.class) -@UseSnapshotConfig(LowercaseToStringSnapshotConfig.class) -public class JUnit5ResolutionHierarchyExample { - - private Expect expect; - - @Test - public void aliasMethodTest() { - expect - .serializer("json") // <------ Using snapshot.properties - .toMatchSnapshot(new TestObject()); - } - - @Test - public void customSerializerTest() { - expect - .serializer(UppercaseToStringSerializer.class) // <------ Using custom serializer - .toMatchSnapshot(new TestObject()); - } - - // Read from LowercaseToStringSnapshotConfig defined on the class - @Test - public void lowercaseTest() { - expect.toMatchSnapshot(new TestObject()); - } -} -``` - -### Example: HibernateSerializer - -Sometimes the default serialization doesn't work for you. An example is Hibernate serialization where you get infinite -recursion on Lists/Sets. - -You can supply any serializer you like Gson, Jackson or something else. - -For example, the following will exclude the rendering of Lists without changing the source code to include `@JsonIgnore` -. This is good because you shouldn't need to add annotations to your source code for testing purposes only. - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.jackson.serializers.DeterministicJacksonSnapshotSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreType; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.time.Instant; -import java.util.List; -import java.util.Set; - -public class HibernateSnapshotSerializer extends DeterministicJacksonSnapshotSerializer { - - @Override - public void configure(ObjectMapper objectMapper) { - super.configure(objectMapper); - - // Ignore Hibernate Lists to prevent infinite recursion - objectMapper.addMixIn(List.class, IgnoreTypeMixin.class); - objectMapper.addMixIn(Set.class, IgnoreTypeMixin.class); - - // Ignore Fields that Hibernate generates for us automatically - objectMapper.addMixIn(BaseEntity.class, IgnoreHibernateEntityFields.class); - } - - @JsonIgnoreType - class IgnoreTypeMixin { - } - - abstract class IgnoreHibernateEntityFields { - @JsonIgnore - abstract Long getId(); - - @JsonIgnore - abstract Instant getCreatedDate(); - - @JsonIgnore - abstract Instant getLastModifiedDate(); - } -} -``` - -## Supplying a custom SnapshotComparator - -The comparator determines if two snapshots match. - -Currently, we support one default comparator (`PlainTextEqualsComparator`) which uses string equals for comparison. - -This should work for most cases. Custom implementations of `SnapshotComparator` can provide more advanced comparisons. - -Comparators follow the same resolution order as Serializers - -1. method -1. class -1. snapshot.properties - -### Example: JsonObjectComparator - -The default comparator may be too strict for certain types of data. For example, when comparing json objects, formatting -of the json string or the order of fields may not be of much importance during comparison. A custom comparator can help -in such cases. - -For example, the following will convert a json string to a Map and then perform an equals comparison so that formatting -and field order are ignored. - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.comparators.SnapshotComparator; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.SneakyThrows; - -public class JsonObjectComparator implements SnapshotComparator { - @Override - public boolean matches(Snapshot previous, Snapshot current) { - return asObject(previous.getName(), previous.getBody()).equals(asObject(current.getName(), current.getBody())); - } - - @SneakyThrows - private static Object asObject(String snapshotName, String json) { - return new ObjectMapper().readValue(json.replaceFirst(snapshotName + "=", ""), Object.class); - } -} -``` - -## Supplying a custom SnapshotReporter - -The reporter reports the details of comparison failures. - -Currently, we support one default reporter (`PlainTextSnapshotReporter`) which uses assertj's DiffUtils to generate a -patch of the differences between two snapshots. - -Custom reporters can be plugged in by implementing `SnapshotReporter`. - -Reporters follow the same resolution order as Serializers and Comparators - -### Example: JsonDiffReporter - -For generating and reporting json diffs using other libraries like https://github.com/skyscreamer/JSONassert -a custom reporter can be created like the one below. - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.reporters.SnapshotReporter; -import au.com.origin.snapshots.serializers.SerializerType; -import lombok.SneakyThrows; -import org.skyscreamer.jsonassert.JSONAssert; -import org.skyscreamer.jsonassert.JSONCompareMode; - -public class JsonAssertReporter implements SnapshotReporter { - @Override - public boolean supportsFormat(String outputFormat) { - return SerializerType.JSON.name().equalsIgnoreCase(outputFormat); - } - - @Override - @SneakyThrows - public void report(Snapshot previous, Snapshot current) { - JSONAssert.assertEquals(previous.getBody(), current.getBody(), JSONCompareMode.STRICT); - } -} -``` - -## Snapshot Headers -You can add metadata to your snapshots via headers. Headers can be used by Serializers, Comparators & Reporters -to help interrogate the snapshot. - -Custom Serializers can also inject default headers as needed. - -Example of injecting a header manually -```java -String obj = "hello" -expect - .header("className", obj.getClass().getName()) - .header("foo", "bar") - .toMatchSnapshot(obj); -``` - -Snapshot output -```text -au.com.origin.snapshots.SnapshotHeaders.canAddHeaders={ - "className": "java.lang.String", - "foo": "bar" -}[ -hello -] -``` - -## Supplying a custom SnapshotConfig - -You can override the snapshot configuration easily using the `@UseSnapshotConfig` annotation - -**JUnit5 Example** - -```java -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.annotations.UseSnapshotConfig; -import au.com.origin.snapshots.junit5.SnapshotExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith(SnapshotExtension.class) -@UseSnapshotConfig(LowercaseToStringSnapshotConfig.class) -public class JUnit5ResolutionHierarchyExample { - - private Expect expect; - - @Test - public void aliasMethodTest() { - expect - .serializer("json") // <------ Using snapshot.properties - .toMatchSnapshot(new TestObject()); - } - - @Test - public void customSerializerTest() { - expect - .serializer(UppercaseToStringSerializer.class) // <------ Using custom serializer - .toMatchSnapshot(new TestObject()); - } - - // Read from LowercaseToStringSnapshotConfig defined on the class - @Test - public void lowercaseTest() { - expect.toMatchSnapshot(new TestObject()); - } -} -``` - -# Troubleshooting - -**I'm seeing this error in my logs** - -``` -org/slf4j/LoggerFactory -java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory -``` - -Solution: -Add an SLF4J Provider such as`testImplementation("org.slf4j:slf4j-simple:2.0.0-alpha0")` - -**My test source files are not in `src/test/java`** - -Solution: Override `output-dir` in `snapshot.properties` - -**I see the following error in JSON snapshots `java.lang.NoSuchFieldError: BINARY`** - -Solution: This happened to me in a spring-boot app, I removed my jackson dependencies and relied on the ones from -spring-boot instead. - -# Contributing - -see `CONTRIBUTING.md` +- Inspired by [facebook's Jest framework](https://facebook.github.io/jest/docs/en/snapshot-testing.html) +- Fork of https://github.com/origin-energy/java-snapshot-testing \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 20ec5f8..0000000 --- a/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -plugins { - id 'net.researchgate.release' version '2.6.0' apply false - id 'com.github.johnrengelman.shadow' version '5.2.0' apply false - id 'io.codearte.nexus-staging' version '0.22.0' - id "com.diffplug.spotless" version "6.11.0" apply false -} - -subprojects { subproject -> - // FIXME this plugin is currently not working for multi-module projects even when defined at the top level only - // Will need to release and TAG manually for now - subproject.apply plugin: 'net.researchgate.release' - subproject.apply plugin: 'java-library' - subproject.apply plugin: 'com.github.johnrengelman.shadow' - subproject.apply plugin: 'maven-publish' - - sourceCompatibility = '1.8' - targetCompatibility = '1.8' - - repositories { - mavenCentral() - } - - shadowJar { - classifier = '' - relocate 'org.assertj', 'shadow.org.assertj' - relocate 'org.opentest4j', 'shadow.org.opentest4j' - exclude "module-info.class" - } - - dependencies { - // Lombok - compileOnly 'org.projectlombok:lombok:1.18.20' - annotationProcessor 'org.projectlombok:lombok:1.18.20' - testCompileOnly 'org.projectlombok:lombok:1.18.20' - testAnnotationProcessor 'org.projectlombok:lombok:1.18.20' - - // Logging implementation - compileOnly 'org.slf4j:slf4j-api:2.0.0-alpha0' - } - - // Add verbose logging for Github Actions - test { - testLogging { - events "passed", "skipped", "failed" - exceptionFormat "full" - } - } -} diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 7321d68..0000000 --- a/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -group=io.github.origin-energy -version=0.0.0-SNAPSHOT diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle deleted file mode 100644 index 5fd7c68..0000000 --- a/gradle/publishing.gradle +++ /dev/null @@ -1,88 +0,0 @@ -task javadocJar(type: Jar) { - archiveClassifier.set('javadoc') - from javadoc -} - -task sourcesJar(type: Jar) { - archiveClassifier.set('sources') - from sourceSets.main.allSource -} - -artifacts { - archives javadocJar, sourcesJar -} - -apply plugin: 'net.researchgate.release' - -// Sign for maven central deployment -if (project.hasProperty("sign")) { - apply plugin: 'signing' - signing { - def signingKey = findProperty("signingKey") - def signingPassword = findProperty("signingPassword") - useInMemoryPgpKeys(signingKey, signingPassword) - sign configurations.archives - } - - // previously the plugin was using the `build` version which did not include the shadowed dependencies - signArchives.dependsOn 'shadowJar' -} - -// Maven Central Publishing -if (project.hasProperty("ossrhUsername") && project.hasProperty("ossrhPassword")) { - apply plugin: 'maven' - - nexusStaging { - username project.property("ossrhUsername") - password project.property("ossrhPassword") - packageGroup 'io.github.origin-energy' - } - - uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { - authentication(userName: ossrhUsername, password: ossrhPassword) - } - - snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { - authentication(userName: ossrhUsername, password: ossrhPassword) - } - - pom.project { - name 'java-snapsho-testing' - packaging 'jar' - description 'Snapshot Testing for Java' - url 'https://github.com/origin-energy/java-snapshot-testing' - scm { - connection 'scm:git:https://github.com/origin-energy/java-snapshot-testing' - url 'https://github.com/origin-energy/java-snapshot-testing' - } - - licenses { - license { - name 'MIT License' - url 'http://www.opensource.org/licenses/mit-license.php' - } - } - - developers { - developer { - id 'jack.matthews' - name 'Jack Matthews' - email 'jack.matthews@origin.com.au' - } - } - } - - // We clear out all the dependencies because we have shadowed them inside the jar - pom.whenConfigured { - p -> p.dependencies = [] - } - } - } - } -} - diff --git a/gradle/spotless-groovy.gradle b/gradle/spotless-groovy.gradle deleted file mode 100644 index 2e4b04f..0000000 --- a/gradle/spotless-groovy.gradle +++ /dev/null @@ -1,10 +0,0 @@ -apply plugin: 'com.diffplug.spotless' - -// Code formatting -spotless { - groovy { - importOrder() - excludeJava() - greclipse() - } -} \ No newline at end of file diff --git a/gradle/spotless.gradle b/gradle/spotless.gradle deleted file mode 100644 index 24b8ba0..0000000 --- a/gradle/spotless.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'com.diffplug.spotless' - -// Code formatting -spotless { - java { - importOrder() - removeUnusedImports() - googleJavaFormat() - formatAnnotations() - } -} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 5c2d1cf..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index d355f4c..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.3-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index 83f2acf..0000000 --- a/gradlew +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 24467a1..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,100 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java-snapshot-testing-core/.gitignore b/java-snapshot-testing-core/.gitignore deleted file mode 100644 index be3f6ef..0000000 --- a/java-snapshot-testing-core/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -src/test/java/au/com/origin/snapshots/__snapshots__/ -src/test/some-folder/ -anyFilePath.debug -blah \ No newline at end of file diff --git a/java-snapshot-testing-core/build.gradle b/java-snapshot-testing-core/build.gradle deleted file mode 100644 index 7b4e6c3..0000000 --- a/java-snapshot-testing-core/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -apply from: "../gradle/publishing.gradle" -apply from: "../gradle/spotless.gradle" - -dependencies { - - compileOnly 'com.fasterxml.jackson.core:jackson-core:2.11.3' - compileOnly 'com.fasterxml.jackson.core:jackson-databind:2.11.3' - - implementation 'org.assertj:assertj-core:3.11.1' - implementation 'org.opentest4j:opentest4j:1.2.0' - - testImplementation 'org.slf4j:slf4j-simple:2.0.0-alpha0' - testImplementation 'org.mockito:mockito-junit-jupiter:2.23.0' - testImplementation 'org.mockito:mockito-core:2.23.4' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.3.2' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.3.2' - testImplementation group: 'commons-io', name: 'commons-io', version: '2.6' -} - -test { useJUnitPlatform() } - -publishing { - publications { - myPublication(MavenPublication) { - artifact shadowJar - groupId 'io.github.origin-energy' - artifactId 'java-snapshot-testing-core' - } - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Expect.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Expect.java deleted file mode 100644 index 7269121..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Expect.java +++ /dev/null @@ -1,172 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.comparators.SnapshotComparator; -import au.com.origin.snapshots.reporters.SnapshotReporter; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; - -@RequiredArgsConstructor -public class Expect { - private final SnapshotVerifier snapshotVerifier; - private final Method testMethod; - - private SnapshotSerializer snapshotSerializer; - private SnapshotComparator snapshotComparator; - private List snapshotReporters; - private String scenario; - - private final Map headers = new HashMap<>(); - - public static Expect of(SnapshotVerifier snapshotVerifier, Method method) { - return new Expect(snapshotVerifier, method); - } - - /** - * Make an assertion on the given input parameters against what already exists - * - *

If you were previously using varargs and see an error - you can fix the error using - * "toMatchSnapshotLegacy", however, a better approach is to use the ".scenario()" feature as - * future versions of this library will most likely remove the legacy implementation completely. - * - * @param object snapshot object - */ - public void toMatchSnapshot(Object object) { - SnapshotContext snapshotContext = snapshotVerifier.expectCondition(testMethod, object); - if (snapshotSerializer != null) { - snapshotContext.setSnapshotSerializer(snapshotSerializer); - } - if (snapshotComparator != null) { - snapshotContext.setSnapshotComparator(snapshotComparator); - } - if (snapshotReporters != null) { - snapshotContext.setSnapshotReporters(snapshotReporters); - } - if (scenario != null) { - snapshotContext.setScenario(scenario); - } - snapshotContext.header.putAll(headers); - - snapshotContext.checkValidContext(); - - snapshotContext.toMatchSnapshot(); - } - - /** - * Normally a snapshot can be applied only once to a test method. - * - *

For Parameterized tests where the same method is executed multiple times you can supply the - * scenario() to overcome this restriction. Ensure each scenario is unique. - * - * @param scenario - unique scenario description - * @return Snapshot - */ - public Expect scenario(String scenario) { - this.scenario = scenario; - return this; - } - - /** - * Apply a custom serializer for this snapshot - * - * @param serializer your custom serializer - * @return Snapshot - */ - public Expect serializer(SnapshotSerializer serializer) { - this.snapshotSerializer = serializer; - return this; - } - - /** - * Apply a custom serializer for this snapshot - * - * @param name - the {name} attribute serializer.{name} from snapshot.properties - * @return Snapshot - */ - public Expect serializer(String name) { - this.snapshotSerializer = SnapshotProperties.getInstance("serializer." + name); - return this; - } - - /** - * Apply a custom comparator for this snapshot - * - * @param comparator your custom comparator - * @return Snapshot - */ - public Expect comparator(SnapshotComparator comparator) { - this.snapshotComparator = comparator; - return this; - } - - /** - * Apply a custom comparator for this snapshot - * - * @param name the {name} attribute comparator.{name} from snapshot.properties - * @return Snapshot - */ - public Expect comparator(String name) { - this.snapshotComparator = SnapshotProperties.getInstance("comparator." + name); - return this; - } - - /** - * Apply a list of custom reporters for this snapshot This will replace the default reporters - * defined in the config - * - * @param reporters your custom reporters - * @return Snapshot - */ - public Expect reporters(SnapshotReporter... reporters) { - this.snapshotReporters = Arrays.asList(reporters); - return this; - } - - /** - * Apply a list of custom reporters for this snapshot This will replace the default reporters - * defined in the config - * - * @param name the {name} attribute reporters.{name} from snapshot.properties - * @return Snapshot - */ - public Expect reporters(String name) { - this.snapshotReporters = SnapshotProperties.getInstances("reporters." + name); - return this; - } - - /** - * Apply a custom serializer for this snapshot. - * - * @param serializer your custom serializer - * @return this - * @see au.com.origin.snapshots.serializers.SnapshotSerializer - *

Example implementations - * @see au.com.origin.snapshots.serializers.ToStringSnapshotSerializer - * @see au.com.origin.snapshots.serializers.Base64SnapshotSerializer - */ - @SneakyThrows - public Expect serializer(Class serializer) { - this.snapshotSerializer = serializer.getConstructor().newInstance(); - return this; - } - - /** - * Add anything you like to the snapshot header. - * - *

These custom headers can be used in serializers, comparators or reporters to change how they - * behave. - * - * @param key key - * @param value value - * @return Expect - */ - public Expect header(String key, String value) { - headers.put(key, value); - return this; - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java deleted file mode 100644 index bc57d4d..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java +++ /dev/null @@ -1,80 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.exceptions.LogGithubIssueException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@EqualsAndHashCode -@Builder -@Getter -@RequiredArgsConstructor -public class Snapshot implements Comparable { - - private final String name; - private final String scenario; - private final SnapshotHeader header; - private final String body; - - @Override - public int compareTo(Snapshot other) { - return (name + scenario).compareTo(other.name + other.scenario); - } - - public String getIdentifier() { - return scenario == null ? name : String.format("%s[%s]", name, scenario); - } - - public static Snapshot parse(String rawText) { - String regex = - "^(?.*?)(\\[(?[^]]*)])?=(?

\\{[^}]*?})?(?(.*)$)"; - Pattern p = Pattern.compile(regex, Pattern.DOTALL); - Matcher m = p.matcher(rawText); - boolean found = m.find(); - if (!found) { - throw new LogGithubIssueException( - "Corrupt Snapshot (REGEX matches = 0): possibly due to manual editing or our REGEX failing\n" - + "Possible Solutions\n" - + "1. Ensure you have not accidentally manually edited the snapshot file!\n" - + "2. Compare the snapshot with GIT history"); - } - - String name = m.group("name"); - String scenario = m.group("scenario"); - String header = m.group("header"); - String snapshot = m.group("snapshot"); - - if (name == null || snapshot == null) { - throw new LogGithubIssueException( - "Corrupt Snapshot (REGEX name or snapshot group missing): possibly due to manual editing or our REGEX failing\n" - + "Possible Solutions\n" - + "1. Ensure you have not accidentally manually edited the snapshot file\n" - + "2. Compare the snapshot with your version control history"); - } - - return Snapshot.builder() - .name(name) - .scenario(scenario) - .header(SnapshotHeader.fromJson(header)) - .body(snapshot) - .build(); - } - - /** - * The raw string representation of the snapshot as it would appear in the *.snap file. - * - * @return raw snapshot - */ - public String raw() { - String headerJson = (header == null) || (header.size() == 0) ? "" : header.toJson(); - return getIdentifier() + "=" + headerJson + body; - } - - @Override - public String toString() { - return raw(); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotContext.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotContext.java deleted file mode 100644 index ae0398e..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotContext.java +++ /dev/null @@ -1,173 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.comparators.SnapshotComparator; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.exceptions.ReservedWordException; -import au.com.origin.snapshots.exceptions.SnapshotExtensionException; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import au.com.origin.snapshots.reporters.SnapshotReporter; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import java.lang.reflect.Method; -import java.util.*; -import java.util.stream.Collectors; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class SnapshotContext { - - private static final List RESERVED_WORDS = Arrays.asList("=", "[", "]"); - - private final SnapshotConfig snapshotConfig; - private final SnapshotFile snapshotFile; - - @Getter final Class testClass; - - @Getter final Method testMethod; - - final Object current; - private final boolean isCI; - - @Setter private SnapshotSerializer snapshotSerializer; - @Setter private SnapshotComparator snapshotComparator; - @Setter private List snapshotReporters; - - @Setter @Getter String scenario; - - @Getter SnapshotHeader header = new SnapshotHeader(); - - SnapshotContext( - SnapshotConfig snapshotConfig, - SnapshotFile snapshotFile, - Class testClass, - Method testMethod, - Object current) { - this.snapshotConfig = snapshotConfig; - this.snapshotFile = snapshotFile; - this.testClass = testClass; - this.testMethod = testMethod; - this.current = current; - - this.isCI = snapshotConfig.isCI(); - this.snapshotSerializer = snapshotConfig.getSerializer(); - this.snapshotComparator = snapshotConfig.getComparator(); - this.snapshotReporters = snapshotConfig.getReporters(); - this.scenario = null; - } - - public void toMatchSnapshot() { - - Set rawSnapshots = snapshotFile.getSnapshots(); - Snapshot previousSnapshot = getRawSnapshot(rawSnapshots); - Snapshot currentSnapshot = takeSnapshot(); - - if (previousSnapshot != null && shouldUpdateSnapshot()) { - snapshotFile.getSnapshots().remove(previousSnapshot); - previousSnapshot = null; - } - - if (previousSnapshot != null) { - snapshotFile.pushDebugSnapshot(currentSnapshot); - - // Match existing Snapshot - if (!snapshotComparator.matches(previousSnapshot, currentSnapshot)) { - snapshotFile.createDebugFile(currentSnapshot); - - List reporters = - snapshotReporters.stream() - .filter(reporter -> reporter.supportsFormat(snapshotSerializer.getOutputFormat())) - .collect(Collectors.toList()); - - if (reporters.isEmpty()) { - String comparator = snapshotComparator.getClass().getSimpleName(); - throw new IllegalStateException( - "No compatible reporters found for comparator " + comparator); - } - - List errors = new ArrayList<>(); - - for (SnapshotReporter reporter : reporters) { - try { - reporter.report(previousSnapshot, currentSnapshot); - } catch (Throwable t) { - errors.add(t); - } - } - - if (!errors.isEmpty()) { - throw new SnapshotMatchException("Error(s) matching snapshot(s)", errors); - } - } - } else { - if (this.isCI) { - log.error( - "We detected you are running on a CI Server - if this is incorrect please override the isCI() method in SnapshotConfig"); - throw new SnapshotMatchException( - "Snapshot [" - + resolveSnapshotIdentifier() - + "] not found. Has this snapshot been committed ?"); - } else { - log.warn( - "We detected you are running on a developer machine - if this is incorrect please override the isCI() method in SnapshotConfig"); - // Create New Snapshot - snapshotFile.pushSnapshot(currentSnapshot); - snapshotFile.pushDebugSnapshot(currentSnapshot); - } - } - } - - private boolean shouldUpdateSnapshot() { - if (snapshotConfig.updateSnapshot().isPresent() && snapshotConfig.isCI()) { - throw new SnapshotExtensionException( - "isCI=true & update-snapshot=" - + snapshotConfig.updateSnapshot() - + ". Updating snapshots on CI is not allowed"); - } - if (snapshotConfig.updateSnapshot().isPresent()) { - return resolveSnapshotIdentifier().contains(snapshotConfig.updateSnapshot().get()); - } else { - return false; - } - } - - private Snapshot getRawSnapshot(Collection rawSnapshots) { - synchronized (rawSnapshots) { - for (Snapshot rawSnapshot : rawSnapshots) { - if (rawSnapshot.getIdentifier().equals(resolveSnapshotIdentifier())) { - return rawSnapshot; - } - } - } - return null; - } - - private Snapshot takeSnapshot() { - SnapshotSerializerContext sg = SnapshotSerializerContext.from(this); - return snapshotSerializer.apply(current, sg); - } - - String resolveSnapshotIdentifier() { - String scenarioFormat = scenario == null ? "" : "[" + scenario + "]"; - return snapshotName() + scenarioFormat; - } - - private String snapshotName() { - SnapshotName snapshotName = testMethod.getAnnotation(SnapshotName.class); - return snapshotName == null - ? testClass.getName() + "." + testMethod.getName() - : snapshotName.value(); - } - - void checkValidContext() { - for (String rw : RESERVED_WORDS) { - if (snapshotName().contains(rw)) { - throw new ReservedWordException("snapshot name", rw, RESERVED_WORDS); - } - if (scenario != null && scenario.contains(rw)) { - throw new ReservedWordException("scenario name", rw, RESERVED_WORDS); - } - } - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotFile.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotFile.java deleted file mode 100644 index 079476f..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotFile.java +++ /dev/null @@ -1,169 +0,0 @@ -package au.com.origin.snapshots; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.Getter; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class SnapshotFile { - - public static final String SPLIT_STRING = "\n\n\n"; - - private final String fileName; - private final Class testClass; - @Getter private Set snapshots = Collections.synchronizedSortedSet(new TreeSet<>()); - private Set debugSnapshots = Collections.synchronizedSortedSet(new TreeSet<>()); - - public SnapshotFile(String srcDirPath, String fileName, Class testClass) throws IOException { - this.testClass = testClass; - this.fileName = srcDirPath + File.separator + fileName; - log.info("Snapshot File: " + this.fileName); - - StringBuilder fileContent = new StringBuilder(); - - try (BufferedReader br = - new BufferedReader( - new InputStreamReader(new FileInputStream(this.fileName), StandardCharsets.UTF_8))) { - - String sCurrentLine; - - while ((sCurrentLine = br.readLine()) != null) { - fileContent.append(sCurrentLine + "\n"); - } - - String fileText = fileContent.toString(); - if (!"".equals(fileText.trim())) { - snapshots = - Collections.synchronizedSortedSet( - Stream.of(fileContent.toString().split(SPLIT_STRING)) - .map(String::trim) - .map(Snapshot::parse) - .collect(Collectors.toCollection(TreeSet::new))); - } - } catch (IOException e) { - // ... - } - - deleteDebugFile(); - } - - private String getDebugFilename() { - return this.fileName + ".debug"; - } - - public File createDebugFile(Snapshot snapshot) { - File file = null; - try { - file = new File(getDebugFilename()); - file.getParentFile().mkdirs(); - file.createNewFile(); - - try (FileOutputStream fileStream = new FileOutputStream(file, false)) { - fileStream.write(snapshot.raw().getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - e.printStackTrace(); - } - } catch (IOException e) { - e.printStackTrace(); - } - - return file; - } - - @SneakyThrows - public void deleteDebugFile() { - Files.deleteIfExists(Paths.get(getDebugFilename())); - } - - @SneakyThrows - public void delete() { - Files.deleteIfExists(Paths.get(this.fileName)); - } - - @SneakyThrows - public synchronized File createFileIfNotExists(String filename) { - Path path = Paths.get(filename); - if (!Files.exists(path)) { - Files.createDirectories(path.getParent()); - Files.createFile(path); - } - return path.toFile(); - } - - public void pushSnapshot(Snapshot snapshot) { - synchronized (snapshots) { - snapshots.add(snapshot); - TreeSet rawSnapshots = - snapshots.stream().map(Snapshot::raw).collect(Collectors.toCollection(TreeSet::new)); - updateFile(this.fileName, rawSnapshots); - } - } - - public synchronized void pushDebugSnapshot(Snapshot snapshot) { - debugSnapshots.add(snapshot); - TreeSet rawDebugSnapshots = - debugSnapshots.stream().map(Snapshot::raw).collect(Collectors.toCollection(TreeSet::new)); - updateFile(getDebugFilename(), rawDebugSnapshots); - } - - private void updateFile(String fileName, Set rawSnapshots) { - File file = createFileIfNotExists(fileName); - try (FileOutputStream fileStream = new FileOutputStream(file, false)) { - byte[] myBytes = String.join(SPLIT_STRING, rawSnapshots).getBytes(StandardCharsets.UTF_8); - fileStream.write(myBytes); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @SneakyThrows - public void cleanup() { - Path path = Paths.get(this.fileName); - if (Files.exists(path)) { - if (Files.size(path) == 0 || snapshotsAreTheSame()) { - deleteDebugFile(); - } - - if (Files.size(path) == 0) { - delete(); - } else { - String content = - new String(Files.readAllBytes(Paths.get(this.fileName)), StandardCharsets.UTF_8); - Files.write( - path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING); - } - } - } - - @SneakyThrows - private boolean snapshotsAreTheSame() { - Path path = Paths.get(this.getDebugFilename()); - if (Files.exists(path)) { - List snapshotFileContent = - Files.readAllLines(Paths.get(this.fileName), StandardCharsets.UTF_8); - List debugSnapshotFileContent = - Files.readAllLines(Paths.get(this.getDebugFilename()), StandardCharsets.UTF_8); - return Objects.equals(snapshotFileContent, debugSnapshotFileContent); - } - - return false; - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotHeader.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotHeader.java deleted file mode 100644 index 1d0419d..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotHeader.java +++ /dev/null @@ -1,48 +0,0 @@ -package au.com.origin.snapshots; - -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; - -@RequiredArgsConstructor -public class SnapshotHeader extends HashMap { - - // - // Manual JSON serialization/deserialization as I don't want to - // include another dependency for it - // - @SneakyThrows - public String toJson() { - StringBuilder b = new StringBuilder(); - b.append("{\n"); - final int lastIndex = this.size(); - int currIndex = 0; - for (Map.Entry entry : this.entrySet()) { - currIndex++; - String format = currIndex == lastIndex ? " \"%s\": \"%s\"\n" : " \"%s\": \"%s\",\n"; - b.append(String.format(format, entry.getKey(), entry.getValue())); - } - b.append("}"); - return b.toString(); - } - - @SneakyThrows - public static SnapshotHeader fromJson(String json) { - SnapshotHeader snapshotHeader = new SnapshotHeader(); - - if (json == null) { - return snapshotHeader; - } - - String regex = "\\\"(?.*)\\\": \\\"(?.*)\\\""; - Pattern p = Pattern.compile(regex); - Matcher m = p.matcher(json); - while (m.find()) { - snapshotHeader.put(m.group("key"), m.group("value")); - } - return snapshotHeader; - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotProperties.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotProperties.java deleted file mode 100644 index 3833b7a..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotProperties.java +++ /dev/null @@ -1,58 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.exceptions.MissingSnapshotPropertiesKeyException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.List; -import java.util.Properties; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public enum SnapshotProperties { - INSTANCE; - - Properties snapshotProperties = new Properties(); - - SnapshotProperties() { - try { - InputStream in = - SnapshotProperties.class.getClassLoader().getResourceAsStream("snapshot.properties"); - snapshotProperties.load(in); - } catch (Exception e) { - // It's ok, if the SnapshotConfig implementation attempts to get a property they will receive - // a MissingSnapshotPropertiesKeyException - } - } - - public static String getOrThrow(String key) { - Object value = INSTANCE.snapshotProperties.get(key); - if (value == null) { - throw new MissingSnapshotPropertiesKeyException(key); - } - return value.toString(); - } - - public static T getInstance(String key) { - String value = SnapshotProperties.getOrThrow(key); - return createInstance(value); - } - - public static List getInstances(String key) { - String value = SnapshotProperties.getOrThrow(key); - return Arrays.stream(value.split(",")) - .map(String::trim) - .map(it -> (T) createInstance(it)) - .collect(Collectors.toList()); - } - - @SuppressWarnings("unchecked") - private static T createInstance(String className) { - try { - Class clazz = Class.forName(className); - return (T) clazz.newInstance(); - } catch (Exception e) { - throw new RuntimeException("Unable to instantiate class " + className, e); - } - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotSerializerContext.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotSerializerContext.java deleted file mode 100644 index b19c31e..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotSerializerContext.java +++ /dev/null @@ -1,43 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.annotations.SnapshotName; -import java.lang.reflect.Method; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -/** - * Contains details of the pending snapshot that can be modified in the Serializer prior to calling - * toSnapshot(). - */ -@AllArgsConstructor -public class SnapshotSerializerContext { - - @Getter @Setter private String name; - - @Getter @Setter private String scenario; - - @Getter @Setter private SnapshotHeader header; - - @Getter private Class testClass; - - @Getter private final Method testMethod; - - public static SnapshotSerializerContext from(SnapshotContext context) { - SnapshotName snapshotName = context.getTestMethod().getAnnotation(SnapshotName.class); - String name = - snapshotName == null - ? context.getTestClass().getName() + "." + context.getTestMethod().getName() - : snapshotName.value(); - return new SnapshotSerializerContext( - name, - context.getScenario(), - context.getHeader(), - context.getTestClass(), - context.getTestMethod()); - } - - public Snapshot toSnapshot(String body) { - return Snapshot.builder().name(name).scenario(scenario).header(header).body(body).build(); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java deleted file mode 100644 index e6e0f93..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java +++ /dev/null @@ -1,146 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.annotations.UseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.exceptions.SnapshotExtensionException; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.*; -import java.util.regex.Matcher; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@RequiredArgsConstructor -public class SnapshotVerifier { - - private final Class testClass; - private final SnapshotFile snapshotFile; - private final SnapshotConfig config; - private final boolean failOnOrphans; - - private final Collection calledSnapshots = - Collections.synchronizedCollection(new ArrayList<>()); - - public SnapshotVerifier(SnapshotConfig frameworkSnapshotConfig, Class testClass) { - this(frameworkSnapshotConfig, testClass, false); - } - - /** - * Instantiate before any tests have run for a given class - * - * @param frameworkSnapshotConfig configuration to use - * @param failOnOrphans should the test break if snapshots exist with no matching method in the - * test class - * @param testClass reference to class under test - */ - public SnapshotVerifier( - SnapshotConfig frameworkSnapshotConfig, Class testClass, boolean failOnOrphans) { - try { - verifyNoConflictingSnapshotNames(testClass); - - UseSnapshotConfig customConfig = testClass.getAnnotation(UseSnapshotConfig.class); - SnapshotConfig snapshotConfig = - customConfig == null ? frameworkSnapshotConfig : customConfig.value().newInstance(); - - // Matcher.quoteReplacement required for Windows - String testFilename = - testClass.getName().replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + ".snap"; - - File fileUnderTest = new File(testFilename); - File snapshotDir = new File(fileUnderTest.getParentFile(), snapshotConfig.getSnapshotDir()); - - // Support legacy trailing space syntax - String testSrcDir = snapshotConfig.getOutputDir(); - String testSrcDirNoTrailing = - testSrcDir.endsWith("/") ? testSrcDir.substring(0, testSrcDir.length() - 1) : testSrcDir; - SnapshotFile snapshotFile = - new SnapshotFile( - testSrcDirNoTrailing, - snapshotDir.getPath() + File.separator + fileUnderTest.getName(), - testClass); - - this.testClass = testClass; - this.snapshotFile = snapshotFile; - this.config = snapshotConfig; - this.failOnOrphans = failOnOrphans; - - } catch (IOException | InstantiationException | IllegalAccessException e) { - throw new SnapshotExtensionException(e.getMessage()); - } - } - - private void verifyNoConflictingSnapshotNames(Class testClass) { - Map> allSnapshotAnnotationNames = - Arrays.stream(testClass.getDeclaredMethods()) - .filter(it -> it.isAnnotationPresent(SnapshotName.class)) - .map(it -> it.getAnnotation(SnapshotName.class)) - .map(SnapshotName::value) - .collect(Collectors.groupingBy(String::toString)); - - boolean hasDuplicateSnapshotNames = - allSnapshotAnnotationNames.entrySet().stream() - .filter(it -> it.getValue().size() > 1) - .peek( - it -> - log.error( - "Oops, looks like you set the same name of two separate snapshots @SnapshotName(\"{}\") in class {}", - it.getKey(), - testClass.getName())) - .count() - > 0; - if (hasDuplicateSnapshotNames) { - throw new SnapshotExtensionException("Duplicate @SnapshotName annotations found!"); - } - } - - @SneakyThrows - public SnapshotContext expectCondition(Method testMethod, Object object) { - SnapshotContext snapshotContext = - new SnapshotContext(config, snapshotFile, testClass, testMethod, object); - calledSnapshots.add(snapshotContext); - return snapshotContext; - } - - public void validateSnapshots() { - Set rawSnapshots = snapshotFile.getSnapshots(); - Set snapshotNames = - calledSnapshots.stream() - .map(SnapshotContext::resolveSnapshotIdentifier) - .collect(Collectors.toSet()); - List unusedSnapshots = new ArrayList<>(); - - for (Snapshot rawSnapshot : rawSnapshots) { - boolean foundSnapshot = false; - for (String snapshotName : snapshotNames) { - if (rawSnapshot.getIdentifier().equals(snapshotName)) { - foundSnapshot = true; - break; - } - } - if (!foundSnapshot) { - unusedSnapshots.add(rawSnapshot); - } - } - if (unusedSnapshots.size() > 0) { - List unusedRawSnapshots = - unusedSnapshots.stream().map(Snapshot::raw).collect(Collectors.toList()); - String errorMessage = - "All unused Snapshots:\n" - + String.join("\n", unusedRawSnapshots) - + "\n\nHave you deleted tests? Have you renamed a test method?"; - if (failOnOrphans) { - log.error(errorMessage); - throw new SnapshotMatchException("ERROR: Found orphan snapshots"); - } else { - log.warn(errorMessage); - } - } - snapshotFile.cleanup(); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/annotations/SnapshotName.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/annotations/SnapshotName.java deleted file mode 100644 index 2fa029e..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/annotations/SnapshotName.java +++ /dev/null @@ -1,11 +0,0 @@ -package au.com.origin.snapshots.annotations; - -import java.lang.annotation.*; - -@Documented -@Target({ElementType.METHOD}) -@Inherited -@Retention(RetentionPolicy.RUNTIME) -public @interface SnapshotName { - String value(); -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/annotations/UseSnapshotConfig.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/annotations/UseSnapshotConfig.java deleted file mode 100644 index 61e4453..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/annotations/UseSnapshotConfig.java +++ /dev/null @@ -1,12 +0,0 @@ -package au.com.origin.snapshots.annotations; - -import au.com.origin.snapshots.config.SnapshotConfig; -import java.lang.annotation.*; - -@Documented -@Target({ElementType.TYPE}) -@Inherited -@Retention(RetentionPolicy.RUNTIME) -public @interface UseSnapshotConfig { - Class value(); -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java deleted file mode 100644 index c5f8925..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java +++ /dev/null @@ -1,17 +0,0 @@ -package au.com.origin.snapshots.comparators; - -import au.com.origin.snapshots.logging.LoggingHelper; -import lombok.extern.slf4j.Slf4j; - -@Deprecated -@Slf4j -public class PlainTextEqualsComparator - extends au.com.origin.snapshots.comparators.v1.PlainTextEqualsComparator { - - public PlainTextEqualsComparator() { - super(); - LoggingHelper.deprecatedV5( - log, - "Update to `au.com.origin.snapshots.comparators.v1.PlainTextEqualsComparator` in `snapshot.properties`"); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java deleted file mode 100644 index 84a9899..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java +++ /dev/null @@ -1,7 +0,0 @@ -package au.com.origin.snapshots.comparators; - -import au.com.origin.snapshots.Snapshot; - -public interface SnapshotComparator { - boolean matches(Snapshot previous, Snapshot current); -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/v1/PlainTextEqualsComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/v1/PlainTextEqualsComparator.java deleted file mode 100644 index e987613..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/v1/PlainTextEqualsComparator.java +++ /dev/null @@ -1,12 +0,0 @@ -package au.com.origin.snapshots.comparators.v1; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.comparators.SnapshotComparator; - -public class PlainTextEqualsComparator implements SnapshotComparator { - - @Override - public boolean matches(Snapshot previous, Snapshot current) { - return previous.getBody().equals(current.getBody()); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/config/PropertyResolvingSnapshotConfig.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/config/PropertyResolvingSnapshotConfig.java deleted file mode 100644 index 05d4a8a..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/config/PropertyResolvingSnapshotConfig.java +++ /dev/null @@ -1,77 +0,0 @@ -package au.com.origin.snapshots.config; - -import au.com.origin.snapshots.SnapshotProperties; -import au.com.origin.snapshots.comparators.SnapshotComparator; -import au.com.origin.snapshots.exceptions.MissingSnapshotPropertiesKeyException; -import au.com.origin.snapshots.logging.LoggingHelper; -import au.com.origin.snapshots.reporters.SnapshotReporter; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import java.util.List; -import java.util.Optional; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class PropertyResolvingSnapshotConfig implements SnapshotConfig { - - @Override - public String getOutputDir() { - return SnapshotProperties.getOrThrow("output-dir"); - } - - @Override - public String getSnapshotDir() { - return SnapshotProperties.getOrThrow("snapshot-dir"); - } - - @Override - public Optional updateSnapshot() { - // This was the original way to update snapshots - Optional legacyFlag = - Optional.ofNullable(System.getProperty(JVM_UPDATE_SNAPSHOTS_PARAMETER)); - if (legacyFlag.isPresent()) { - LoggingHelper.deprecatedV5( - log, - "Passing -PupdateSnapshot will be removed in a future release. Consider using snapshot.properties 'update-snapshot' toggle instead"); - if ("false".equals(legacyFlag.get())) { - return Optional.empty(); - } - return legacyFlag; - } - - try { - String updateSnapshot = SnapshotProperties.getOrThrow("update-snapshot"); - if ("all".equals(updateSnapshot)) { - return Optional.of(""); - } else if ("none".equals(updateSnapshot)) { - return Optional.empty(); - } - return Optional.of(updateSnapshot); - } catch (MissingSnapshotPropertiesKeyException ex) { - LoggingHelper.deprecatedV5( - log, - "You do not have 'update-snapshot=none' defined in your snapshot.properties - consider adding it now"); - return Optional.empty(); - } - } - - @Override - public SnapshotSerializer getSerializer() { - return SnapshotProperties.getInstance("serializer"); - } - - @Override - public SnapshotComparator getComparator() { - return SnapshotProperties.getInstance("comparator"); - } - - @Override - public List getReporters() { - return SnapshotProperties.getInstances("reporters"); - } - - @Override - public boolean isCI() { - String envVariable = SnapshotProperties.getOrThrow("ci-env-var"); - return System.getenv(envVariable) != null; - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/config/SnapshotConfig.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/config/SnapshotConfig.java deleted file mode 100644 index 45bdeae..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/config/SnapshotConfig.java +++ /dev/null @@ -1,86 +0,0 @@ -package au.com.origin.snapshots.config; - -import au.com.origin.snapshots.comparators.SnapshotComparator; -import au.com.origin.snapshots.reporters.SnapshotReporter; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import java.util.List; -import java.util.Optional; - -/** - * Snapshot Configuration ---------------------- - * - *

Implement this interface when integrating `java-snapshot-testing` with a custom testing - * library - */ -public interface SnapshotConfig { - @Deprecated String JVM_UPDATE_SNAPSHOTS_PARAMETER = "updateSnapshot"; - - /** - * The base directory where files get written (excluding package directories) default: - * "src/test/java" - * - *

You might want to override if you have tests under "src/test/integration" for example - * - * @return snapshot output folder - */ - String getOutputDir(); - - /** - * Subdirectory to store snapshots in - * - * @return name of subdirectory - */ - String getSnapshotDir(); - - /** - * Optional - * - * @return snapshots should be updated automatically without verification - */ - default Optional updateSnapshot() { - return Optional.ofNullable(System.getProperty(JVM_UPDATE_SNAPSHOTS_PARAMETER)); - } - - /** - * Optional Override to supply your own custom serialization function - * - * @return custom serialization function - */ - SnapshotSerializer getSerializer(); - - /** - * Optional Override to supply your own custom comparator function - * - * @return custom comparator function - */ - SnapshotComparator getComparator(); - - /** - * Optional Override to supply your own custom reporter functions Reporters will run in the same - * sequence as provided. Reporters should throw exceptions to indicate comparison failure. - * Exceptions thrown from reporters are aggregated and reported together. Reporters that wish to - * leverage IDE comparison tools can use standard assertion libraries like assertj, junit jupiter - * assertions (or) opentest4j. - * - * @return custom reporter functions - */ - List getReporters(); - - /** - * Optional This method is meant to detect if we're running on a CI environment. This is used to - * determine the action to be taken when a snapshot is not found. - * - *

If this method returns false, meaning we're NOT running on a CI environment (probably a dev - * machine), a new snapshot is created when not found. - * - *

If this method returns true, meaning we're running on a CI environment, no new snapshots are - * created and an error is thrown instead to prevent tests from silently passing when snapshots - * are not found. - * - *

Often to determine if running on a CI environment is to check for the presence of a 'CI' env - * variable - * - * @return boolean indicating if we're running on a CI environment or not - */ - boolean isCI(); -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/config/SnapshotConfigInjector.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/config/SnapshotConfigInjector.java deleted file mode 100644 index 46d4d01..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/config/SnapshotConfigInjector.java +++ /dev/null @@ -1,5 +0,0 @@ -package au.com.origin.snapshots.config; - -public interface SnapshotConfigInjector { - SnapshotConfig getSnapshotConfig(); -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/LogGithubIssueException.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/LogGithubIssueException.java deleted file mode 100644 index 9054935..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/LogGithubIssueException.java +++ /dev/null @@ -1,12 +0,0 @@ -package au.com.origin.snapshots.exceptions; - -public class LogGithubIssueException extends RuntimeException { - - private static final String LOG_SUPPORT_TICKET = - "\n\n*** This exception should never be thrown ***\n" - + "Log a support ticket at https://github.com/origin-energy/java-snapshot-testing/issues with details of the exception\n"; - - public LogGithubIssueException(String message) { - super(message + LOG_SUPPORT_TICKET); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/MissingSnapshotPropertiesKeyException.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/MissingSnapshotPropertiesKeyException.java deleted file mode 100644 index 6504d6c..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/MissingSnapshotPropertiesKeyException.java +++ /dev/null @@ -1,8 +0,0 @@ -package au.com.origin.snapshots.exceptions; - -public class MissingSnapshotPropertiesKeyException extends RuntimeException { - - public MissingSnapshotPropertiesKeyException(String key) { - super("\"snapshot.properties\" is missing required key=" + key); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/ReservedWordException.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/ReservedWordException.java deleted file mode 100644 index 61a20d2..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/ReservedWordException.java +++ /dev/null @@ -1,18 +0,0 @@ -package au.com.origin.snapshots.exceptions; - -import java.util.List; -import java.util.stream.Collectors; - -public class ReservedWordException extends RuntimeException { - - public ReservedWordException(String message) { - super(message); - } - - public ReservedWordException(String element, String reservedWord, List reservedWords) { - super( - String.format( - "You cannot use the '%s' character inside '%s'. Reserved characters are ", - reservedWord, element, reservedWords.stream().collect(Collectors.joining(",")))); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotExtensionException.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotExtensionException.java deleted file mode 100644 index d8050b2..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotExtensionException.java +++ /dev/null @@ -1,12 +0,0 @@ -package au.com.origin.snapshots.exceptions; - -public class SnapshotExtensionException extends RuntimeException { - - public SnapshotExtensionException(String message) { - super(message); - } - - public SnapshotExtensionException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java deleted file mode 100644 index 66bfd20..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java +++ /dev/null @@ -1,20 +0,0 @@ -package au.com.origin.snapshots.exceptions; - -import java.util.Collections; -import java.util.List; -import org.opentest4j.MultipleFailuresError; - -public class SnapshotMatchException extends MultipleFailuresError { - - public SnapshotMatchException(String message) { - super(message, Collections.emptyList()); - } - - public SnapshotMatchException(String message, Throwable cause) { - super(message, Collections.singletonList(cause)); - } - - public SnapshotMatchException(String message, List causes) { - super(message, causes); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/logging/LoggingHelper.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/logging/LoggingHelper.java deleted file mode 100644 index b3978ae..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/logging/LoggingHelper.java +++ /dev/null @@ -1,13 +0,0 @@ -package au.com.origin.snapshots.logging; - -import org.slf4j.Logger; - -public class LoggingHelper { - - public static void deprecatedV5(Logger log, String message) { - log.warn( - "\n\n** Deprecation Warning **\nThis feature will be removed in version 5.X\n" - + message - + "\n\n"); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java deleted file mode 100644 index 9daf4f5..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java +++ /dev/null @@ -1,17 +0,0 @@ -package au.com.origin.snapshots.reporters; - -import au.com.origin.snapshots.logging.LoggingHelper; -import lombok.extern.slf4j.Slf4j; - -@Deprecated -@Slf4j -public class PlainTextSnapshotReporter - extends au.com.origin.snapshots.reporters.v1.PlainTextSnapshotReporter { - - public PlainTextSnapshotReporter() { - super(); - LoggingHelper.deprecatedV5( - log, - "Update to `au.com.origin.snapshots.reporters.v1.PlainTextSnapshotReporter` in `snapshot.properties`"); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotReporter.java deleted file mode 100644 index 39fd9e9..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotReporter.java +++ /dev/null @@ -1,10 +0,0 @@ -package au.com.origin.snapshots.reporters; - -import au.com.origin.snapshots.Snapshot; - -public interface SnapshotReporter { - - boolean supportsFormat(String outputFormat); - - void report(Snapshot previous, Snapshot current); -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/v1/PlainTextSnapshotReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/v1/PlainTextSnapshotReporter.java deleted file mode 100644 index dbe718b..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/v1/PlainTextSnapshotReporter.java +++ /dev/null @@ -1,40 +0,0 @@ -package au.com.origin.snapshots.reporters.v1; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.reporters.SnapshotReporter; -import java.util.Arrays; -import java.util.function.Supplier; -import org.assertj.core.util.diff.DiffUtils; -import org.assertj.core.util.diff.Patch; -import org.opentest4j.AssertionFailedError; - -public class PlainTextSnapshotReporter implements SnapshotReporter { - - private static final Supplier NO_DIFF_EXCEPTION_SUPPLIER = - () -> - new IllegalStateException( - "No differences found. Potential mismatch between comparator and reporter"); - - public static String getDiffString(Patch patch) { - return patch.getDeltas().stream() - .map(delta -> delta.toString() + "\n") - .reduce(String::concat) - .orElseThrow(NO_DIFF_EXCEPTION_SUPPLIER); - } - - @Override - public boolean supportsFormat(String outputFormat) { - return true; // always true - } - - @Override - public void report(Snapshot previous, Snapshot current) { - Patch patch = - DiffUtils.diff( - Arrays.asList(previous.raw().split("\n")), Arrays.asList(current.raw().split("\n"))); - - String message = "Error on: \n" + current.raw() + "\n\n" + getDiffString(patch); - - throw new AssertionFailedError(message, previous.raw(), current.raw()); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/Base64SnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/Base64SnapshotSerializer.java deleted file mode 100644 index 4a08d98..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/Base64SnapshotSerializer.java +++ /dev/null @@ -1,20 +0,0 @@ -package au.com.origin.snapshots.serializers; - -import au.com.origin.snapshots.logging.LoggingHelper; -import lombok.extern.slf4j.Slf4j; - -/** - * This Serializer converts a byte[] into a base64 encoded string. If the input is not a byte[] it - * will be converted using `.getBytes(StandardCharsets.UTF_8)` method - */ -@Slf4j -@Deprecated -public class Base64SnapshotSerializer - extends au.com.origin.snapshots.serializers.v1.Base64SnapshotSerializer { - public Base64SnapshotSerializer() { - super(); - LoggingHelper.deprecatedV5( - log, - "Update to `au.com.origin.snapshots.serializers.v1.Base64SnapshotSerializer` in `snapshot.properties`"); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SerializerType.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SerializerType.java deleted file mode 100644 index 62915f3..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SerializerType.java +++ /dev/null @@ -1,7 +0,0 @@ -package au.com.origin.snapshots.serializers; - -public enum SerializerType { - TEXT, - JSON, - BASE64; -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SnapshotSerializer.java deleted file mode 100644 index d774561..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SnapshotSerializer.java +++ /dev/null @@ -1,10 +0,0 @@ -package au.com.origin.snapshots.serializers; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotSerializerContext; -import java.util.function.BiFunction; - -public interface SnapshotSerializer - extends BiFunction { - String getOutputFormat(); -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java deleted file mode 100644 index c4c1325..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java +++ /dev/null @@ -1,21 +0,0 @@ -package au.com.origin.snapshots.serializers; - -import au.com.origin.snapshots.logging.LoggingHelper; -import lombok.extern.slf4j.Slf4j; - -/** - * This Serializer does a snapshot of the {@link Object#toString()} method - * - *

Will render each toString() on a separate line - */ -@Slf4j -@Deprecated -public class ToStringSnapshotSerializer - extends au.com.origin.snapshots.serializers.v1.ToStringSnapshotSerializer { - public ToStringSnapshotSerializer() { - super(); - LoggingHelper.deprecatedV5( - log, - "Update to `au.com.origin.snapshots.serializers.v1.ToStringSnapshotSerializer` in `snapshot.properties`"); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/v1/Base64SnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/v1/Base64SnapshotSerializer.java deleted file mode 100644 index 41ed117..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/v1/Base64SnapshotSerializer.java +++ /dev/null @@ -1,35 +0,0 @@ -package au.com.origin.snapshots.serializers.v1; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotSerializerContext; -import au.com.origin.snapshots.serializers.SerializerType; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -/** - * This Serializer converts a byte[] into a base64 encoded string. If the input is not a byte[] it - * will be converted using `.getBytes(StandardCharsets.UTF_8)` method - */ -public class Base64SnapshotSerializer implements SnapshotSerializer { - private static final ToStringSnapshotSerializer toStringSnapshotSerializer = - new ToStringSnapshotSerializer(); - - @Override - public Snapshot apply(Object object, SnapshotSerializerContext gen) { - if (object == null) { - toStringSnapshotSerializer.apply("", gen); - } - byte[] bytes = - object instanceof byte[] - ? (byte[]) object - : object.toString().getBytes(StandardCharsets.UTF_8); - String encoded = Base64.getEncoder().encodeToString(bytes); - return toStringSnapshotSerializer.apply(encoded, gen); - } - - @Override - public String getOutputFormat() { - return SerializerType.BASE64.name(); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/v1/ToStringSnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/v1/ToStringSnapshotSerializer.java deleted file mode 100644 index 61e5075..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/v1/ToStringSnapshotSerializer.java +++ /dev/null @@ -1,33 +0,0 @@ -package au.com.origin.snapshots.serializers.v1; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotFile; -import au.com.origin.snapshots.SnapshotSerializerContext; -import au.com.origin.snapshots.serializers.SerializerType; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import lombok.extern.slf4j.Slf4j; - -/** - * This Serializer does a snapshot of the {@link Object#toString()} method - * - *

Will render each toString() on a separate line - */ -@Slf4j -public class ToStringSnapshotSerializer implements SnapshotSerializer { - - @Override - public Snapshot apply(Object object, SnapshotSerializerContext gen) { - String body = "[\n" + object.toString() + "\n]"; - if (body.contains(SnapshotFile.SPLIT_STRING)) { - log.warn( - "Found 3 consecutive lines in your snapshot \\n\\n\\n. This sequence is reserved as the snapshot separator - replacing with \\n.\\n.\\n"); - body = body.replaceAll(SnapshotFile.SPLIT_STRING, "\n.\n.\n"); - } - return gen.toSnapshot(body); - } - - @Override - public String getOutputFormat() { - return SerializerType.TEXT.name(); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/utils/ReflectionUtils.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/utils/ReflectionUtils.java deleted file mode 100644 index 2f5b78b..0000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/utils/ReflectionUtils.java +++ /dev/null @@ -1,48 +0,0 @@ -package au.com.origin.snapshots.utils; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Optional; -import java.util.function.Predicate; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class ReflectionUtils { - - /** - * Find {@link Field} by given predicate. - * - *

Invoke the given predicate on all fields in the target class, going up the class hierarchy - * to get all declared fields. - * - * @param clazz the target class to analyze - * @param predicate the predicate - * @return the field or empty optional - */ - public static Optional findFieldByPredicate( - final Class clazz, final Predicate predicate) { - Class targetClass = clazz; - - do { - final Field[] fields = targetClass.getDeclaredFields(); - for (final Field field : fields) { - if (!predicate.test(field)) { - continue; - } - return Optional.of(field); - } - targetClass = targetClass.getSuperclass(); - } while (targetClass != null && targetClass != Object.class); - - return Optional.empty(); - } - - public static void makeAccessible(final Field field) { - if ((!Modifier.isPublic(field.getModifiers()) - || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) - || Modifier.isFinal(field.getModifiers())) - && !field.isAccessible()) { - field.setAccessible(true); - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/CustomFolderSnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/CustomFolderSnapshotTest.java deleted file mode 100644 index 5e7b74a..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/CustomFolderSnapshotTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public class CustomFolderSnapshotTest { - - private static final String OUTPUT_FILE = - "src/test/some-folder/au/com/origin/snapshots/__snapshots__/CustomFolderSnapshotTest.snap"; - - @BeforeEach - public void beforeEach() throws IOException { - Path path = Paths.get(OUTPUT_FILE); - Files.deleteIfExists(path); - } - - @Test - void shouldBeAbleToChangeSnapshotFolder(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new CustomFolderSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(FakeObject.builder().id("shouldBeAbleToChangeSnapshotFolder").build()); - snapshotVerifier.validateSnapshots(); - - Path path = Paths.get(OUTPUT_FILE); - Assertions.assertThat(Files.exists(path)); - } - - @Test - void shouldBeAbleToChangeSnapshotFolderLegacyTrailginSlash(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new CustomFolderSnapshotConfigLegacy(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(FakeObject.builder().id("shouldBeAbleToChangeSnapshotFolder").build()); - snapshotVerifier.validateSnapshots(); - - Path path = Paths.get(OUTPUT_FILE); - Assertions.assertThat(Files.exists(path)); - } - - public static class CustomFolderSnapshotConfig extends BaseSnapshotConfig { - - @Override - public String getOutputDir() { - return "src/test/some-folder"; - } - } - - public static class CustomFolderSnapshotConfigLegacy extends BaseSnapshotConfig { - - @Override - public String getOutputDir() { - return "src/test/some-folder/"; - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/DebugSnapshotLineEndingsTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/DebugSnapshotLineEndingsTest.java deleted file mode 100644 index afdad05..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/DebugSnapshotLineEndingsTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.serializers.SerializerType; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Collection; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.SneakyThrows; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -class DebugSnapshotLineEndingsTest { - private static final SnapshotConfig CONFIG = - new BaseSnapshotConfig() { - @Override - public SnapshotSerializer getSerializer() { - return new MultiLineSnapshotSerializer(); - } - }; - private static final String DEBUG_FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap.debug"; - private static final String SNAPSHOT_FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap"; - - @BeforeAll - static void beforeAll() { - SnapshotUtils.copyTestSnapshots(); - } - - @SneakyThrows - @BeforeEach - void beforeEach() { - Files.deleteIfExists(Paths.get(DEBUG_FILE_PATH)); - } - - /** - * Scenario: - An existing snapshot file checked out from git will have CR LF line endings on - * Windows OS - A newly created snapshot file will have LF line endings on any OS (see - * MultiLineSnapshotSerializer) Expectation: - As snapshot file content is identical (except for - * line endings), the debug file should not be created - */ - @DisplayName( - "Debug file should not be created when snapshots match the existing snapshot regardless of line endings") - @Test - void existingSnapshotDifferentLineEndings(TestInfo testInfo) { - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); - assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); - - SnapshotVerifier snapshotVerifier = new SnapshotVerifier(CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(Arrays.asList("a", "b")); - snapshotVerifier.validateSnapshots(); - - assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH)), "Debug file should not be created"); - } - - private static class MultiLineSnapshotSerializer implements SnapshotSerializer { - @Override - public String getOutputFormat() { - return SerializerType.TEXT.name(); - } - - @Override - public Snapshot apply(Object object, SnapshotSerializerContext snapshotSerializerContext) { - Object body = - "[\n" - + Arrays.asList(object).stream() - .flatMap( - o -> { - if (o instanceof Collection) { - return ((Collection) o).stream(); - } - return Stream.of(o); - }) - .map(Object::toString) - .collect(Collectors.joining("\n")) - + "\n]"; - - return Snapshot.builder() - .name( - "au.com.origin.snapshots.DebugSnapshotLineEndingsTest.existingSnapshotDifferentLineEndings") - .body(body.toString()) - .build(); - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/DebugSnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/DebugSnapshotTest.java deleted file mode 100644 index 2934f7c..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/DebugSnapshotTest.java +++ /dev/null @@ -1,111 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; -import java.nio.file.Files; -import java.nio.file.Paths; -import lombok.SneakyThrows; -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -public class DebugSnapshotTest { - - private static final SnapshotConfig DEFAULT_CONFIG = - new BaseSnapshotConfig() { - @Override - public SnapshotSerializer getSerializer() { - return new ToStringSnapshotSerializer(); - } - }; - - private static final String DEBUG_FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/DebugSnapshotTest.snap.debug"; - private static final String SNAPSHOT_FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/DebugSnapshotTest.snap"; - - @BeforeAll - static void beforeAll() { - SnapshotUtils.copyTestSnapshots(); - } - - @SneakyThrows - @BeforeEach - public void beforeEach() { - Files.deleteIfExists(Paths.get(DEBUG_FILE_PATH)); - } - - @DisplayName("Debug file should be created when snapshots don't match") - @Test - void createDebugFile(TestInfo testInfo) { - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); - assertThrows(SnapshotMatchException.class, () -> expect.toMatchSnapshot(new TestObjectBad())); - assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); - } - - @DisplayName("Debug file should be created when snapshots match for a new snapshot") - @Test - void debugFileCreatedNewSnapshot(TestInfo testInfo) { - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); - expect.toMatchSnapshot(new TestObjectGood()); - assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); - } - - @DisplayName("Debug file should be created when snapshots match for an existing snapshot") - @Test - void debugFileCreatedExistingSnapshot(TestInfo testInfo) { - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); - expect.toMatchSnapshot(new TestObjectGood()); - assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); - } - - @SneakyThrows - @DisplayName("Existing debug file should not be deleted once snapshots match") - @Test - void debugFileCreatedSnapshotMatch(TestInfo testInfo) { - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); - Files.createFile(Paths.get(DEBUG_FILE_PATH)); - assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(new TestObjectGood()); - assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); - } - - private static class TestObjectBad { - @Override - public String toString() { - return "Bad Snapshot"; - } - } - - private static class TestObjectGood { - @Override - public String toString() { - return "Good Snapshot"; - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/EmptySnapshotFileTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/EmptySnapshotFileTest.java deleted file mode 100644 index db94093..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/EmptySnapshotFileTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import au.com.origin.snapshots.config.ToStringSnapshotConfig; -import java.nio.file.Files; -import java.nio.file.Paths; -import org.junit.jupiter.api.*; - -public class EmptySnapshotFileTest { - - private static final String SNAPSHOT_FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/EmptySnapshotFileTest.snap"; - private static final String DEBUG_FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/EmptySnapshotFileTest.snap.debug"; - - @BeforeAll - static void beforeAll() { - SnapshotUtils.copyTestSnapshots(); - } - - @DisplayName("Should remove empty snapshots") - @Test - public void shouldRemoveEmptySnapshots(TestInfo testInfo) { - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); - assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new ToStringSnapshotConfig(), testInfo.getTestClass().get()); - snapshotVerifier.validateSnapshots(); - - assertTrue(Files.notExists(Paths.get(SNAPSHOT_FILE_PATH))); - assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); - } - - @Nested - public class NestedClass { - - private static final String SNAPSHOT_FILE_PATH_NESTED = - "src/test/java/au/com/origin/snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap"; - private static final String DEBUG_FILE_PATH_NESTED = - "src/test/java/au/com/origin/snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap.debug"; - - @DisplayName("Should remove empty nested snapshots") - @Test - public void shouldRemoveEmptyNestedSnapshots(TestInfo testInfo) { - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH_NESTED))); - assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH_NESTED))); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new ToStringSnapshotConfig(), testInfo.getTestClass().get()); - snapshotVerifier.validateSnapshots(); - - assertTrue(Files.notExists(Paths.get(SNAPSHOT_FILE_PATH_NESTED))); - assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH_NESTED))); - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/EqualDebugSnapshotFileTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/EqualDebugSnapshotFileTest.java deleted file mode 100644 index 1d8eea5..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/EqualDebugSnapshotFileTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import au.com.origin.snapshots.config.ToStringSnapshotConfig; -import java.nio.file.Files; -import java.nio.file.Paths; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public class EqualDebugSnapshotFileTest { - - private static final String SNAPSHOT_FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap"; - private static final String DEBUG_FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap.debug"; - - @BeforeAll - static void beforeAll() { - SnapshotUtils.copyTestSnapshots(); - } - - @DisplayName("Should remove equal debug snapshots") - @Test - public void shouldRemoveEmptySnapshots(TestInfo testInfo) { - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); - assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new ToStringSnapshotConfig(), testInfo.getTestClass().get()); - snapshotVerifier.validateSnapshots(); - - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); - assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/FakeObject.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/FakeObject.java deleted file mode 100644 index a684d55..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/FakeObject.java +++ /dev/null @@ -1,25 +0,0 @@ -package au.com.origin.snapshots; - -import java.util.List; -import lombok.*; - -@ToString -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class FakeObject { - - private String id; - - private Integer value; - - private String name; - - @Setter private FakeObject fakeObject; - - public void fakeMethod(String fakeName, Long fakeNumber, List fakeList) {} - - public void fakeMethodWithComplexObject(Object fakeObj) {} - - public void fakeMethodWithComplexFakeObject(FakeObject fakeObj) {} -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/NoNameChangeTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/NoNameChangeTest.java deleted file mode 100644 index 3fade4f..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/NoNameChangeTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package au.com.origin.snapshots; - -import static org.assertj.core.api.Assertions.assertThat; - -import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; -import au.com.origin.snapshots.reporters.PlainTextSnapshotReporter; -import au.com.origin.snapshots.serializers.Base64SnapshotSerializer; -import au.com.origin.snapshots.serializers.SerializerType; -import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; -import org.junit.jupiter.api.Test; - -/** - * These classes are likely defined in snapshot.properties as a string. - * - *

The clients IDE will not complain if they change so ensure they don't - */ -public class NoNameChangeTest { - - @Test - public void serializersApiShouldNotChange() { - assertThat(Base64SnapshotSerializer.class.getName()) - .isEqualTo("au.com.origin.snapshots.serializers.Base64SnapshotSerializer"); - assertThat(ToStringSnapshotSerializer.class.getName()) - .isEqualTo("au.com.origin.snapshots.serializers.ToStringSnapshotSerializer"); - assertThat(SerializerType.class.getName()) - .isEqualTo("au.com.origin.snapshots.serializers.SerializerType"); - } - - @Test - public void reportersApiShouldNotChange() { - assertThat(PlainTextSnapshotReporter.class.getName()) - .isEqualTo("au.com.origin.snapshots.reporters.PlainTextSnapshotReporter"); - } - - @Test - public void comparatorsApiShouldNotChange() { - assertThat(PlainTextEqualsComparator.class.getName()) - .isEqualTo("au.com.origin.snapshots.comparators.PlainTextEqualsComparator"); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/OnLoadSnapshotFileTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/OnLoadSnapshotFileTest.java deleted file mode 100644 index a1c1a44..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/OnLoadSnapshotFileTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package au.com.origin.snapshots; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public class OnLoadSnapshotFileTest { - - private static final String SNAPSHOT_FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/OnLoadSnapshotFileTest.snap"; - private final SnapshotConfig CUSTOM_SNAPSHOT_CONFIG = new BaseSnapshotConfig(); - - @BeforeAll - static void beforeAll() throws IOException { - Files.deleteIfExists(Paths.get(SNAPSHOT_FILE_PATH)); - String snapshotFileContent = - "au.com.origin.snapshots.OnLoadSnapshotFileTest.shouldLoadFileWithCorrectEncodingForCompare=[\n" - + "any special characters that need correct encoding äöüèéàè\n" - + "]"; - createSnapshotFile(snapshotFileContent); - } - - @DisplayName("Should load snapshots with correct encoding") - @Test - public void shouldLoadFileWithCorrectEncodingForCompare(TestInfo testInfo) throws IOException { - assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(CUSTOM_SNAPSHOT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect - .serializer(ToStringSnapshotSerializer.class) - .toMatchSnapshot("any special characters that need correct encoding äöüèéàè"); - snapshotVerifier.validateSnapshots(); - - File f = new File(SNAPSHOT_FILE_PATH); - assertThat(String.join("\n", Files.readAllLines(f.toPath()))) - .isEqualTo( - "au.com.origin.snapshots.OnLoadSnapshotFileTest.shouldLoadFileWithCorrectEncodingForCompare=[\n" - + "any special characters that need correct encoding äöüèéàè\n" - + "]"); - } - - private static void createSnapshotFile(String snapshot) { - try { - File file = new File(SNAPSHOT_FILE_PATH); - file.getParentFile().mkdirs(); - file.createNewFile(); - try (FileOutputStream fileStream = new FileOutputStream(file, false)) { - fileStream.write(snapshot.getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - e.printStackTrace(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/OrphanSnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/OrphanSnapshotTest.java deleted file mode 100644 index 94533b8..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/OrphanSnapshotTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package au.com.origin.snapshots; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public class OrphanSnapshotTest { - - private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); - - @BeforeAll - static void beforeAll() { - SnapshotUtils.copyTestSnapshots(); - } - - @DisplayName("should fail the build when failOnOrphans=true") - @Test - void orphanSnapshotsShouldFailTheBuild(TestInfo testInfo) throws IOException { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get(), true); - FakeObject fakeObject1 = FakeObject.builder().id("anyId1").value(1).name("anyName1").build(); - final Path snapshotFile = - Paths.get("src/test/java/au/com/origin/snapshots/__snapshots__/OrphanSnapshotTest.snap"); - - long bytesBefore = Files.size(snapshotFile); - - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(fakeObject1); - - Throwable exceptionThatWasThrown = - assertThrows( - SnapshotMatchException.class, - () -> { - snapshotVerifier.validateSnapshots(); - }); - - assertThat(exceptionThatWasThrown.getMessage()).isEqualTo("ERROR: Found orphan snapshots"); - - // Ensure file has not changed - long bytesAfter = Files.size(snapshotFile); - assertThat(bytesAfter).isGreaterThan(bytesBefore); - } - - @DisplayName("should not fail the build when failOnOrphans=false") - @Test - void orphanSnapshotsShouldNotFailTheBuild(TestInfo testInfo) throws IOException { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get(), false); - FakeObject fakeObject1 = FakeObject.builder().id("anyId1").value(1).name("anyName1").build(); - final Path snapshotFile = - Paths.get("src/test/java/au/com/origin/snapshots/__snapshots__/OrphanSnapshotTest.snap"); - - long bytesBefore = Files.size(snapshotFile); - - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(fakeObject1); - - snapshotVerifier.validateSnapshots(); - - // Ensure file has not changed - long bytesAfter = Files.size(snapshotFile); - assertThat(bytesAfter).isGreaterThan(bytesBefore); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/PrivateCalledMethodTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/PrivateCalledMethodTest.java deleted file mode 100644 index 99ee602..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/PrivateCalledMethodTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -class PrivateCalledMethodTest { - - @Test - void testName(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - testBasedOnArgs("testContent", testInfo); - snapshotVerifier.validateSnapshots(); - } - - private void testBasedOnArgs(String arg, TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(arg); - snapshotVerifier.validateSnapshots(); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/ReflectionUtilities.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/ReflectionUtilities.java deleted file mode 100644 index 0fe0100..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/ReflectionUtilities.java +++ /dev/null @@ -1,30 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import java.lang.reflect.Method; -import java.util.Optional; -import java.util.stream.Stream; - -public class ReflectionUtilities { - - // FIXME consider guava reflection instead - public static Method getMethod(Class clazz, String methodName) { - try { - return Stream.of(clazz.getDeclaredMethods()) - .filter(method -> method.getName().equals(methodName)) - .findFirst() - .orElseThrow(() -> new NoSuchMethodException("Not Found")); - } catch (NoSuchMethodException e) { - return Optional.ofNullable(clazz.getSuperclass()) - .map(superclass -> getMethod(superclass, methodName)) - .orElseThrow( - () -> - new SnapshotMatchException( - "Could not find method " - + methodName - + " on class " - + clazz - + "\nPlease annotate your test method with @Test and make it without any parameters!")); - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/ScenarioTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/ScenarioTest.java deleted file mode 100644 index 3bff448..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/ScenarioTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -class ScenarioTest { - - private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); - - @Test - void canTakeMultipleSnapshotsUsingScenario(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("Default Snapshot"); - expect.scenario("additional").toMatchSnapshot("Additional Snapshot"); - snapshotVerifier.validateSnapshots(); - } - - @Test - void canTakeTheSameSnapshotTwice(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("Default Snapshot"); - expect.toMatchSnapshot("Default Snapshot"); - expect.scenario("scenario").toMatchSnapshot("Scenario Snapshot"); - expect.scenario("scenario").toMatchSnapshot("Scenario Snapshot"); - snapshotVerifier.validateSnapshots(); - } - - @Test - void cannotTakeDifferentSnapshotsAtDefaultLevel(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("Default Snapshot"); - assertThrows(SnapshotMatchException.class, () -> expect.toMatchSnapshot("Default Snapshot 2")); - } - - @Test - void cannotTakeDifferentSnapshotsAtScenarioLevel(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.scenario("scenario").toMatchSnapshot("Default Snapshot"); - assertThrows( - SnapshotMatchException.class, - () -> expect.scenario("scenario").toMatchSnapshot("Default Snapshot 2")); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotCaptor.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotCaptor.java deleted file mode 100644 index 25872f0..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotCaptor.java +++ /dev/null @@ -1,104 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.exceptions.SnapshotExtensionException; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; - -class SnapshotCaptor { - - private Class parameterClass; - - private Class argumentClass; - - private String[] ignore; - - public SnapshotCaptor(Class parameterClass, String... ignore) { - this.parameterClass = parameterClass; - this.argumentClass = parameterClass; - this.ignore = ignore; - } - - public SnapshotCaptor(Class parameterClass, Class argumentClass, String... ignore) { - this.parameterClass = parameterClass; - this.argumentClass = argumentClass; - this.ignore = ignore; - } - - public Class getParameterClass() { - return parameterClass; - } - - public Object removeIgnored(Object value) { - Object newValue = value; - if (ignore != null && ignore.length > 0) { - newValue = shallowCopy(value); - for (String each : ignore) { - try { - Field field = this.argumentClass.getDeclaredField(each); - field.setAccessible(true); - if (field.getType().isPrimitive()) { - field.setByte(newValue, Integer.valueOf(0).byteValue()); - } else { - field.set(newValue, null); - } - } catch (IllegalAccessException | NoSuchFieldException e) { - throw new SnapshotExtensionException("Invalid Ignore value " + each, e.getCause()); - } - } - } - return newValue; - } - - private Object shallowCopy(Object value) { - try { - Object newValue = constructCopy(this.argumentClass); - - Field[] fields = this.argumentClass.getDeclaredFields(); - - for (Field field : fields) { - field.setAccessible(true); - try { - field.set(newValue, field.get(value)); - } catch (Exception e) { - // ignore - } - } - return newValue; - } catch (Exception e) { - throw new SnapshotExtensionException( - "Class " - + this.argumentClass.getSimpleName() - + " must have a default empty constructor!"); - } - } - - private Object constructCopy(Class argumentClass) - throws InstantiationException, IllegalAccessException, - java.lang.reflect.InvocationTargetException, NoSuchMethodException { - - try { - return argumentClass.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - // Ignore - should log - } - - Constructor[] constructors = argumentClass.getDeclaredConstructors(); - - if (constructors.length == 0) { - return argumentClass.getDeclaredConstructor().newInstance(); - } - - int i = 0; - Class[] types = constructors[i].getParameterTypes(); - Object[] paramValues = new Object[types.length]; - - for (int j = 0; j < types.length; j++) { - if (types[j].isPrimitive()) { - paramValues[j] = Integer.valueOf(0).byteValue(); - } else { - paramValues[j] = constructCopy(types[j]); - } - } - return constructors[i].newInstance(paramValues); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotContextTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotContextTest.java deleted file mode 100644 index 550514a..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotContextTest.java +++ /dev/null @@ -1,230 +0,0 @@ -package au.com.origin.snapshots; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import au.com.origin.snapshots.reporters.SnapshotReporter; -import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.SneakyThrows; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.opentest4j.AssertionFailedError; - -@ExtendWith(MockitoExtension.class) -class SnapshotContextTest { - - private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); - private static final String FILE_PATH = "src/test/java/anyFilePath"; - private static final String SNAPSHOT_NAME = "java.lang.String.toString"; - private static final String SNAPSHOT = "java.lang.String.toString=[\nanyObject\n]"; - - private SnapshotFile snapshotFile; - - private SnapshotContext snapshotContext; - - @BeforeEach - void setUp() throws NoSuchMethodException, IOException { - snapshotFile = - new SnapshotFile(DEFAULT_CONFIG.getOutputDir(), "anyFilePath", SnapshotContextTest.class); - snapshotContext = - new SnapshotContext( - DEFAULT_CONFIG, - snapshotFile, - String.class, - String.class.getDeclaredMethod("toString"), - "anyObject"); - } - - @AfterEach - void tearDown() throws IOException { - Files.deleteIfExists(Paths.get(FILE_PATH)); - } - - @Test - void shouldGetSnapshotNameSuccessfully() { - String snapshotName = snapshotContext.resolveSnapshotIdentifier(); - assertThat(snapshotName).isEqualTo(SNAPSHOT_NAME); - } - - @Test - void shouldMatchSnapshotSuccessfully() { - snapshotContext.toMatchSnapshot(); - assertThat(snapshotFile.getSnapshots().stream().findFirst().get().raw()).isEqualTo(SNAPSHOT); - } - - @Test - void shouldMatchSnapshotWithException() { - snapshotFile.pushSnapshot(Snapshot.parse(SNAPSHOT_NAME + "=anyWrongSnapshot")); - assertThrows(SnapshotMatchException.class, snapshotContext::toMatchSnapshot); - } - - @SneakyThrows - @Test - void shouldRenderScenarioNameWhenSupplied() { - SnapshotContext snapshotContextWithScenario = - new SnapshotContext( - DEFAULT_CONFIG, - snapshotFile, - String.class, - String.class.getDeclaredMethod("toString"), - "anyObject"); - snapshotContextWithScenario.setScenario("hello world"); - assertThat(snapshotContextWithScenario.resolveSnapshotIdentifier()) - .isEqualTo("java.lang.String.toString[hello world]"); - } - - @SneakyThrows - @Test - void shouldNotRenderScenarioNameWhenNull() { - SnapshotContext snapshotContextWithoutScenario = - new SnapshotContext( - DEFAULT_CONFIG, - snapshotFile, - String.class, - String.class.getDeclaredMethod("toString"), - "anyObject"); - assertThat(snapshotContextWithoutScenario.resolveSnapshotIdentifier()) - .isEqualTo("java.lang.String.toString"); - } - - @SneakyThrows - @Test - void shouldOverwriteSnapshotsWhenParamIsPassed() { - SnapshotConfig mockConfig = Mockito.mock(SnapshotConfig.class); - Mockito.when(mockConfig.updateSnapshot()).thenReturn(Optional.of("")); - Mockito.when(mockConfig.getSerializer()).thenReturn(new ToStringSnapshotSerializer()); - SnapshotFile snapshotFile = Mockito.mock(SnapshotFile.class); - Set set = new HashSet<>(); - set.add(Snapshot.parse("java.lang.String.toString[hello world]=[{" + "\"a\": \"b\"" + "}]")); - Mockito.when(snapshotFile.getSnapshots()).thenReturn(set); - - SnapshotContext snapshotContext = - new SnapshotContext( - mockConfig, - snapshotFile, - String.class, - String.class.getDeclaredMethod("toString"), - "anyObject"); - snapshotContext.setScenario("hello world"); - snapshotContext.toMatchSnapshot(); - Mockito.verify(snapshotFile) - .pushSnapshot(Snapshot.parse("java.lang.String.toString[hello world]=[\nanyObject\n]")); - } - - @SneakyThrows - @Test - void shouldFailWhenRunningOnCiWithoutExistingSnapshot() { - BaseSnapshotConfig ciSnapshotConfig = - new BaseSnapshotConfig() { - @Override - public boolean isCI() { - return true; - } - }; - - SnapshotContext ciSnapshotContext = - new SnapshotContext( - ciSnapshotConfig, - new SnapshotFile(ciSnapshotConfig.getOutputDir(), "blah", SnapshotContextTest.class), - String.class, - String.class.getDeclaredMethod("toString"), - "anyObject"); - - Assertions.assertThatThrownBy(ciSnapshotContext::toMatchSnapshot) - .hasMessage( - "Snapshot [java.lang.String.toString] not found. Has this snapshot been committed ?"); - } - - @SneakyThrows - @Test - void shouldAggregateMultipleFailures() { - SnapshotFile snapshotFile = Mockito.mock(SnapshotFile.class); - Set set = new HashSet<>(); - set.add(Snapshot.parse("java.lang.String.toString=[\n \"hello\"\n]")); - Mockito.when(snapshotFile.getSnapshots()).thenReturn(set); - - Stream> reportingFunctions = - Stream.of( - (rawSnapshot, currentObject) -> - assertThat(currentObject).isEqualTo(rawSnapshot), // assertj - org.junit.jupiter.api.Assertions::assertEquals, // junit jupiter - (previous, current) -> { - String message = - String.join( - System.lineSeparator(), - "Expected : ", - previous.raw(), - "Actual : ", - current.raw()); - throw new AssertionFailedError(message, previous, current); // opentest4j - }); - - Stream reporters = - reportingFunctions.map( - consumer -> - new SnapshotReporter() { - @Override - public boolean supportsFormat(String outputFormat) { - return true; - } - - @Override - public void report(Snapshot previous, Snapshot current) { - consumer.accept(previous, current); - } - }); - - SnapshotContext failingSnapshotContext = - new SnapshotContext( - DEFAULT_CONFIG, - snapshotFile, - String.class, - String.class.getDeclaredMethod("toString"), - "hola"); - failingSnapshotContext.setSnapshotSerializer(new ToStringSnapshotSerializer()); - failingSnapshotContext.setSnapshotReporters(reporters.collect(Collectors.toList())); - - try { - failingSnapshotContext.toMatchSnapshot(); - } catch (Throwable m) { - String cleanMessage = - m.getMessage() - .replace("<\"", "") - .replace("<", "") - .replaceAll("\n", "") - .replaceAll("\r", "") - .replaceAll("\t", "") - .replace("\">", " ") - .replace(">", " ") - .replace("]", "") - .replace("java.lang.String.toString=[", "") - .replaceAll(" +", " "); - - assertThat(cleanMessage) - .containsPattern("Expecting.*hola.*to be equal to.*hello.*but was not"); // assertj - assertThat(cleanMessage).containsPattern("expected.*hello.*but was.*hola"); // junit jupiter - assertThat(cleanMessage).containsPattern("Expected.*hello.*Actual.*hola"); // opentest4j - - return; - } - - Assertions.fail("Expected an error to be thrown"); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotHeaders.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotHeaders.java deleted file mode 100644 index 1f0b13b..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotHeaders.java +++ /dev/null @@ -1,59 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.serializers.LowercaseToStringSerializer; -import au.com.origin.snapshots.serializers.SerializerType; -import lombok.NoArgsConstructor; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -public class SnapshotHeaders { - - private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); - - static SnapshotVerifier snapshotVerifier; - - @BeforeAll - static void beforeAll() { - SnapshotUtils.copyTestSnapshots(); - snapshotVerifier = new SnapshotVerifier(DEFAULT_CONFIG, SnapshotHeaders.class); - } - - @AfterAll - static void afterAll() { - snapshotVerifier.validateSnapshots(); - } - - @Test - void shouldBeAbleToSnapshotASingleCustomHeader(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.serializer(CustomHeadersSerializer.class).toMatchSnapshot("Hello World"); - } - - @Test - void shouldBeAbleToSnapshotMultipleCustomHeader(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.serializer(CustomHeadersSerializer.class).toMatchSnapshot("Hello World"); - } - - @NoArgsConstructor - private static class CustomHeadersSerializer extends LowercaseToStringSerializer { - @Override - public String getOutputFormat() { - return SerializerType.JSON.name(); - } - - @Override - public Snapshot apply(Object object, SnapshotSerializerContext snapshotSerializerContext) { - snapshotSerializerContext.getHeader().put("custom", "anything"); - snapshotSerializerContext.getHeader().put("custom2", "anything2"); - return super.apply(object, snapshotSerializerContext); - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotIntegrationTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotIntegrationTest.java deleted file mode 100644 index 22d255a..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotIntegrationTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import au.com.origin.snapshots.serializers.UppercaseToStringSerializer; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -public class SnapshotIntegrationTest { - - private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); - - static SnapshotVerifier snapshotVerifier; - - @BeforeAll - static void beforeAll() { - SnapshotUtils.copyTestSnapshots(); - snapshotVerifier = new SnapshotVerifier(DEFAULT_CONFIG, SnapshotIntegrationTest.class); - } - - @AfterAll - static void afterAll() { - snapshotVerifier.validateSnapshots(); - } - - @Test - void shouldMatchSnapshotOne(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(FakeObject.builder().id("anyId1").value(1).name("anyName1").build()); - } - - @Test - void shouldMatchSnapshotTwo(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(FakeObject.builder().id("anyId2").value(2).name("anyName2").build()); - } - - @Test - void shouldMatchSnapshotThree(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(FakeObject.builder().id("anyId3").value(3).name("anyName3").build()); - } - - @Test - void shouldMatchSnapshotFour(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot( - FakeObject.builder().id("anyId4").value(4).name("any\n\n\nName4").build()); - } - - @Test - void shouldMatchSnapshotInsidePrivateMethod(TestInfo testInfo) { - matchInsidePrivate(testInfo); - } - - @Test - void shouldThrowSnapshotMatchException(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertThrows( - SnapshotMatchException.class, - () -> - expect.toMatchSnapshot( - FakeObject.builder().id("anyId5").value(6).name("anyName5").build()), - "Error on: \n" - + "au.com.origin.snapshots.SnapshotIntegrationTest.shouldThrowSnapshotMatchException=["); - } - - @Test - void shouldSnapshotUsingSerializerClass(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.serializer(UppercaseToStringSerializer.class).toMatchSnapshot("Hello World"); - } - - @Test - void shouldSnapshotUsingSerializerPropertyName(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.serializer("lowercase").toMatchSnapshot("Hello World"); - } - - private void matchInsidePrivate(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot( - FakeObject.builder().id("anyPrivate").value(5).name("anyPrivate").build()); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotMatcherScenarioTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotMatcherScenarioTest.java deleted file mode 100644 index ab45e39..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotMatcherScenarioTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package au.com.origin.snapshots; - -import static org.assertj.core.api.Assertions.assertThat; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -class SnapshotMatcherScenarioTest { - - private static final String FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotMatcherScenarioTest.snap"; - - static SnapshotVerifier snapshotVerifier; - - @BeforeAll - static void beforeAll() { - snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), SnapshotMatcherScenarioTest.class); - } - - @AfterAll - static void afterAll() throws IOException { - snapshotVerifier.validateSnapshots(); - File f = new File(FILE_PATH); - assertThat(String.join("\n", Files.readAllLines(f.toPath()))) - .isEqualTo( - "au.com.origin.snapshots.SnapshotMatcherScenarioTest.should1ShowSnapshotSuccessfully[Scenario A]=[\n" - + "any type of object\n" - + "]\n\n\n" - + "au.com.origin.snapshots.SnapshotMatcherScenarioTest.should2SecondSnapshotExecutionSuccessfully[Scenario B]=[\n" - + "any second type of object\n" - + "]"); - Files.delete(Paths.get(FILE_PATH)); - } - - @Test - void should1ShowSnapshotSuccessfully(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.scenario("Scenario A").toMatchSnapshot("any type of object"); - File f = new File(FILE_PATH); - if (!f.exists() || f.isDirectory()) { - throw new RuntimeException("File should exist here"); - } - } - - @Test - void should2SecondSnapshotExecutionSuccessfully(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.scenario("Scenario B").toMatchSnapshot("any second type of object"); - File f = new File(FILE_PATH); - if (!f.exists() || f.isDirectory()) { - throw new RuntimeException("File should exist here"); - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotMatcherTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotMatcherTest.java deleted file mode 100644 index 3cfcfbe..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotMatcherTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package au.com.origin.snapshots; - -import static org.assertj.core.api.Assertions.assertThat; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -class SnapshotMatcherTest { - - private static final String FILE_PATH = - "src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotMatcherTest.snap"; - - static SnapshotVerifier snapshotVerifier; - - @BeforeAll - static void beforeAll() { - snapshotVerifier = new SnapshotVerifier(new BaseSnapshotConfig(), SnapshotMatcherTest.class); - } - - @AfterAll - static void afterAll() throws IOException { - snapshotVerifier.validateSnapshots(); - File f = new File(FILE_PATH); - assertThat(String.join("\n", Files.readAllLines(f.toPath()))) - .isEqualTo( - "au.com.origin.snapshots.SnapshotMatcherTest.should1ShowSnapshotSuccessfully=[\n" - + "any type of object\n" - + "]\n\n\n" - + "au.com.origin.snapshots.SnapshotMatcherTest.should2SecondSnapshotExecutionSuccessfully=[\n" - + "any second type of object\n" - + "]"); - Files.delete(Paths.get(FILE_PATH)); - } - - @Test - void should1ShowSnapshotSuccessfully(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("any type of object"); - File f = new File(FILE_PATH); - if (!f.exists() || f.isDirectory()) { - throw new RuntimeException("File should exist here"); - } - } - - @Test - void should2SecondSnapshotExecutionSuccessfully(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("any second type of object"); - File f = new File(FILE_PATH); - if (!f.exists() || f.isDirectory()) { - throw new RuntimeException("File should exist here"); - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotNameAnnotationTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotNameAnnotationTest.java deleted file mode 100644 index e8740ab..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotNameAnnotationTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.exceptions.ReservedWordException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public class SnapshotNameAnnotationTest { - - @BeforeEach - void beforeEach() { - SnapshotUtils.copyTestSnapshots(); - } - - @SnapshotName("can_use_snapshot_name") - @Test - void canUseSnapshotNameAnnotation(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("Hello World"); - snapshotVerifier.validateSnapshots(); - } - - @SnapshotName("can use snapshot name with spaces") - @Test - void canUseSnapshotNameAnnotationWithSpaces(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("Hello World"); - snapshotVerifier.validateSnapshots(); - } - - @SnapshotName("can't use '=' character in snapshot name") - @Test - void cannotUseEqualsInsideSnapshotName(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertThrows(ReservedWordException.class, () -> expect.toMatchSnapshot("FooBar")); - } - - @SnapshotName("can't use '[' character in snapshot name") - @Test - void cannotUseOpeningSquareBracketInsideSnapshotName(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertThrows(ReservedWordException.class, () -> expect.toMatchSnapshot("FooBar")); - } - - @SnapshotName("can't use ']' character in snapshot name") - @Test - void cannotUseClosingSquareBracketInsideSnapshotName(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertThrows(ReservedWordException.class, () -> expect.toMatchSnapshot("FooBar")); - } - - @Test - void cannotUseEqualsInsideScenarioName(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertThrows( - ReservedWordException.class, - () -> expect.scenario("can't use = symbol in scenario").toMatchSnapshot("FooBar")); - } - - @Test - void cannotUseOpeningSquareBracketInsideScenarioName(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertThrows( - ReservedWordException.class, - () -> expect.scenario("can't use [ symbol in scenario").toMatchSnapshot("FooBar")); - } - - @Test - void cannotUseClosingSquareBracketInsideScenarioName(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertThrows( - ReservedWordException.class, - () -> expect.scenario("can't use ] symbol in scenario").toMatchSnapshot("FooBar")); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotNameAnnotationWithDuplicatesTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotNameAnnotationWithDuplicatesTest.java deleted file mode 100644 index 570f327..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotNameAnnotationWithDuplicatesTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.exceptions.SnapshotExtensionException; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public class SnapshotNameAnnotationWithDuplicatesTest { - - @SnapshotName("hello_world") - @Test - void canUseSnapshotNameAnnotation(TestInfo testInfo) { - assertThrows( - SnapshotExtensionException.class, - () -> new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()), - "Oops, looks like you set the same name of two separate snapshots @SnapshotName(\"hello_world\") in class au.com.origin.snapshots.SnapshotNameAnnotationTest"); - } - - @SnapshotName("hello_world") - private void anotherMethodWithSameSnapshotName() {} -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotOverrideClassTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotOverrideClassTest.java deleted file mode 100644 index 2526ef3..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotOverrideClassTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; - -public class SnapshotOverrideClassTest extends SnapshotSuperClassTest { - - @BeforeEach - void beforeEach() { - snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), SnapshotOverrideClassTest.class); - } - - @AfterEach - void afterEach() { - snapshotVerifier.validateSnapshots(); - } - - @Override - public String getName() { - return "anyName"; - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotSuperClassTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotSuperClassTest.java deleted file mode 100644 index 95ab048..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotSuperClassTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package au.com.origin.snapshots; - -import lombok.Getter; -import lombok.Setter; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public abstract class SnapshotSuperClassTest { - - @Getter @Setter static SnapshotVerifier snapshotVerifier; - - public abstract String getName(); - - @Test - void shouldMatchSnapshotOne(TestInfo testInfo) { - Expect.of(snapshotVerifier, testInfo.getTestMethod().get()).toMatchSnapshot(getName()); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java deleted file mode 100644 index ba1113d..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package au.com.origin.snapshots; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - -import org.junit.jupiter.api.Test; - -class SnapshotTest { - - @Test - public void shouldParseSnapshot() { - Snapshot snapshot = - Snapshot.parse( - Snapshot.builder().name("au.com.origin.snapshots.Test").body("body").build().raw()); - assertThat(snapshot.getIdentifier()).isEqualTo("au.com.origin.snapshots.Test"); - assertThat(snapshot.getName()).isEqualTo("au.com.origin.snapshots.Test"); - assertThat(snapshot.getHeader()).isEmpty(); - assertThat(snapshot.getScenario()).isBlank(); - assertThat(snapshot.getBody()).isEqualTo("body"); - } - - @Test - public void shouldParseSnapshotWithHeaders() { - SnapshotHeader header = new SnapshotHeader(); - header.put("header1", "value1"); - Snapshot snapshot = - Snapshot.parse( - Snapshot.builder() - .name("au.com.origin.snapshots.Test") - .header(header) - .body("body") - .build() - .raw()); - assertThat(snapshot.getIdentifier()).isEqualTo("au.com.origin.snapshots.Test"); - assertThat(snapshot.getName()).isEqualTo("au.com.origin.snapshots.Test"); - assertThat(snapshot.getHeader()).containsExactly(entry("header1", "value1")); - assertThat(snapshot.getScenario()).isBlank(); - assertThat(snapshot.getBody()).isEqualTo("body"); - } - - @Test - public void shouldParseSnapshotWithScenario() { - Snapshot snapshot = - Snapshot.parse( - Snapshot.builder() - .name("au.com.origin.snapshots.Test") - .scenario("scenario") - .body("body") - .build() - .raw()); - assertThat(snapshot.getIdentifier()).isEqualTo("au.com.origin.snapshots.Test[scenario]"); - assertThat(snapshot.getName()).isEqualTo("au.com.origin.snapshots.Test"); - assertThat(snapshot.getHeader()).isEmpty(); - assertThat(snapshot.getScenario()).isEqualTo("scenario"); - assertThat(snapshot.getBody()).isEqualTo("body"); - } - - @Test - public void shouldParseSnapshotWithScenarioAndHeaders() { - SnapshotHeader header = new SnapshotHeader(); - header.put("header1", "value1"); - Snapshot snapshot = - Snapshot.parse( - Snapshot.builder() - .name("au.com.origin.snapshots.Test") - .scenario("scenario") - .header(header) - .body("body") - .build() - .raw()); - assertThat(snapshot.getIdentifier()).isEqualTo("au.com.origin.snapshots.Test[scenario]"); - assertThat(snapshot.getName()).isEqualTo("au.com.origin.snapshots.Test"); - assertThat(snapshot.getHeader()).containsExactly(entry("header1", "value1")); - assertThat(snapshot.getScenario()).isEqualTo("scenario"); - assertThat(snapshot.getBody()).isEqualTo("body"); - } - - @Test - public void - shouldParseSnapshotWithScenarioAndBodyWithSomethingSimilarToAnScenarioToConfuseRegex() { - Snapshot snapshot = - Snapshot.parse( - Snapshot.builder() - .name("au.com.origin.snapshots.Test") - .scenario("scenario") - .body("[xxx]=yyy") - .build() - .raw()); - assertThat(snapshot.getIdentifier()).isEqualTo("au.com.origin.snapshots.Test[scenario]"); - assertThat(snapshot.getName()).isEqualTo("au.com.origin.snapshots.Test"); - assertThat(snapshot.getHeader()).isEmpty(); - assertThat(snapshot.getScenario()).isEqualTo("scenario"); - assertThat(snapshot.getBody()).isEqualTo("[xxx]=yyy"); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotUtils.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotUtils.java deleted file mode 100644 index 1514cc2..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotUtils.java +++ /dev/null @@ -1,105 +0,0 @@ -package au.com.origin.snapshots; - -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.verify; - -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import java.io.IOException; -import java.lang.reflect.Parameter; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import org.apache.commons.io.FileUtils; -import org.mockito.ArgumentCaptor; - -public class SnapshotUtils { - - public static HashMap>> extractArgs( - T object, String methodName, SnapshotCaptor... snapshotCaptors) { - List captors = new ArrayList<>(); - Class[] classes = new Class[snapshotCaptors.length]; - - int i = 0; - for (SnapshotCaptor snapshotCaptor : snapshotCaptors) { - classes[i] = snapshotCaptor.getParameterClass(); - captors.add(ArgumentCaptor.forClass(snapshotCaptor.getParameterClass())); - i++; - } - - return process(object, methodName, captors, classes, snapshotCaptors); - } - - public static HashMap>> extractArgs( - T object, String methodName, Class... classes) { - List captors = new ArrayList<>(); - - for (Class clazz : classes) { - captors.add(ArgumentCaptor.forClass(clazz)); - } - - return process(object, methodName, captors, classes, null); - } - - private static HashMap>> process( - T object, - String methodName, - List captors, - Class[] classes, - SnapshotCaptor[] snapshotCaptors) { - HashMap>> result = new HashMap<>(); - try { - Parameter[] parameters = - object.getClass().getDeclaredMethod(methodName, classes).getParameters(); - - object - .getClass() - .getDeclaredMethod(methodName, classes) - .invoke( - verify(object, atLeastOnce()), - captors.stream().map(ArgumentCaptor::capture).toArray()); - - List> extractedObjects = new ArrayList<>(); - - int numberOfCall; - - if (captors.size() > 0) { - numberOfCall = captors.get(0).getAllValues().size(); - - for (int i = 0; i < numberOfCall; i++) { - LinkedHashMap objectMap = new LinkedHashMap<>(); - - int j = 0; - for (ArgumentCaptor captor : captors) { - Object value = captor.getAllValues().get(i); - if (snapshotCaptors != null) { - value = snapshotCaptors[j].removeIgnored(value); - } - objectMap.put(parameters[j].getName(), value); - j++; - } - extractedObjects.add(objectMap); - } - } - - result.put( - object.getClass().getSuperclass().getSimpleName() + "." + methodName, extractedObjects); - } catch (Exception e) { - throw new SnapshotMatchException(e.getMessage(), e.getCause()); - } - - return result; - } - - public static void copyTestSnapshots() { - try { - FileUtils.copyDirectory( - Paths.get("src/test/java/au/com/origin/snapshots/existing-snapshots").toFile(), - Paths.get("src/test/java/au/com/origin/snapshots").toFile()); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException("Can't move files to __snapshots__ folder"); - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotUtilsTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotUtilsTest.java deleted file mode 100644 index 91f39b7..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotUtilsTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package au.com.origin.snapshots; - -import static au.com.origin.snapshots.SnapshotUtils.extractArgs; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -class SnapshotUtilsTest { - - @Mock private FakeObject fakeObject; - - @Test - void shouldExtractArgsFromFakeMethod(TestInfo testInfo) { - fakeObject.fakeMethod("test1", 1L, Arrays.asList("listTest1")); - fakeObject.fakeMethod("test2", 2L, Arrays.asList("listTest1", "listTest2")); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot( - extractArgs( - fakeObject, - "fakeMethod", - new SnapshotCaptor(String.class), - new SnapshotCaptor(Long.class), - new SnapshotCaptor(List.class))); - snapshotVerifier.validateSnapshots(); - } - - @Test - void shouldExtractArgsFromFakeMethodWithComplexObject(TestInfo testInfo) { - FakeObject fake = new FakeObject.FakeObjectBuilder().id("idMock").name("nameMock").build(); - - // With Ignore - fakeObject.fakeMethodWithComplexFakeObject(fake); - Object fakeMethodWithComplexObjectWithIgnore = - extractArgs( - fakeObject, - "fakeMethodWithComplexFakeObject", - new SnapshotCaptor(FakeObject.class, "name")); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(fakeMethodWithComplexObjectWithIgnore); - snapshotVerifier.validateSnapshots(); - } - - @Test - void shouldExtractArgsFromFakeMethodWithComplexFakeObject(TestInfo testInfo) { - - FakeObject fake = new FakeObject.FakeObjectBuilder().id("idMock").name("nameMock").build(); - - // With Ignore - fakeObject.fakeMethodWithComplexObject(fake); - Object fakeMethodWithComplexObjectWithIgnore = - extractArgs( - fakeObject, - "fakeMethodWithComplexObject", - new SnapshotCaptor(Object.class, FakeObject.class, "name")); - - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(fakeMethodWithComplexObjectWithIgnore); - snapshotVerifier.validateSnapshots(); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UpdateSnapshotPropertyTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UpdateSnapshotPropertyTest.java deleted file mode 100644 index c6f2b92..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UpdateSnapshotPropertyTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@Deprecated -@ExtendWith(MockitoExtension.class) -public class UpdateSnapshotPropertyTest { - - @AfterAll - static void afterAll() { - System.clearProperty(SnapshotConfig.JVM_UPDATE_SNAPSHOTS_PARAMETER); - } - - @BeforeEach - public void beforeEach() throws Exception { - File file = - new File( - "src/test/java/au/com/origin/snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap"); - String content = - "au.com.origin.snapshots.UpdateSnapshotPropertyTest.shouldNotUpdateSnapshot=[\n" - + "FakeObject(id=ERROR, value=1, name=anyName1, fakeObject=null)\n" - + "]\n" - + "\n" - + "\n" - + "au.com.origin.snapshots.UpdateSnapshotPropertyTest.shouldUpdateSnapshot=[\n" - + "FakeObject(id=ERROR, value=2, name=anyName2, fakeObject=null)\n" - + "]"; - Path parentDir = file.getParentFile().toPath(); - if (!Files.exists(parentDir)) { - Files.createDirectories(parentDir); - } - Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8)); - } - - @Test - void shouldUpdateSnapshot(TestInfo testInfo) throws IOException { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get(), false); - System.setProperty(SnapshotConfig.JVM_UPDATE_SNAPSHOTS_PARAMETER, ""); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(FakeObject.builder().id("anyId2").value(2).name("anyName2").build()); - snapshotVerifier.validateSnapshots(); - - String content = - new String( - Files.readAllBytes( - Paths.get( - "src/test/java/au/com/origin/snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap")), - StandardCharsets.UTF_8); - Assertions.assertThat(content) - .isEqualTo( - "au.com.origin.snapshots.UpdateSnapshotPropertyTest.shouldNotUpdateSnapshot=[\n" - + "FakeObject(id=ERROR, value=1, name=anyName1, fakeObject=null)\n" - + "]\n" - + "\n" - + "\n" - + "au.com.origin.snapshots.UpdateSnapshotPropertyTest.shouldUpdateSnapshot=[\n" - + "FakeObject(id=anyId2, value=2, name=anyName2, fakeObject=null)\n" - + "]"); - } - - @Test - void shouldNotUpdateSnapshot(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get(), false); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - System.setProperty(SnapshotConfig.JVM_UPDATE_SNAPSHOTS_PARAMETER, "true"); - assertThrows( - SnapshotMatchException.class, - () -> - expect.toMatchSnapshot( - FakeObject.builder().id("anyId1").value(1).name("anyName1").build()), - "Error on: \n" - + "au.com.origin.snapshots.UpdateSnapshotPropertyTest.shouldNotUpdateSnapshot=["); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UpdateSnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UpdateSnapshotTest.java deleted file mode 100644 index b999284..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UpdateSnapshotTest.java +++ /dev/null @@ -1,153 +0,0 @@ -package au.com.origin.snapshots; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Optional; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -public class UpdateSnapshotTest { - - @BeforeEach - public void beforeEach() throws Exception { - File file = - new File("src/test/java/au/com/origin/snapshots/__snapshots__/UpdateSnapshotTest.snap"); - String content = - "au.com.origin.snapshots.UpdateSnapshotTest.canUpdateAllSnapshots=[\n" - + "OLD\n" - + "]\n" - + "\n" - + "\n" - + "au.com.origin.snapshots.UpdateSnapshotTest.canUpdateClassNameSnapshots=[\n" - + "OLD\n" - + "]\n" - + "\n" - + "\n" - + "au.com.origin.snapshots.UpdateSnapshotTest.canUpdateNoSnapshots=[\n" - + "OLD\n" - + "]"; - Path parentDir = file.getParentFile().toPath(); - if (!Files.exists(parentDir)) { - Files.createDirectories(parentDir); - } - Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8)); - } - - @Test - void canUpdateAllSnapshots(TestInfo testInfo) throws IOException { - SnapshotConfig config = - new BaseSnapshotConfig() { - @Override - public Optional updateSnapshot() { - return Optional.of(""); - } - }; - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(config, testInfo.getTestClass().get(), false); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("NEW"); - snapshotVerifier.validateSnapshots(); - - String content = - new String( - Files.readAllBytes( - Paths.get( - "src/test/java/au/com/origin/snapshots/__snapshots__/UpdateSnapshotTest.snap")), - StandardCharsets.UTF_8); - Assertions.assertThat(content) - .isEqualTo( - "au.com.origin.snapshots.UpdateSnapshotTest.canUpdateAllSnapshots=[\n" - + "NEW\n" - + "]\n" - + "\n" - + "\n" - + "au.com.origin.snapshots.UpdateSnapshotTest.canUpdateClassNameSnapshots=[\n" - + "OLD\n" - + "]\n" - + "\n" - + "\n" - + "au.com.origin.snapshots.UpdateSnapshotTest.canUpdateNoSnapshots=[\n" - + "OLD\n" - + "]"); - } - - @Test - void canUpdateNoSnapshots(TestInfo testInfo) { - SnapshotConfig config = - new BaseSnapshotConfig() { - @Override - public Optional updateSnapshot() { - return Optional.empty(); - } - }; - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(config, testInfo.getTestClass().get(), false); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - assertThrows(SnapshotMatchException.class, () -> expect.toMatchSnapshot("FOOBAR")); - } - - @Test - public void canUpdateNewSnapshots() { - SnapshotConfig config = - new BaseSnapshotConfig() { - @Override - public Optional updateSnapshot() { - return Optional.of("new"); - } - }; - - // TODO Pending Implementation - } - - @Test - public void canUpdateClassNameSnapshots(TestInfo testInfo) throws IOException { - SnapshotConfig config = - new BaseSnapshotConfig() { - @Override - public Optional updateSnapshot() { - return Optional.of("UpdateSnapshotTest"); - } - }; - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(config, testInfo.getTestClass().get(), false); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("NEW"); - snapshotVerifier.validateSnapshots(); - - String content = - new String( - Files.readAllBytes( - Paths.get( - "src/test/java/au/com/origin/snapshots/__snapshots__/UpdateSnapshotTest.snap")), - StandardCharsets.UTF_8); - Assertions.assertThat(content) - .isEqualTo( - "au.com.origin.snapshots.UpdateSnapshotTest.canUpdateAllSnapshots=[\n" - + "OLD\n" - + "]\n" - + "\n" - + "\n" - + "au.com.origin.snapshots.UpdateSnapshotTest.canUpdateClassNameSnapshots=[\n" - + "NEW\n" - + "]\n" - + "\n" - + "\n" - + "au.com.origin.snapshots.UpdateSnapshotTest.canUpdateNoSnapshots=[\n" - + "OLD\n" - + "]"); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UseCustomConfigTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UseCustomConfigTest.java deleted file mode 100644 index dea37e3..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UseCustomConfigTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.annotations.UseSnapshotConfig; -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.config.ToStringSnapshotConfig; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@UseSnapshotConfig(ToStringSnapshotConfig.class) -@ExtendWith(MockitoExtension.class) -public class UseCustomConfigTest { - - private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); - - @BeforeAll - static void beforeAll() { - SnapshotUtils.copyTestSnapshots(); - } - - @Test - void canUseSnapshotConfigAnnotationAtClassLevel(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(new TestObject()); - snapshotVerifier.validateSnapshots(); - } - - private class TestObject { - @Override - public String toString() { - return "This is a snapshot of the toString() method"; - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UseCustomSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UseCustomSerializerTest.java deleted file mode 100644 index 21cc8ad..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/UseCustomSerializerTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.config.BaseSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.serializers.UppercaseToStringSerializer; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -public class UseCustomSerializerTest { - - private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); - - @BeforeAll - static void beforeEach() { - SnapshotUtils.copyTestSnapshots(); - } - - @DisplayName("@SnapshotSerializer on a method via new instance") - @Test - public void canUseSnapshotSerializerAnnotationAtMethodLevelUsingNewInstance(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.serializer(new UppercaseToStringSerializer()).toMatchSnapshot(new TestObject()); - snapshotVerifier.validateSnapshots(); - } - - @DisplayName("@SnapshotSerializer on a method via class name") - @Test - public void canUseSnapshotSerializerAnnotationAtMethodLevelUsingClassName(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.serializer(new UppercaseToStringSerializer()).toMatchSnapshot(new TestObject()); - snapshotVerifier.validateSnapshots(); - } - - private class TestObject { - @Override - public String toString() { - return "This is a snapshot of the toString() method"; - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java deleted file mode 100644 index a14be0e..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package au.com.origin.snapshots.comparators; - -import au.com.origin.snapshots.Snapshot; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -class PlainTextEqualsComparatorTest { - - private static final PlainTextEqualsComparator COMPARATOR = new PlainTextEqualsComparator(); - - @Test - void successfulComparison() { - Snapshot snap1 = Snapshot.builder().name("snap1").scenario("A").body("foo").build(); - Snapshot snap2 = Snapshot.builder().name("snap1").scenario("A").body("foo").build(); - Assertions.assertThat(COMPARATOR.matches(snap1, snap2)).isTrue(); - } - - @Test - void failingComparison() { - Snapshot snap1 = Snapshot.builder().name("snap1").scenario("A").body("foo").build(); - Snapshot snap2 = Snapshot.builder().name("snap1").scenario("A").body("bar").build(); - Assertions.assertThat(COMPARATOR.matches(snap1, snap2)).isFalse(); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java deleted file mode 100644 index 5e6e6a8..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java +++ /dev/null @@ -1,43 +0,0 @@ -package au.com.origin.snapshots.config; - -import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; -import au.com.origin.snapshots.comparators.SnapshotComparator; -import au.com.origin.snapshots.reporters.PlainTextSnapshotReporter; -import au.com.origin.snapshots.reporters.SnapshotReporter; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; -import java.util.Collections; -import java.util.List; - -public class BaseSnapshotConfig implements SnapshotConfig { - - @Override - public String getOutputDir() { - return "src/test/java"; - } - - @Override - public String getSnapshotDir() { - return "__snapshots__"; - } - - @Override - public SnapshotSerializer getSerializer() { - return new ToStringSnapshotSerializer(); - } - - @Override - public SnapshotComparator getComparator() { - return new PlainTextEqualsComparator(); - } - - @Override - public List getReporters() { - return Collections.singletonList(new PlainTextSnapshotReporter()); - } - - @Override - public boolean isCI() { - return false; - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java deleted file mode 100644 index 84daa1a..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package au.com.origin.snapshots.config; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotSerializerContext; -import au.com.origin.snapshots.serializers.SerializerType; -import au.com.origin.snapshots.serializers.SnapshotSerializer; - -public class ToStringSnapshotConfig extends BaseSnapshotConfig { - - @Override - public SnapshotSerializer getSerializer() { - return new SnapshotSerializer() { - @Override - public String getOutputFormat() { - return SerializerType.TEXT.name(); - } - - @Override - public Snapshot apply(Object object, SnapshotSerializerContext gen) { - return gen.toSnapshot(object.toString()); - } - }; - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap deleted file mode 100644 index def0f2f..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap +++ /dev/null @@ -1,4 +0,0 @@ -au.com.origin.snapshots.DebugSnapshotLineEndingsTest.existingSnapshotDifferentLineEndings=[ -a -b -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/DebugSnapshotTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/DebugSnapshotTest.snap deleted file mode 100644 index ad53cf0..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/DebugSnapshotTest.snap +++ /dev/null @@ -1,13 +0,0 @@ -au.com.origin.snapshots.DebugSnapshotTest.createDebugFile=[ -Good Snapshot -] - - -au.com.origin.snapshots.DebugSnapshotTest.debugFileCreatedSnapshotMatch=[ -Good Snapshot -] - - -au.com.origin.snapshots.DebugSnapshotTest.debugFileCreatedExistingSnapshot=[ -Good Snapshot -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap deleted file mode 100644 index ad53cf0..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap +++ /dev/null @@ -1,13 +0,0 @@ -au.com.origin.snapshots.DebugSnapshotTest.createDebugFile=[ -Good Snapshot -] - - -au.com.origin.snapshots.DebugSnapshotTest.debugFileCreatedSnapshotMatch=[ -Good Snapshot -] - - -au.com.origin.snapshots.DebugSnapshotTest.debugFileCreatedExistingSnapshot=[ -Good Snapshot -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap.debug b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap.debug deleted file mode 100644 index ad53cf0..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap.debug +++ /dev/null @@ -1,13 +0,0 @@ -au.com.origin.snapshots.DebugSnapshotTest.createDebugFile=[ -Good Snapshot -] - - -au.com.origin.snapshots.DebugSnapshotTest.debugFileCreatedSnapshotMatch=[ -Good Snapshot -] - - -au.com.origin.snapshots.DebugSnapshotTest.debugFileCreatedExistingSnapshot=[ -Good Snapshot -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/PrivateCalledMethodTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/PrivateCalledMethodTest.snap deleted file mode 100644 index ac99c35..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/PrivateCalledMethodTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.PrivateCalledMethodTest.testName=[ -testContent -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/ScenarioTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/ScenarioTest.snap deleted file mode 100644 index 209ac10..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/ScenarioTest.snap +++ /dev/null @@ -1,28 +0,0 @@ -au.com.origin.snapshots.ScenarioTest.canTakeMultipleSnapshotsUsingScenario=[ -Default Snapshot -] - - -au.com.origin.snapshots.ScenarioTest.canTakeMultipleSnapshotsUsingScenario[additional]=[ -Additional Snapshot -] - - -au.com.origin.snapshots.ScenarioTest.canTakeTheSameSnapshotTwice=[ -Default Snapshot -] - - -au.com.origin.snapshots.ScenarioTest.canTakeTheSameSnapshotTwice[scenario]=[ -Scenario Snapshot -] - - -au.com.origin.snapshots.ScenarioTest.cannotTakeDifferentSnapshotsAtDefaultLevel=[ -Default Snapshot -] - - -au.com.origin.snapshots.ScenarioTest.cannotTakeDifferentSnapshotsAtScenarioLevel[scenario]=[ -Default Snapshot -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotHeaders.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotHeaders.snap deleted file mode 100644 index 5fa1e53..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotHeaders.snap +++ /dev/null @@ -1,10 +0,0 @@ -au.com.origin.snapshots.SnapshotHeaders.shouldBeAbleToSnapshotASingleCustomHeader={ - "custom": "anything", - "custom2": "anything2" -}hello world - - -au.com.origin.snapshots.SnapshotHeaders.shouldBeAbleToSnapshotMultipleCustomHeader={ - "custom": "anything", - "custom2": "anything2" -}hello world \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotIntegrationTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotIntegrationTest.snap deleted file mode 100644 index 8e7a6de..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotIntegrationTest.snap +++ /dev/null @@ -1,37 +0,0 @@ -au.com.origin.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotFour=[ -FakeObject(id=anyId4, value=4, name=any -. -. -Name4, fakeObject=null) -] - - -au.com.origin.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotInsidePrivateMethod=[ -FakeObject(id=anyPrivate, value=5, name=anyPrivate, fakeObject=null) -] - - -au.com.origin.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotOne=[ -FakeObject(id=anyId1, value=1, name=anyName1, fakeObject=null) -] - - -au.com.origin.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotThree=[ -FakeObject(id=anyId3, value=3, name=anyName3, fakeObject=null) -] - - -au.com.origin.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotTwo=[ -FakeObject(id=anyId2, value=2, name=anyName2, fakeObject=null) -] - - -au.com.origin.snapshots.SnapshotIntegrationTest.shouldSnapshotUsingSerializerClass=HELLO WORLD - - -au.com.origin.snapshots.SnapshotIntegrationTest.shouldSnapshotUsingSerializerPropertyName=hello world - - -au.com.origin.snapshots.SnapshotIntegrationTest.shouldThrowSnapshotMatchException=[ -FakeObject(id=anyId5, value=7, name=anyName5, fakeObject=null) -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotOverrideClassTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotOverrideClassTest.snap deleted file mode 100644 index b9f842c..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotOverrideClassTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.SnapshotOverrideClassTest.shouldMatchSnapshotOne=[ -anyName -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotUtilsTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotUtilsTest.snap deleted file mode 100644 index b0128f4..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotUtilsTest.snap +++ /dev/null @@ -1,13 +0,0 @@ -au.com.origin.snapshots.SnapshotUtilsTest.shouldExtractArgsFromFakeMethod=[ -{FakeObject.fakeMethod=[{arg0=test1, arg1=1, arg2=[listTest1]}, {arg0=test2, arg1=2, arg2=[listTest1, listTest2]}]} -] - - -au.com.origin.snapshots.SnapshotUtilsTest.shouldExtractArgsFromFakeMethodWithComplexFakeObject=[ -{FakeObject.fakeMethodWithComplexObject=[{arg0=FakeObject(id=idMock, value=null, name=null, fakeObject=null)}]} -] - - -au.com.origin.snapshots.SnapshotUtilsTest.shouldExtractArgsFromFakeMethodWithComplexObject=[ -{FakeObject.fakeMethodWithComplexFakeObject=[{arg0=FakeObject(id=idMock, value=null, name=null, fakeObject=null)}]} -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap deleted file mode 100644 index 9cf2c6c..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap +++ /dev/null @@ -1,8 +0,0 @@ -au.com.origin.snapshots.UpdateSnapshotPropertyTest.shouldNotUpdateSnapshot=[ -FakeObject(id=ERROR, value=1, name=anyName1, fakeObject=null) -] - - -au.com.origin.snapshots.UpdateSnapshotPropertyTest.shouldUpdateSnapshot=[ -FakeObject(id=ERROR, value=1, name=anyName2, fakeObject=null) -] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/UseCustomConfigTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/UseCustomConfigTest.snap deleted file mode 100644 index e25ac7b..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/UseCustomConfigTest.snap +++ /dev/null @@ -1,4 +0,0 @@ -au.com.origin.snapshots.UseCustomConfigTest.canUseSnapshotConfigAnnotationAtClassLevel=This is a snapshot of the toString() method - - -au.com.origin.snapshots.UseCustomConfigTest.canUseSnapshotConfigAnnotationAtMethodLevel=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/UseCustomSerializerTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/UseCustomSerializerTest.snap deleted file mode 100644 index 5d5d5bb..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/UseCustomSerializerTest.snap +++ /dev/null @@ -1,10 +0,0 @@ -au.com.origin.snapshots.UseCustomSerializerTest.canUseSnapshotSerializerAnnotationAtClassLevel=this is a snapshot of the tostring() method - - -au.com.origin.snapshots.UseCustomSerializerTest.canUseSnapshotSerializerAnnotationAtMethodLevel=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD - - -au.com.origin.snapshots.UseCustomSerializerTest.canUseSnapshotSerializerAnnotationAtMethodLevelUsingClassName=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD - - -au.com.origin.snapshots.UseCustomSerializerTest.canUseSnapshotSerializerAnnotationAtMethodLevelUsingNewInstance=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java deleted file mode 100644 index c6bd638..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package au.com.origin.snapshots.reporters; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.serializers.SerializerType; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; -import org.opentest4j.AssertionFailedError; - -class PlainTextSnapshotReporterTest { - private static final PlainTextSnapshotReporter REPORTER = new PlainTextSnapshotReporter(); - - @Test - void shouldSupportAllFormats() { - Assertions.assertThat(REPORTER.supportsFormat(SerializerType.TEXT.name())).isTrue(); - Assertions.assertThat(REPORTER.supportsFormat(SerializerType.JSON.name())).isTrue(); - - Assertions.assertThat(REPORTER.supportsFormat("xml")).isTrue(); - Assertions.assertThat(REPORTER.supportsFormat("blah")).isTrue(); - } - - @Test - void doReport() { - Snapshot snap1 = Snapshot.builder().name("snap1").scenario("A").body("[\nfoo\n]").build(); - Snapshot snap2 = Snapshot.builder().name("snap1").scenario("A").body("[\nbar\n]").build(); - assertThatExceptionOfType(AssertionFailedError.class) - .isThrownBy(() -> REPORTER.report(snap1, snap2)) - .withMessageContaining("expecting:") - .withMessageContaining("[\"foo\"]") - .withMessageContaining("but was:") - .withMessageContaining("[\"bar\"]"); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/Base64SnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/Base64SnapshotSerializerTest.java deleted file mode 100644 index 7acd308..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/Base64SnapshotSerializerTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package au.com.origin.snapshots.serializers; - -import static org.assertj.core.api.Assertions.assertThat; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotHeader; -import au.com.origin.snapshots.SnapshotSerializerContext; -import java.io.File; -import java.nio.file.Files; -import org.junit.jupiter.api.Test; - -public class Base64SnapshotSerializerTest { - - private SnapshotSerializerContext mockSnapshotGenerator = - new SnapshotSerializerContext( - "base64Test", - null, - new SnapshotHeader(), - Base64SnapshotSerializerTest.class, - null // it's not used in these scenarios - ); - - @Test - void shouldSnapshotAByteArray() { - Base64SnapshotSerializer serializer = new Base64SnapshotSerializer(); - Snapshot result = serializer.apply("John Doe".getBytes(), mockSnapshotGenerator); - assertThat(result.getBody()).isEqualTo("[\nSm9obiBEb2U=\n]"); - } - - @Test - void shouldSnapshotAnyString() { - Base64SnapshotSerializer serializer = new Base64SnapshotSerializer(); - Snapshot result = serializer.apply("John Doe", mockSnapshotGenerator); - assertThat(result.getBody()).isEqualTo("[\nSm9obiBEb2U=\n]"); - } - - @Test - void shouldSnapshotAFile() throws Exception { - Base64SnapshotSerializer serializer = new Base64SnapshotSerializer(); - File f = new File("src/test/resources/origin-logo.png"); - byte[] content = Files.readAllBytes(f.toPath()); - - Snapshot result = serializer.apply(content, mockSnapshotGenerator); - assertThat(result.getBody()) - .isEqualTo( - "[\niVBORw0KGgoAAAANSUhEUgAAAFgAAABYCAIAAAD+96djAAAKaklEQVR4nOxce3BcVRk/59zX3t3N5p3m1UeaNLQ2adKaFhDp9MFD3j7Qoqhg1VHpjEMR7CgKyghVQKa24wzSUhQcxwGLU0TsH0AphQLSxKSQJm02aRrSvDbPfd/HOddZ2LLJZp/n3F0aJr/Zf7J77nd+95fvfN85537n8oZhgHkAgD5pAhcK5oUIY16IMOaFCGNeiDD4rPVEpiZ1p1PvceLhITIyQlwuw+c1FMVQFGAYUJKAJEFZ5oqKUUkJKlnAL63mq5dxxcXZoQczmj7xqEtrflc7fUprbSGuEQoLMC9fbFzN1y4X1zRx5RUZ4Hi+o0wIQdxu5Y0jyuuv6p0dwDz73JIqacMmaf0mrrDILJsfw2Qh9L7ewMF/KEdfA5pmotkZQEhcd6l801eE5StNtGqaEHrfWf8z+9T/vWuiCyQGv2Kl7datwoo6U6yZI4Ty9lH/M0/i4UEzKKUB5Mi1bvm2ZfMXAM8a9VmFwMODvn171LZmRh4s4BYusf/gTqF2BYsRBiEwDhw66H/2L6H890kDIiRddb3tlq3QYqG0QCcEcU96//iw9l4LXa8ZAle5KOeu+7lSmixLI4Tu7PTseZCMuij6yzSg1WbftkNsXJf2hekKob7X7Nn1AFA/+eEQF4iz3bbNsunatC5KTwjl7SPevY8CPWNzBPMg33y79YZbUm+fRtZR3n7N+6ffhaYJkIpadhE48GcIoXz9lhTbpyqE2vqW78lHAJwbKnwE//NPAQ7J13w1lcYpCaF1d3gf3wkMAtHckeFDBA7sR3kF0qWbk7ZMLgSZHPM98RAg2hzdu/D9dTcqKhGW1SduliRY4vER3/5H9K73zaaXXUhS3kNPI7sjQZOEQhDi3nOv3tmaEXLZBbdoWe5dDwMp7rwzkbsHj76on24NNZn7H9zf5Xt+b4KbjSsEHh8JHHwqlCM+LR/l2EvaqbjeHTdYBl96xtAVYHaagILIV63ga1bxC2tQSQVy5ENeABACTSN+Dx45hwfO6N3va10njIDP3K5DSeTgPuHuPwDExSAWM0boHzg9j90JDGIiCW5hjXTZdWLDZVC2J2+tKWr7u8obL+rOEyZyAABYv7FdWnfl7O9jC+F54j6987hZfaOSSutN3xdWrKW4VjvTHvjXftzbCYA5G1+oYEHuz/YCLnooxBBC73d6dv3YlH6hbJO/vE1adRngBXorhGi9J/1/f4yMDZnACQDrLdultdFOESNYKq89F4ouzIGaW1ids323tGYDkwof7tYKS+sc23cLK9eZkkGCh58DBCcRgkyNqu3H2DsTVl7suONRrrCMSYJpgLLdfvsvpctvZOdGxvq17ujQEy2E2vwyMDBjouJr19i/dS8QJbNUOE+Ws974Q8umLeypVG15Jcp2dMzQuo4zrqygLdf6xTtmRyOzIG/cop8+jgd7WIzoPW0AY8BF8ugMj8DjQ7ivg0lsBK03b+cKy1lYJoFosd76c2i1sfA0vOPa2RkLqBlC6M5mxuEnXnKdcBFNmkwLXEGZfMOPGKnqXc1xhdC6W1hMw/xi65W3ZVqFjyA2bOSrG1jYat3xhCAE97ax+Ju86ZtAsmZHiFCwuHpraAVAy5a4eolvMoYQeKzf0ALUAqPCMrF+Q9ZUCA2QshrhIoaZBQR4sCuWEENOpujQeEXmMkU8iE3XsnDGg86PTUWo4+EelsQp1K1nvq/0O61qRPY8wz9FdzkeieTgiEcQ9zD1eENFFVx+JlNmPHA8t6SePky4R2IIYXhc1D7GLTKnSIEC/KJ6atqGJyJEZGgQ7yig3afmihebcE/UXdPSNhQP0BQgSDOEMDQfoA0RqCCDZV5Jus4vp6Yd+vcrPjRDCIIB0amlhdZEO+UZBbLmfrifSLt9ooefZp8XQldhSAVKbaGYvXlUNBAHJSnk4VQwooWAiNodQiA6w8XMgICe/PmN3PNCCFLIwWjLiAzVT0vEDBCVOkxAPrxpMm0uKEpAC9KZM7wuAJhquagRSnYsz+iFWUJAW67hphSCTJ2jJcIKMtHPMi6QFH62EBEC5ZRg7zCdQezqouXCCjzcQS0EyimaFSMAQI5iTLtdjodPAqxnf9EVSneDJ+gDRE6k9H+aEHmV9Isu3a8NtAoLmygvpwXxjxNXBzVtlBdZH0W8iiuuYVnS6l3R+8JZgOY8DAChXyIVVn1sappH5FayzFX1wVag+oBoY725dID73mLhjByRxy4Rj0D2YmgroHcK3ad0/pv1ztKBPtiGx04zeDFEhdUxhAiNjvJGltGhnXyeBCayJAPWlOP7WNiioqVIzostBF/ewPSwQPerLfuzo4PSfoBMnWVhy1fOCO0zEh5XtjqUAo3oB6SpQzv7OregTqi5muEekwO7Tqonn2UsYuErL57x5/Q/kJzHVzbhc++wdKC07IX2BXxpI4uRBCA+V/DYoxBgllUidCzkCmumfxNtTKi+isXfQh9DCx7bicczMtc0/KOBI/cZwTFGkkLVxijL0ULwpathTglLEAp9cDBw5Bf6cJu5KhDPgP/wDsM3wEqPF/glyYQAiBOqr2HtCQFAgsE3f612PDu7JIMOev+b/sN3G8FRdm784vVILoiyH6uGSvV6D30XYMqVaBS4guVC7Zf4skuoLeAJp+Z8Qe8/YgofAKC8eTfnWBT9bcxiMqX9aa3rgEkdh8CVNIq1X+OK0jupid29Wtc/9f6jLIksCnzF5y1r75n9fZwSZC3gP7zNCI6b1X24M8divmI9v6AJORJt/xPvOTzSrA8cI+OnzCqmCwPx8sY9yBajoCluLbb2watq224zSUyHlMc5qqC9AooOiMTQZAxrhuo2vOeI+4yhZGp6yi+9UfrM1pg/JSpKD/73V3j001CR/hGgvMB6+S7AyzF/TTQpEevuAILMOq24QD4ISvXb4qmQ/LyG1v+y2vEEIGo60l+IEJd/T1h8fYIGSTbXhMorIOKV9l0m84I8tBRBIQciCXASgBBgxcAK0H0kOAqIyWcpuaK1iVVI6SgTX74Buzv1/kMsVKBcyuXWopwa5KhG1jIo5SUYlYY6RQJDxNND3N3E7SS+syxvIODyV1oafpqcYUrnPokWbPsNmUizUF7M5Ys/h/JXoZylSC5J79ppMFQ38fTgiTY88qYRTO/4MbRWyGseBGJu8papHoDVA4HWew3vmRQ657jCJr50M1ewGiBz97UJnmjXB1/Bo2+lEragVCyt2YmkwlRMp3MSWJ0MnLjf8PXFbcDLfOkVfPm1SC5N1SYVDHVSGzikD/4HqO54baBUIK16AFlTrVdI70i0obmDrTuMYIznHyi3Tlr+EyjmpW6NFbpfcT6OXUdj/MRZLA2/RbY0ylfSf1sADgbadhj+iF9AqUhafg/KqU3PjkkggSH11O+J1zmNT6Glfie0pPfeJqr3R2hu5fRjeKotNBqKN4pV3wF8TtpGTATRtL6/aYMvAIMg+zLponuglPbbq6jfKEK0/gPItpTL/yzV5eYDe06RiXeEyq8DRHNOJrMv5JpDmJvnvTOAeSHCmBcijHkhwpgXIox5IcKYFyKM/wcAAP//h4bYYlJz5AYAAAAASUVORK5CYII=\n]"); - } - - @Test - void shouldSupportBase64SerializerType() { - Base64SnapshotSerializer serializer = new Base64SnapshotSerializer(); - assertThat(serializer.getOutputFormat()).isEqualTo("BASE64"); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java deleted file mode 100644 index 265d57e..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java +++ /dev/null @@ -1,16 +0,0 @@ -package au.com.origin.snapshots.serializers; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotSerializerContext; - -public class LowercaseToStringSerializer implements SnapshotSerializer { - @Override - public Snapshot apply(Object object, SnapshotSerializerContext gen) { - return gen.toSnapshot(object.toString().toLowerCase()); - } - - @Override - public String getOutputFormat() { - return SerializerType.TEXT.name(); - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java deleted file mode 100644 index b8035db..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package au.com.origin.snapshots.serializers; - -import static org.assertj.core.api.Assertions.assertThat; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotHeader; -import au.com.origin.snapshots.SnapshotSerializerContext; -import lombok.AllArgsConstructor; -import lombok.Data; -import org.junit.jupiter.api.Test; - -public class ToStringSnapshotSerializerTest { - ToStringSnapshotSerializer serializer = new ToStringSnapshotSerializer(); - - private SnapshotSerializerContext mockSnapshotGenerator = - new SnapshotSerializerContext( - "base64Test", - null, - new SnapshotHeader(), - ToStringSnapshotSerializerTest.class, - null // it's not used in these scenarios - ); - - @Test - void shouldSnapshotAnyString() { - Snapshot result = serializer.apply("John Doe", mockSnapshotGenerator); - assertThat(result.getBody()).isEqualTo("[\nJohn Doe\n]"); - } - - @Test - void shouldSnapshotUnicode() { - Snapshot result = serializer.apply("🤔", mockSnapshotGenerator); - assertThat(result.getBody()).isEqualTo("[\n🤔\n]"); - } - - @Test - void shouldSnapshotAnyObject() { - Snapshot result = serializer.apply(new Dummy(1, "John Doe"), mockSnapshotGenerator); - assertThat(result.getBody()) - .isEqualTo("[\nToStringSerializerTest.Dummy(id=1, name=John Doe)\n]"); - } - - @Test - void shouldSnapshotMultipleObjects() { - Snapshot result = serializer.apply(new Dummy(1, "John Doe"), mockSnapshotGenerator); - assertThat(result.getBody()) - .isEqualTo("[\nToStringSerializerTest.Dummy(id=1, name=John Doe)\n]"); - } - - @Test - void shouldSupportBase64SerializerType() { - assertThat(serializer.getOutputFormat()).isEqualTo("TEXT"); - } - - @Test - void shouldReplaceThreeConsecutiveNewLines() { - Snapshot result = serializer.apply("John\n\n\nDoe", mockSnapshotGenerator); - assertThat(result.getBody()).isEqualTo("[\nJohn\n.\n.\nDoe\n]"); - } - - @Test - void shouldReplaceTwoConsecutiveNewLinesAtEnd() { - Snapshot result = serializer.apply("John Doe\n\n", mockSnapshotGenerator); - assertThat(result.getBody()).isEqualTo("[\nJohn Doe\n.\n.\n]"); - } - - @Test - void shouldReplaceTwoConsecutiveNewLinesAtBeginning() { - Snapshot result = serializer.apply("\n\nJohn Doe", mockSnapshotGenerator); - assertThat(result.getBody()).isEqualTo("[\n.\n.\nJohn Doe\n]"); - } - - @Test - void shouldReplaceIllegalNewlineSequencesEverywhere() { - Snapshot result = serializer.apply("\n\nJohn\n\n\nDoe\n\n", mockSnapshotGenerator); - assertThat(result.getBody()).isEqualTo("[\n.\n.\nJohn\n.\n.\nDoe\n.\n.\n]"); - } - - @AllArgsConstructor - @Data - private static class Dummy { - private int id; - private String name; - - public String toString() { - return "ToStringSerializerTest.Dummy(id=" + this.getId() + ", name=" + this.getName() + ")"; - } - } -} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java deleted file mode 100644 index 70cd338..0000000 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java +++ /dev/null @@ -1,16 +0,0 @@ -package au.com.origin.snapshots.serializers; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotSerializerContext; - -public class UppercaseToStringSerializer implements SnapshotSerializer { - @Override - public Snapshot apply(Object object, SnapshotSerializerContext gen) { - return gen.toSnapshot(object.toString().toUpperCase()); - } - - @Override - public String getOutputFormat() { - return SerializerType.TEXT.name(); - } -} diff --git a/java-snapshot-testing-core/src/test/resources/snapshot.properties b/java-snapshot-testing-core/src/test/resources/snapshot.properties deleted file mode 100644 index 31d4012..0000000 --- a/java-snapshot-testing-core/src/test/resources/snapshot.properties +++ /dev/null @@ -1 +0,0 @@ -serializer.lowercase=au.com.origin.snapshots.serializers.LowercaseToStringSerializer \ No newline at end of file diff --git a/java-snapshot-testing-junit4/build.gradle b/java-snapshot-testing-junit4/build.gradle deleted file mode 100644 index 54745cb..0000000 --- a/java-snapshot-testing-junit4/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply from: "../gradle/publishing.gradle" -apply from: "../gradle/spotless.gradle" - -dependencies { - implementation project(':java-snapshot-testing-core') - - // User supplied JUnit4 Version - compileOnly 'org.junit.platform:junit-platform-runner:1.2.0' - compileOnly 'org.junit.vintage:junit-vintage-engine:5.2.0' - - // Testing - testImplementation 'org.slf4j:slf4j-simple:2.0.0-alpha0' - testImplementation 'org.junit.platform:junit-platform-runner:1.2.0' - testImplementation 'org.junit.vintage:junit-vintage-engine:5.2.0' - testImplementation 'org.assertj:assertj-core:3.11.1' - - // Required java-snapshot-testing peer dependencies - testImplementation 'com.fasterxml.jackson.core:jackson-core:2.11.3' - testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.11.3' -} - -publishing { - publications { - myPublication(MavenPublication) { - artifact shadowJar - groupId 'io.github.origin-energy' - artifactId 'java-snapshot-testing-junit4' - } - } -} \ No newline at end of file diff --git a/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SharedSnapshotHelpers.java b/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SharedSnapshotHelpers.java deleted file mode 100644 index c6b01e4..0000000 --- a/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SharedSnapshotHelpers.java +++ /dev/null @@ -1,58 +0,0 @@ -package au.com.origin.snapshots.junit4; - -import au.com.origin.snapshots.*; -import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfigInjector; -import au.com.origin.snapshots.utils.ReflectionUtils; -import java.lang.reflect.Method; -import java.util.Arrays; -import org.junit.runner.Description; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; - -class SharedSnapshotHelpers implements SnapshotConfigInjector { - - public void injectExpectInstanceVariable( - SnapshotVerifier snapshotVerifier, Method testMethod, Object testInstance) { - - ReflectionUtils.findFieldByPredicate( - testInstance.getClass(), (field) -> field.getType() == Expect.class) - .ifPresent( - (field) -> { - Expect expect = Expect.of(snapshotVerifier, testMethod); - ReflectionUtils.makeAccessible(field); - try { - field.set(testInstance, expect); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }); - } - - public Statement injectExpectMethodArgument( - SnapshotVerifier snapshotVerifier, FrameworkMethod method, Object test) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - method.invokeExplosively(test, new Expect(snapshotVerifier, method.getMethod())); - } - }; - } - - public boolean hasExpectArgument(FrameworkMethod method) { - return Arrays.asList(method.getMethod().getParameterTypes()).contains(Expect.class); - } - - @Override - public SnapshotConfig getSnapshotConfig() { - return new PropertyResolvingSnapshotConfig(); - } - - public SnapshotVerifier getSnapshotVerifier(Description description) { - // We don't want the orphan check to happen when the user runs a single test in their IDE - boolean failOnOrphans = description.getChildren().size() > 1; - - return new SnapshotVerifier(getSnapshotConfig(), description.getTestClass(), failOnOrphans); - } -} diff --git a/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SnapshotClassRule.java b/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SnapshotClassRule.java deleted file mode 100644 index b04aef8..0000000 --- a/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SnapshotClassRule.java +++ /dev/null @@ -1,29 +0,0 @@ -package au.com.origin.snapshots.junit4; - -import au.com.origin.snapshots.SnapshotVerifier; -import lombok.Getter; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -public class SnapshotClassRule implements TestRule { - - @Getter private SnapshotVerifier snapshotVerifier; - - @Getter private SharedSnapshotHelpers helpers = new SharedSnapshotHelpers(); - - @Override - public Statement apply(Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - snapshotVerifier = helpers.getSnapshotVerifier(description); - try { - base.evaluate(); - } finally { - snapshotVerifier.validateSnapshots(); - } - } - }; - } -} diff --git a/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SnapshotRule.java b/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SnapshotRule.java deleted file mode 100644 index 88da396..0000000 --- a/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SnapshotRule.java +++ /dev/null @@ -1,32 +0,0 @@ -package au.com.origin.snapshots.junit4; - -import au.com.origin.snapshots.exceptions.SnapshotExtensionException; -import lombok.RequiredArgsConstructor; -import org.junit.rules.MethodRule; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; - -@RequiredArgsConstructor -public class SnapshotRule implements MethodRule { - - private final SnapshotClassRule snapshotClassRule; - - @Override - public Statement apply(Statement base, FrameworkMethod method, Object test) { - final SharedSnapshotHelpers helpers = snapshotClassRule.getHelpers(); - helpers.injectExpectInstanceVariable( - snapshotClassRule.getSnapshotVerifier(), method.getMethod(), test); - if (helpers.hasExpectArgument(method)) { - throw new SnapshotExtensionException( - "Sorry, we don't support 'Expect' as a method argument for @Rule or @ClassRule. " - + "Please use an instance variable or @RunWith(SnapshotRunner.class) instead."); - } - - return new Statement() { - @Override - public void evaluate() throws Throwable { - base.evaluate(); - } - }; - } -} diff --git a/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SnapshotRunner.java b/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SnapshotRunner.java deleted file mode 100644 index dfc954e..0000000 --- a/java-snapshot-testing-junit4/src/main/java/au/com/origin/snapshots/junit4/SnapshotRunner.java +++ /dev/null @@ -1,73 +0,0 @@ -package au.com.origin.snapshots.junit4; - -import au.com.origin.snapshots.SnapshotVerifier; -import au.com.origin.snapshots.logging.LoggingHelper; -import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; - -/** - * Runner to enable java-snapshot-testing - * - *

If you are already using @RunWith for something else such as @RunWith(Parameterized.class) use - * these Rules instead. - * - * @see SnapshotClassRule - * @see SnapshotRule - *

{@code
- * {@literal @}ClassRule
- * public static SnapshotClassRule snapshotClassRule = new SnapshotClassRule();
- *
- * {@literal @}Rule
- * public SnapshotRule snapshotRule = new SnapshotRule(snapshotClassRule);
- *
- * private Expect expect;
- * }
- * Loosely based on: - * https://stackoverflow.com/questions/27745691/how-to-combine-runwith-with-runwithparameterized-class - */ -@Slf4j -public class SnapshotRunner extends BlockJUnit4ClassRunner { - - SnapshotVerifier snapshotVerifier; - - private SharedSnapshotHelpers helpers = new SharedSnapshotHelpers(); - - public SnapshotRunner(Class klass) throws InitializationError { - super(klass); - } - - @Override - protected Statement methodInvoker(FrameworkMethod method, Object test) { - boolean isTest = method.getMethod().isAnnotationPresent(Test.class); - if (isTest) { - helpers.injectExpectInstanceVariable(snapshotVerifier, method.getMethod(), test); - boolean shouldInjectMethodArgument = helpers.hasExpectArgument(method); - if (shouldInjectMethodArgument) { - LoggingHelper.deprecatedV5( - log, - "Injecting 'Expect' via method a argument is no longer recommended. Consider using instance variable injection instead."); - return helpers.injectExpectMethodArgument(snapshotVerifier, method, test); - } - } - - return super.methodInvoker(method, test); - } - - @Override - public void run(RunNotifier notifier) { - snapshotVerifier = helpers.getSnapshotVerifier(getDescription()); - super.run(notifier); - snapshotVerifier.validateSnapshots(); - } - - @Override - protected void validateTestMethods(List errors) { - // Disable as it checks for zero arguments - } -} diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/BaseClassTest.java b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/BaseClassTest.java deleted file mode 100644 index fbd78ad..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/BaseClassTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.junit4.SnapshotRunner; -import org.junit.Test; -import org.junit.experimental.runners.Enclosed; -import org.junit.runner.RunWith; - -@RunWith(Enclosed.class) -public class BaseClassTest { - - static class TestBase { - Expect expect; - } - - @RunWith(SnapshotRunner.class) - public static class NestedClass extends TestBase { - - @Test - public void helloWorldTest() { - expect.toMatchSnapshot("Hello World"); - } - } -} diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/MyTest.java b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/MyTest.java deleted file mode 100644 index 9ad8827..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/MyTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.junit4.SnapshotRunner; -import junit.framework.TestCase; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(SnapshotRunner.class) -public class MyTest { - - @Test - public void someTest(Expect expect) { - expect.toMatchSnapshot("Hello World"); - } - - @Test - public void aNormalTest() { - TestCase.assertTrue(true); - } -} diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/ParameterizedTest.java b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/ParameterizedTest.java deleted file mode 100644 index 444cb18..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/ParameterizedTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.junit4.SnapshotClassRule; -import au.com.origin.snapshots.junit4.SnapshotRule; -import java.util.Arrays; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -@RunWith(Parameterized.class) -public class ParameterizedTest { - - @ClassRule public static SnapshotClassRule snapshotClassRule = new SnapshotClassRule(); - @Rule public SnapshotRule snapshotRule = new SnapshotRule(snapshotClassRule); - @Rule public TestName testName = new TestName(); - - private Expect expect; - - @Parameters(name = "letter is {0}") - public static Iterable data() { - return Arrays.asList(new Object[][] {{"a"}, {"b"}, {"c"}}); - } - - private String input; - - public ParameterizedTest(String input) { - this.input = input; - } - - @Test - public void test() { - expect.scenario(input).toMatchSnapshot(input); - } -} diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/SnapshotRuleUsedTest.java b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/SnapshotRuleUsedTest.java deleted file mode 100644 index f7ee367..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/SnapshotRuleUsedTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.junit4.SnapshotClassRule; -import au.com.origin.snapshots.junit4.SnapshotRule; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; - -public class SnapshotRuleUsedTest { - - @ClassRule public static SnapshotClassRule snapshotClassRule = new SnapshotClassRule(); - - @Rule public SnapshotRule snapshotRule = new SnapshotRule(snapshotClassRule); - - private Expect expect; - - @Test - public void shouldUseExtensionViaInstanceVariable() { - this.expect.toMatchSnapshot("Hello World"); - } - - @Test - public void shouldUseExtensionAgainViaInstanceVariable() { - this.expect.toMatchSnapshot("Hello World"); - } - - @SnapshotName("hello_world") - @Test - public void shouldUseExtensionWithSnapshotName() { - expect.toMatchSnapshot("Hello World"); - } -} diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/SnapshotRunnerUsedTest.java b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/SnapshotRunnerUsedTest.java deleted file mode 100644 index d2ec5b0..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/SnapshotRunnerUsedTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.junit4.SnapshotRunner; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(SnapshotRunner.class) -public class SnapshotRunnerUsedTest { - - private Expect expect; - - @Test - public void shouldUseExtension(Expect expect) { - expect.toMatchSnapshot("Hello World"); - } - - @Test - public void shouldUseExtensionAgain(Expect expect) { - expect.toMatchSnapshot("Hello World"); - } - - @Test - public void shouldUseExtensionViaInstanceVariable() { - this.expect.toMatchSnapshot("Hello World"); - } - - @Test - public void shouldUseExtensionAgainViaInstanceVariable() { - this.expect.toMatchSnapshot("Hello World"); - } - - @SnapshotName("hello_world") - @Test - public void shouldUseExtensionWithSnapshotName() { - expect.toMatchSnapshot("Hello World"); - } - - @SnapshotName("hello_world_again") - @Test - public void shouldUseExtensionAgainWithSnapshotName(Expect expect) { - expect.toMatchSnapshot("Hello World"); - } -} diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/BaseClassTest$NestedClass.snap b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/BaseClassTest$NestedClass.snap deleted file mode 100644 index a659739..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/BaseClassTest$NestedClass.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.BaseClassTest$NestedClass.helloWorldTest=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/MyTest.snap b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/MyTest.snap deleted file mode 100644 index 63c07f0..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/MyTest.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.MyTest.someTest=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/ParameterizedTest.snap b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/ParameterizedTest.snap deleted file mode 100644 index 0a2175b..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/ParameterizedTest.snap +++ /dev/null @@ -1,13 +0,0 @@ -au.com.origin.snapshots.ParameterizedTest.test[a]=[ -a -] - - -au.com.origin.snapshots.ParameterizedTest.test[b]=[ -b -] - - -au.com.origin.snapshots.ParameterizedTest.test[c]=[ -c -] \ No newline at end of file diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotContextRuleUsedTest.snap b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotContextRuleUsedTest.snap deleted file mode 100644 index cb453d4..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotContextRuleUsedTest.snap +++ /dev/null @@ -1,31 +0,0 @@ -au.com.origin.snapshots.SnapshotRuleUsedTest.shouldUseExtension=[ -Hello World -] - - -au.com.origin.snapshots.SnapshotRuleUsedTest.shouldUseExtensionAgain=[ -Hello World -Hello World Again -] - - -au.com.origin.snapshots.SnapshotRuleUsedTest.shouldUseExtensionAgainViaInstanceVariable=[ -Hello World -Hello World Again -] - - -au.com.origin.snapshots.SnapshotRuleUsedTest.shouldUseExtensionViaInstanceVariable=[ -Hello World -] - - -hello_world=[ -Hello World -] - - -hello_world_again=[ -Hello World -Hello World Again -] \ No newline at end of file diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotRuleUsedTest.snap b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotRuleUsedTest.snap deleted file mode 100644 index 0aeefe0..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotRuleUsedTest.snap +++ /dev/null @@ -1,13 +0,0 @@ -au.com.origin.snapshots.SnapshotRuleUsedTest.shouldUseExtensionAgainViaInstanceVariable=[ -Hello World -] - - -au.com.origin.snapshots.SnapshotRuleUsedTest.shouldUseExtensionViaInstanceVariable=[ -Hello World -] - - -hello_world=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotRunnerUsedTest.snap b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotRunnerUsedTest.snap deleted file mode 100644 index 7c762ff..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotRunnerUsedTest.snap +++ /dev/null @@ -1,28 +0,0 @@ -au.com.origin.snapshots.SnapshotRunnerUsedTest.shouldUseExtension=[ -Hello World -] - - -au.com.origin.snapshots.SnapshotRunnerUsedTest.shouldUseExtensionAgain=[ -Hello World -] - - -au.com.origin.snapshots.SnapshotRunnerUsedTest.shouldUseExtensionAgainViaInstanceVariable=[ -Hello World -] - - -au.com.origin.snapshots.SnapshotRunnerUsedTest.shouldUseExtensionViaInstanceVariable=[ -Hello World -] - - -hello_world=[ -Hello World -] - - -hello_world_again=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/JUnit4Example.java b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/JUnit4Example.java deleted file mode 100644 index cd8cf31..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/JUnit4Example.java +++ /dev/null @@ -1,29 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.junit4.SnapshotRunner; -import org.junit.Test; -import org.junit.runner.RunWith; - -// Ensure you RunWith the SnapshotRunner -@RunWith(SnapshotRunner.class) -public class JUnit4Example { - - // Option 1: inject Expect as an instance variable - private Expect expect; - - @SnapshotName("my first test") - @Test - public void myTest1() { - // Verify your snapshot - expect.toMatchSnapshot("Hello World"); - } - - @SnapshotName("my second test") - @Test - // Option 2: inject Expect into the method signature - public void myTest2(Expect expect) { - expect.toMatchSnapshot("Hello World Again"); - } -} diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/JUnit4RulesExample.java b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/JUnit4RulesExample.java deleted file mode 100644 index a2b94d7..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/JUnit4RulesExample.java +++ /dev/null @@ -1,25 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.junit4.SnapshotClassRule; -import au.com.origin.snapshots.junit4.SnapshotRule; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; - -public class JUnit4RulesExample { - - @ClassRule public static SnapshotClassRule snapshotClassRule = new SnapshotClassRule(); - - @Rule public SnapshotRule snapshotRule = new SnapshotRule(snapshotClassRule); - - private Expect expect; - - @SnapshotName("my first test") - @Test - public void myTest1() { - // Verify your snapshot - expect.toMatchSnapshot("Hello World"); - } -} diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit4Example.snap b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit4Example.snap deleted file mode 100644 index c7300b4..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit4Example.snap +++ /dev/null @@ -1,8 +0,0 @@ -my first test=[ -Hello World -] - - -my second test=[ -Hello World Again -] \ No newline at end of file diff --git a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit4RulesExample.snap b/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit4RulesExample.snap deleted file mode 100644 index 9561cb6..0000000 --- a/java-snapshot-testing-junit4/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit4RulesExample.snap +++ /dev/null @@ -1,3 +0,0 @@ -my first test=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit4/src/test/resources/snapshot.properties b/java-snapshot-testing-junit4/src/test/resources/snapshot.properties deleted file mode 100644 index 52d673d..0000000 --- a/java-snapshot-testing-junit4/src/test/resources/snapshot.properties +++ /dev/null @@ -1,7 +0,0 @@ -serializer=au.com.origin.snapshots.serializers.v1.ToStringSnapshotSerializer -comparator=au.com.origin.snapshots.comparators.v1.PlainTextEqualsComparator -reporters=au.com.origin.snapshots.reporters.v1.PlainTextSnapshotReporter -snapshot-dir=__snapshots__ -output-dir=src/test/java -ci-env-var=CI -update-snapshot=none \ No newline at end of file diff --git a/java-snapshot-testing-junit5/build.gradle b/java-snapshot-testing-junit5/build.gradle deleted file mode 100644 index 0b09446..0000000 --- a/java-snapshot-testing-junit5/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -apply from: "../gradle/publishing.gradle" -apply from: "../gradle/spotless.gradle" - -ext { - junitVersion = '5.7.2' -} - -dependencies { - implementation project(':java-snapshot-testing-core') - - // User supplied Junit5 version - compileOnly "org.junit.jupiter:junit-jupiter-api:${project.junitVersion}" - compileOnly "org.junit.jupiter:junit-jupiter-engine:${project.junitVersion}" - - // Testing - testImplementation 'org.slf4j:slf4j-simple:2.0.0-alpha0' - testImplementation "org.junit.jupiter:junit-jupiter-params:${project.junitVersion}" - testImplementation "org.junit.jupiter:junit-jupiter-api:${project.junitVersion}" - testImplementation "org.junit.jupiter:junit-jupiter-engine:${project.junitVersion}" - testImplementation 'org.assertj:assertj-core:3.11.1' - - // Required java-snapshot-testing peer dependencies - testImplementation project(':java-snapshot-testing-plugin-jackson') - testImplementation 'com.fasterxml.jackson.core:jackson-core:2.11.3' - testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.11.3' -} - -test { useJUnitPlatform() } - -publishing { - publications { - myPublication(MavenPublication) { - artifact shadowJar - groupId 'io.github.origin-energy' - artifactId 'java-snapshot-testing-junit5' - } - } -} \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/main/java/au/com/origin/snapshots/junit5/SnapshotExtension.java b/java-snapshot-testing-junit5/src/main/java/au/com/origin/snapshots/junit5/SnapshotExtension.java deleted file mode 100644 index ec21f86..0000000 --- a/java-snapshot-testing-junit5/src/main/java/au/com/origin/snapshots/junit5/SnapshotExtension.java +++ /dev/null @@ -1,127 +0,0 @@ -package au.com.origin.snapshots.junit5; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.SnapshotVerifier; -import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfigInjector; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import au.com.origin.snapshots.logging.LoggingHelper; -import au.com.origin.snapshots.utils.ReflectionUtils; -import java.lang.reflect.Field; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.extension.AfterAllCallback; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.api.extension.ParameterResolutionException; -import org.junit.jupiter.api.extension.ParameterResolver; -import org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor; -import org.junit.jupiter.engine.descriptor.ClassTestDescriptor; - -@Slf4j -public class SnapshotExtension - implements AfterAllCallback, - BeforeAllCallback, - SnapshotConfigInjector, - ParameterResolver, - BeforeEachCallback { - - private SnapshotVerifier snapshotVerifier; - - @Override - public void beforeAll(ExtensionContext context) { - // don't fail if a test is run alone from the IDE for example - boolean failOnOrphans = shouldFailOnOrphans(context); - Class testClass = - context - .getTestClass() - .orElseThrow(() -> new SnapshotMatchException("Unable to locate Test class")); - this.snapshotVerifier = new SnapshotVerifier(getSnapshotConfig(), testClass, failOnOrphans); - } - - @Override - public void afterAll(ExtensionContext context) { - this.snapshotVerifier.validateSnapshots(); - } - - @Override - public SnapshotConfig getSnapshotConfig() { - return new PropertyResolvingSnapshotConfig(); - } - - /** - * FIXME This is a hack until I find the correct way to determine if a test run is individual or - * as part of a class - * - * @param context - * @return - */ - private boolean shouldFailOnOrphans(ExtensionContext context) { - try { - Field field = context.getClass().getSuperclass().getDeclaredField("testDescriptor"); - field.setAccessible(true); - Object testDescriptor = field.get(context); - if (testDescriptor instanceof ClassTestDescriptor) { // Junit 5.3.2 - ClassTestDescriptor classTestDescriptor = (ClassTestDescriptor) testDescriptor; - return classTestDescriptor.getChildren().size() > 1; - } else if (testDescriptor instanceof ClassBasedTestDescriptor) { // Junit 5.7.2 - ClassBasedTestDescriptor classTestDescriptor = (ClassBasedTestDescriptor) testDescriptor; - return classTestDescriptor.getChildren().size() > 1; - } - } catch (Exception e) { - log.error( - "FAILED: (Java Snapshot Testing) Unable to get JUnit5 ClassTestDescriptor or ClassBasedTestDescriptor!\n" - + "Ensure you are using Junit5 >= 5.3.2\n" - + "This may be due to JUnit5 changing their private api as we use reflection to access it\n" - + "Log a support ticket https://github.com/origin-energy/java-snapshot-testing/issues and supply your JUnit5 version\n" - + "Setting failOnOrphans=true as this is the safest option." - + "This means that running a test alone (say from the IDE) will fail the snapshot, you need to run the entire class.", - e); - } - return true; - } - - @Override - public boolean supportsParameter( - ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - boolean supports = parameterContext.getParameter().getType() == Expect.class; - if (supports) { - LoggingHelper.deprecatedV5( - log, - "Injecting 'Expect' via method a argument is no longer recommended. Consider using instance variable injection instead."); - } - return supports; - } - - @Override - public Object resolveParameter( - ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - return new Expect( - snapshotVerifier, - extensionContext - .getTestMethod() - .orElseThrow(() -> new RuntimeException("getTestMethod() is missing"))); - } - - @Override - public void beforeEach(ExtensionContext context) { - if (context.getTestInstance().isPresent() && context.getTestMethod().isPresent()) { - ReflectionUtils.findFieldByPredicate( - context.getTestClass().get(), (field) -> field.getType() == Expect.class) - .ifPresent( - (field) -> { - Expect expect = Expect.of(snapshotVerifier, context.getTestMethod().get()); - ReflectionUtils.makeAccessible(field); - try { - field.set(context.getTestInstance().get(), expect); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }); - } - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/BaseClassTest.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/BaseClassTest.java deleted file mode 100644 index 8b89737..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/BaseClassTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.junit5.SnapshotExtension; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith({SnapshotExtension.class}) -public class BaseClassTest { - - class TestBase { - Expect expect; - } - - @Nested - @ExtendWith(SnapshotExtension.class) - class NestedClass extends TestBase { - - @Test - public void helloWorldTest() { - expect.toMatchSnapshot("Hello World"); - } - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/NestedClassTest.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/NestedClassTest.java deleted file mode 100644 index 7267329..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/NestedClassTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package au.com.origin.snapshots; - -import static org.assertj.core.api.Assertions.assertThat; - -import au.com.origin.snapshots.junit5.SnapshotExtension; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith({SnapshotExtension.class}) -public class NestedClassTest { - - @AfterAll - public static void afterAll() { - Path path = - Paths.get("src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest.snap"); - assertThat(Files.exists(path)).isFalse(); - } - - @Nested - class NestedClassWithExpectArgument { - - @Test - public void helloWorldTest(Expect expect) { - expect.toMatchSnapshot("Hello World"); - } - } - - @Nested - class NestedClassWithoutSnapshot { - - @Test - public void helloWorldTest() { - assertThat(true).isTrue(); - } - } - - @Nested - class NestedClassWithExpectInstance { - - Expect expect; - - @Test - public void helloWorldTest() { - expect.toMatchSnapshot("Hello World"); - } - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/NestedClassTestWithExtends.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/NestedClassTestWithExtends.java deleted file mode 100644 index d4ac92c..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/NestedClassTestWithExtends.java +++ /dev/null @@ -1,35 +0,0 @@ -package au.com.origin.snapshots; - -import static org.assertj.core.api.Assertions.assertThat; - -import au.com.origin.snapshots.junit5.SnapshotExtension; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -public class NestedClassTestWithExtends { - - @AfterAll - public static void afterAll() { - Path path = - Paths.get( - "src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTestWithExtends.snap"); - assertThat(Files.exists(path)).isFalse(); - } - - @ExtendWith(SnapshotExtension.class) - @Nested - class NestedClass { - - Expect expect; - - @Test - public void helloWorldTest() { - expect.toMatchSnapshot("Hello World"); - } - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/SnapshotExtensionUsedTest.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/SnapshotExtensionUsedTest.java deleted file mode 100644 index e1c4044..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/SnapshotExtensionUsedTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.junit5.SnapshotExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith(SnapshotExtension.class) -public class SnapshotExtensionUsedTest { - - private Expect expect; - - @Test - public void shouldUseExtension(Expect expect) { - expect.toMatchSnapshot("Hello World"); - } - - @Test - public void shouldUseExtensionAgain(Expect expect) { - expect.toMatchSnapshot("Hello World"); - } - - @Test - public void shouldUseExtensionViaInstanceVariable() { - expect.toMatchSnapshot("Hello World"); - } - - @Test - public void shouldUseExtensionAgainViaInstanceVariable() { - expect.toMatchSnapshot("Hello World"); - } - - @SnapshotName("hello_world") - @Test - public void snapshotWithName() { - expect.toMatchSnapshot("Hello World"); - } - - @SnapshotName("hello_world_2") - @Test - public void snapshotWithNameAgain() { - expect.toMatchSnapshot("Hello World"); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/SnapshotParameterTest.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/SnapshotParameterTest.java deleted file mode 100644 index 0b282e7..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/SnapshotParameterTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package au.com.origin.snapshots; - -import au.com.origin.snapshots.jackson.serializers.v1.JacksonSnapshotSerializer; -import au.com.origin.snapshots.junit5.SnapshotExtension; -import java.util.stream.Stream; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -@ExtendWith({SnapshotExtension.class}) -class SnapshotParameterTest { - - private Expect expect; - - static Stream testData() { - - return Stream.of( - Arguments.of("Scenario2", "test input 1"), - Arguments.of("Scenario2", "test input 1"), - Arguments.of("Scenario2", "test input 1"), - Arguments.of("Scenario3", "test input 2"), - Arguments.of("Scenario3", "test input 2")); - } - - @ParameterizedTest - @MethodSource("au.com.origin.snapshots.SnapshotParameterTest#testData") - void shouldSupportParameterizedTest(String scenario, String testInput, Expect expect) { - expect.toMatchSnapshot("Duplicates are OK"); - expect.toMatchSnapshot("Duplicates are OK"); - expect.scenario("Scenario1").toMatchSnapshot("Additional snapshots need to include a scenario"); - expect - .serializer(JacksonSnapshotSerializer.class) - .scenario(scenario) - .toMatchSnapshot(testInput); - } - - @ParameterizedTest - @MethodSource("au.com.origin.snapshots.SnapshotParameterTest#testData") - void shouldSupportParameterizedTestViaInstanceVariable(String scenario, String testInput) { - this.expect.toMatchSnapshot("Duplicates are OK"); - this.expect.toMatchSnapshot("Duplicates are OK"); - this.expect - .scenario("Scenario1") - .toMatchSnapshot("Additional snapshots need to include a scenario"); - this.expect - .serializer(JacksonSnapshotSerializer.class) - .scenario(scenario) - .toMatchSnapshot(testInput); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/BaseClassTest$NestedClass.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/BaseClassTest$NestedClass.snap deleted file mode 100644 index a659739..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/BaseClassTest$NestedClass.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.BaseClassTest$NestedClass.helloWorldTest=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest$NestedClassWithExpectArgument.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest$NestedClassWithExpectArgument.snap deleted file mode 100644 index e70d0b5..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest$NestedClassWithExpectArgument.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.NestedClassTest$NestedClassWithExpectArgument.helloWorldTest=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest$NestedClassWithExpectInstance.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest$NestedClassWithExpectInstance.snap deleted file mode 100644 index 7fd7503..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest$NestedClassWithExpectInstance.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.NestedClassTest$NestedClassWithExpectInstance.helloWorldTest=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest$NestedClassWithoutSnapshot.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest$NestedClassWithoutSnapshot.snap deleted file mode 100644 index b8d8953..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTest$NestedClassWithoutSnapshot.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.NestedClassTest$NestedClassWithoutSnapshot.helloWorldTest=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTestWithExtends$NestedClass.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTestWithExtends$NestedClass.snap deleted file mode 100644 index b9bf45e..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/NestedClassTestWithExtends$NestedClass.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.NestedClassTestWithExtends$NestedClass.helloWorldTest=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotExtensionUsedTest.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotExtensionUsedTest.snap deleted file mode 100644 index 09fb8b1..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotExtensionUsedTest.snap +++ /dev/null @@ -1,28 +0,0 @@ -au.com.origin.snapshots.SnapshotExtensionUsedTest.shouldUseExtension=[ -Hello World -] - - -au.com.origin.snapshots.SnapshotExtensionUsedTest.shouldUseExtensionAgain=[ -Hello World -] - - -au.com.origin.snapshots.SnapshotExtensionUsedTest.shouldUseExtensionAgainViaInstanceVariable=[ -Hello World -] - - -au.com.origin.snapshots.SnapshotExtensionUsedTest.shouldUseExtensionViaInstanceVariable=[ -Hello World -] - - -hello_world=[ -Hello World -] - - -hello_world_2=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotParameterTest.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotParameterTest.snap deleted file mode 100644 index a632d11..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/__snapshots__/SnapshotParameterTest.snap +++ /dev/null @@ -1,38 +0,0 @@ -au.com.origin.snapshots.SnapshotParameterTest.shouldSupportParameterizedTest=[ -Duplicates are OK -] - - -au.com.origin.snapshots.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable=[ -Duplicates are OK -] - - -au.com.origin.snapshots.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable[Scenario1]=[ -Additional snapshots need to include a scenario -] - - -au.com.origin.snapshots.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable[Scenario2]=[ - "test input 1" -] - - -au.com.origin.snapshots.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable[Scenario3]=[ - "test input 2" -] - - -au.com.origin.snapshots.SnapshotParameterTest.shouldSupportParameterizedTest[Scenario1]=[ -Additional snapshots need to include a scenario -] - - -au.com.origin.snapshots.SnapshotParameterTest.shouldSupportParameterizedTest[Scenario2]=[ - "test input 1" -] - - -au.com.origin.snapshots.SnapshotParameterTest.shouldSupportParameterizedTest[Scenario3]=[ - "test input 2" -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/CustomFrameworkExample.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/CustomFrameworkExample.java deleted file mode 100644 index 841635c..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/CustomFrameworkExample.java +++ /dev/null @@ -1,32 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.SnapshotVerifier; -import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -// Notice we aren't using any framework extensions -public class CustomFrameworkExample { - - private static SnapshotVerifier snapshotVerifier; - - @BeforeAll - static void beforeAll() { - snapshotVerifier = - new SnapshotVerifier(new PropertyResolvingSnapshotConfig(), CustomFrameworkExample.class); - } - - @AfterAll - static void afterAll() { - snapshotVerifier.validateSnapshots(); - } - - @Test - void shouldMatchSnapshotOne(TestInfo testInfo) { - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot("Hello World"); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/CustomSnapshotConfigExample.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/CustomSnapshotConfigExample.java deleted file mode 100644 index 1f0fece..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/CustomSnapshotConfigExample.java +++ /dev/null @@ -1,18 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.annotations.UseSnapshotConfig; -import au.com.origin.snapshots.junit5.SnapshotExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith(SnapshotExtension.class) -// apply your custom snapshot configuration to this test class -@UseSnapshotConfig(LowercaseToStringSnapshotConfig.class) -public class CustomSnapshotConfigExample { - - @Test - public void myTest(Expect expect) { - expect.toMatchSnapshot("hello world"); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/JUnit5Example.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/JUnit5Example.java deleted file mode 100644 index 1fe4b8a..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/JUnit5Example.java +++ /dev/null @@ -1,26 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.junit5.SnapshotExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -// Ensure you extend your test class with the SnapshotExtension -@ExtendWith({SnapshotExtension.class}) -public class JUnit5Example { - - // Option 1: inject Expect as an instance variable - private Expect expect; - - @Test - public void myTest1() { - // Verify your snapshot - expect.toMatchSnapshot("Hello World"); - } - - // Option 2: inject Expect into the method signature - @Test - public void myTest2(Expect expect) { - expect.toMatchSnapshot("Hello World Again"); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/JUnit5ResolutionHierarchyExample.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/JUnit5ResolutionHierarchyExample.java deleted file mode 100644 index eae0ab2..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/JUnit5ResolutionHierarchyExample.java +++ /dev/null @@ -1,34 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.annotations.UseSnapshotConfig; -import au.com.origin.snapshots.junit5.SnapshotExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith(SnapshotExtension.class) -@UseSnapshotConfig(LowercaseToStringSnapshotConfig.class) -public class JUnit5ResolutionHierarchyExample { - - private Expect expect; - - @Test - public void aliasMethodTest() { - expect - .serializer("json") // <------ Using snapshot.properties - .toMatchSnapshot(new TestObject()); - } - - @Test - public void customSerializerTest() { - expect - .serializer(UppercaseToStringSerializer.class) // <------ Using custom serializer - .toMatchSnapshot(new TestObject()); - } - - // Read from LowercaseToStringSnapshotConfig defined on the class - @Test - public void lowercaseTest() { - expect.toMatchSnapshot(new TestObject()); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/LowercaseToStringSerializer.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/LowercaseToStringSerializer.java deleted file mode 100644 index d333eca..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/LowercaseToStringSerializer.java +++ /dev/null @@ -1,18 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotSerializerContext; -import au.com.origin.snapshots.serializers.SerializerType; -import au.com.origin.snapshots.serializers.SnapshotSerializer; - -public class LowercaseToStringSerializer implements SnapshotSerializer { - @Override - public Snapshot apply(Object object, SnapshotSerializerContext gen) { - return gen.toSnapshot(object.toString().toLowerCase()); - } - - @Override - public String getOutputFormat() { - return SerializerType.TEXT.name(); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/LowercaseToStringSnapshotConfig.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/LowercaseToStringSnapshotConfig.java deleted file mode 100644 index aac8eb5..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/LowercaseToStringSnapshotConfig.java +++ /dev/null @@ -1,12 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig; -import au.com.origin.snapshots.serializers.SnapshotSerializer; - -public class LowercaseToStringSnapshotConfig extends PropertyResolvingSnapshotConfig { - - @Override - public SnapshotSerializer getSerializer() { - return new LowercaseToStringSerializer(); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/MyFirstSnapshotTest.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/MyFirstSnapshotTest.java deleted file mode 100644 index 0c7d617..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/MyFirstSnapshotTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.annotations.SnapshotName; -import au.com.origin.snapshots.junit5.SnapshotExtension; -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith({SnapshotExtension.class}) -public class MyFirstSnapshotTest { - - private Expect expect; - - @SnapshotName("i_can_give_custom_names_to_my_snapshots") - @Test - public void toStringSerializationTest() { - expect.toMatchSnapshot("Hello World"); - } - - @Test - public void jsonSerializationTest() { - Map map = new HashMap<>(); - map.put("name", "John Doe"); - map.put("age", 40); - - expect.serializer("json").toMatchSnapshot(map); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/TestObject.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/TestObject.java deleted file mode 100644 index bec56a7..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/TestObject.java +++ /dev/null @@ -1,8 +0,0 @@ -package au.com.origin.snapshots.docs; - -public class TestObject { - @Override - public String toString() { - return "This is a snapshot of the toString() method"; - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/UppercaseToStringSerializer.java b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/UppercaseToStringSerializer.java deleted file mode 100644 index 9760d3a..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/UppercaseToStringSerializer.java +++ /dev/null @@ -1,18 +0,0 @@ -package au.com.origin.snapshots.docs; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotSerializerContext; -import au.com.origin.snapshots.serializers.SerializerType; -import au.com.origin.snapshots.serializers.SnapshotSerializer; - -public class UppercaseToStringSerializer implements SnapshotSerializer { - @Override - public Snapshot apply(Object object, SnapshotSerializerContext gen) { - return gen.toSnapshot(object.toString().toUpperCase()); - } - - @Override - public String getOutputFormat() { - return SerializerType.TEXT.name(); - } -} diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/CustomFrameworkExample.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/CustomFrameworkExample.snap deleted file mode 100644 index 2438c51..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/CustomFrameworkExample.snap +++ /dev/null @@ -1,3 +0,0 @@ -au.com.origin.snapshots.docs.CustomFrameworkExample.shouldMatchSnapshotOne=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/CustomSnapshotConfigExample.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/CustomSnapshotConfigExample.snap deleted file mode 100644 index ed598d1..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/CustomSnapshotConfigExample.snap +++ /dev/null @@ -1 +0,0 @@ -au.com.origin.snapshots.docs.CustomSnapshotConfigExample.myTest=hello world \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/CustomSnapshotContextConfigExample.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/CustomSnapshotContextConfigExample.snap deleted file mode 100644 index ed598d1..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/CustomSnapshotContextConfigExample.snap +++ /dev/null @@ -1 +0,0 @@ -au.com.origin.snapshots.docs.CustomSnapshotConfigExample.myTest=hello world \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit5Example.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit5Example.snap deleted file mode 100644 index 7273361..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit5Example.snap +++ /dev/null @@ -1,8 +0,0 @@ -au.com.origin.snapshots.docs.JUnit5Example.myTest1=[ -Hello World -] - - -au.com.origin.snapshots.docs.JUnit5Example.myTest2=[ -Hello World Again -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit5ResolutionHierarchyExample.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit5ResolutionHierarchyExample.snap deleted file mode 100644 index b48f7c3..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/JUnit5ResolutionHierarchyExample.snap +++ /dev/null @@ -1,9 +0,0 @@ -au.com.origin.snapshots.docs.JUnit5ResolutionHierarchyExample.aliasMethodTest=[ - { } -] - - -au.com.origin.snapshots.docs.JUnit5ResolutionHierarchyExample.customSerializerTest=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD - - -au.com.origin.snapshots.docs.JUnit5ResolutionHierarchyExample.lowercaseTest=this is a snapshot of the tostring() method \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/MyFirstSnapshotContextTest.snap b/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/MyFirstSnapshotContextTest.snap deleted file mode 100644 index 1987c84..0000000 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/MyFirstSnapshotContextTest.snap +++ /dev/null @@ -1,11 +0,0 @@ -au.com.origin.snapshots.docs.MyFirstSnapshotTest.jsonSerializationTest=[ - { - "age": 40, - "name": "John Doe" - } -] - - -i_can_give_custom_names_to_my_snapshots=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/resources/snapshot.properties b/java-snapshot-testing-junit5/src/test/resources/snapshot.properties deleted file mode 100644 index ca78343..0000000 --- a/java-snapshot-testing-junit5/src/test/resources/snapshot.properties +++ /dev/null @@ -1,8 +0,0 @@ -serializer=au.com.origin.snapshots.serializers.v1.ToStringSnapshotSerializer -serializer.json=au.com.origin.snapshots.jackson.serializers.v1.DeterministicJacksonSnapshotSerializer -comparator=au.com.origin.snapshots.comparators.v1.PlainTextEqualsComparator -reporters=au.com.origin.snapshots.reporters.v1.PlainTextSnapshotReporter -snapshot-dir=__snapshots__ -output-dir=src/test/java -ci-env-var=CI -update-snapshot=none \ No newline at end of file diff --git a/java-snapshot-testing-plugin-jackson/build.gradle b/java-snapshot-testing-plugin-jackson/build.gradle deleted file mode 100644 index c2660d3..0000000 --- a/java-snapshot-testing-plugin-jackson/build.gradle +++ /dev/null @@ -1,40 +0,0 @@ -apply from: "../gradle/publishing.gradle" -apply from: "../gradle/spotless.gradle" - -dependencies { - compileOnly project(':java-snapshot-testing-core') - - // Client needs to supply their own versions - compileOnly 'com.fasterxml.jackson.core:jackson-core:2.11.3' - compileOnly 'com.fasterxml.jackson.core:jackson-databind:2.11.3' - - // User supplied Junit5 version - compileOnly 'org.junit.jupiter:junit-jupiter-api:5.3.2' - compileOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2' - - // Testing - testImplementation project(':java-snapshot-testing-core') - testImplementation 'org.slf4j:slf4j-simple:2.0.0-alpha0' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.3.2' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.3.2' - testImplementation 'org.assertj:assertj-core:3.11.1' - testImplementation 'org.skyscreamer:jsonassert:1.5.0' // For docs/ reporter example - - testImplementation 'com.fasterxml.jackson.core:jackson-core:2.16.0' - testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.16.0' - testRuntimeOnly 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.16.0' - testRuntimeOnly 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.0' -} - -test { useJUnitPlatform() } - -publishing { - publications { - myPublication(MavenPublication) { - artifact shadowJar - groupId 'io.github.origin-energy' - artifactId 'java-snapshot-testing-plugin-jackson' - } - } -} \ No newline at end of file diff --git a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/DeterministicCollectionModule.java b/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/DeterministicCollectionModule.java deleted file mode 100644 index f4f966c..0000000 --- a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/DeterministicCollectionModule.java +++ /dev/null @@ -1,58 +0,0 @@ -package au.com.origin.snapshots.jackson.serializers; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.module.SimpleModule; -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.Objects; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; - -/** - * Inspired by: - * https://www.stubbornjava.com/posts/creating-a-somewhat-deterministic-jackson-objectmapper - */ -@Slf4j -public class DeterministicCollectionModule extends SimpleModule { - - public DeterministicCollectionModule() { - addSerializer(Collection.class, new CollectionSerializer()); - } - - /** - * Collections gets converted into a sorted Object[]. This then gets serialized using the default - * Array serializer. - */ - private static class CollectionSerializer extends JsonSerializer { - - @Override - public void serialize(Collection value, JsonGenerator gen, SerializerProvider serializers) - throws IOException { - Object[] sorted = convert(value); - serializers.defaultSerializeValue(sorted, gen); - } - - private Object[] convert(Collection value) { - if (value == null || value.isEmpty()) { - return Collections.emptyList().toArray(); - } - - try { - return value.stream() - .filter(Objects::nonNull) - .sorted() - .collect(Collectors.toList()) - .toArray(); - } catch (ClassCastException ex) { - log.warn( - "Unable to sort() collection - this may result in a non deterministic snapshot.\n" - + "Consider adding a custom serializer for this type via the JacksonSnapshotSerializer#configure() method.\n" - + ex.getMessage()); - return value.toArray(); - } - } - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/DeterministicJacksonSnapshotSerializer.java b/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/DeterministicJacksonSnapshotSerializer.java deleted file mode 100644 index d4feea2..0000000 --- a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/DeterministicJacksonSnapshotSerializer.java +++ /dev/null @@ -1,24 +0,0 @@ -package au.com.origin.snapshots.jackson.serializers; - -import au.com.origin.snapshots.logging.LoggingHelper; -import lombok.extern.slf4j.Slf4j; - -/** - * Attempts to deterministically render a snapshot. - * - *

This can help in situations where collections are rendering in a different order on subsequent - * runs. - * - *

Note that collections will be ordered which mar or may not be desirable given your use case. - */ -@Deprecated -@Slf4j -public class DeterministicJacksonSnapshotSerializer - extends au.com.origin.snapshots.jackson.serializers.v1.DeterministicJacksonSnapshotSerializer { - public DeterministicJacksonSnapshotSerializer() { - super(); - LoggingHelper.deprecatedV5( - log, - "Update to `au.com.origin.snapshots.jackson.serializers.v1.DeterministicJacksonSnapshotSerializer` in `snapshot.properties`"); - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/JacksonSnapshotSerializer.java b/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/JacksonSnapshotSerializer.java deleted file mode 100644 index 74f1fed..0000000 --- a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/JacksonSnapshotSerializer.java +++ /dev/null @@ -1,17 +0,0 @@ -package au.com.origin.snapshots.jackson.serializers; - -import au.com.origin.snapshots.logging.LoggingHelper; -import lombok.extern.slf4j.Slf4j; - -@Deprecated -@Slf4j -public class JacksonSnapshotSerializer - extends au.com.origin.snapshots.jackson.serializers.v1.JacksonSnapshotSerializer { - - public JacksonSnapshotSerializer() { - super(); - LoggingHelper.deprecatedV5( - log, - "Update to `au.com.origin.snapshots.jackson.serializers.v1.JacksonSnapshotSerializer` in `snapshot.properties`"); - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/v1/JacksonSnapshotSerializer.java b/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/v1/JacksonSnapshotSerializer.java deleted file mode 100644 index 4897482..0000000 --- a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/v1/JacksonSnapshotSerializer.java +++ /dev/null @@ -1,73 +0,0 @@ -package au.com.origin.snapshots.jackson.serializers.v1; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.SnapshotSerializerContext; -import au.com.origin.snapshots.exceptions.SnapshotExtensionException; -import au.com.origin.snapshots.serializers.SerializerType; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.PrettyPrinter; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import java.util.Arrays; -import java.util.List; - -public class JacksonSnapshotSerializer implements SnapshotSerializer { - - private final PrettyPrinter pp = new SnapshotPrettyPrinter(); - private final ObjectMapper objectMapper = - new ObjectMapper() { - { - this.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); - this.enable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID); - this.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - this.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - this.setSerializationInclusion(JsonInclude.Include.NON_NULL); - - if (shouldFindAndRegisterModules()) { - this.findAndRegisterModules(); - } - - this.setVisibility( - this.getSerializationConfig() - .getDefaultVisibilityChecker() - .withFieldVisibility(JsonAutoDetect.Visibility.ANY) - .withGetterVisibility(JsonAutoDetect.Visibility.NONE) - .withSetterVisibility(JsonAutoDetect.Visibility.NONE) - .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)); - JacksonSnapshotSerializer.this.configure(this); - } - }; - - /** - * Override to customize the Jackson objectMapper - * - * @param objectMapper existing ObjectMapper - */ - public void configure(ObjectMapper objectMapper) {} - - /** - * Override to control the registration of all available jackson modules within the classpath - * which are locatable via JDK ServiceLoader facility, along with module-provided SPI. - */ - protected boolean shouldFindAndRegisterModules() { - return true; - } - - @Override - public Snapshot apply(Object object, SnapshotSerializerContext gen) { - try { - List objects = Arrays.asList(object); - String body = objectMapper.writer(pp).writeValueAsString(objects); - return gen.toSnapshot(body); - } catch (Exception e) { - throw new SnapshotExtensionException("Jackson Serialization failed", e); - } - } - - @Override - public String getOutputFormat() { - return SerializerType.JSON.name(); - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/v1/SnapshotPrettyPrinter.java b/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/v1/SnapshotPrettyPrinter.java deleted file mode 100644 index 479ea9c..0000000 --- a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/v1/SnapshotPrettyPrinter.java +++ /dev/null @@ -1,23 +0,0 @@ -package au.com.origin.snapshots.jackson.serializers.v1; - -import com.fasterxml.jackson.core.util.DefaultIndenter; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; - -class SnapshotPrettyPrinter extends DefaultPrettyPrinter { - - public SnapshotPrettyPrinter() { - super(""); - Indenter lfOnlyIndenter = new DefaultIndenter(" ", "\n"); - this.indentArraysWith(lfOnlyIndenter); - this.indentObjectsWith(lfOnlyIndenter); - - this._objectFieldValueSeparatorWithSpaces = - this._separators.getObjectFieldValueSeparator() + " "; - } - - // It's a requirement - // @see https://github.com/FasterXML/jackson-databind/issues/2203 - public DefaultPrettyPrinter createInstance() { - return new DefaultPrettyPrinter(this); - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/NoNameChangeTest.java b/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/NoNameChangeTest.java deleted file mode 100644 index 61764b7..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/NoNameChangeTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package au.com.origin.snapshots.jackson; - -import static org.assertj.core.api.Assertions.assertThat; - -import au.com.origin.snapshots.jackson.serializers.DeterministicJacksonSnapshotSerializer; -import au.com.origin.snapshots.jackson.serializers.JacksonSnapshotSerializer; -import org.junit.jupiter.api.Test; - -/** - * These classes are likely defined in snapshot.properties as a string. - * - *

The clients IDE will not complain if they change so ensure they don't - */ -public class NoNameChangeTest { - - @Test - public void serializersApiShouldNotChange() { - assertThat(JacksonSnapshotSerializer.class.getName()) - .isEqualTo("au.com.origin.snapshots.jackson.serializers.JacksonSnapshotSerializer"); - assertThat(DeterministicJacksonSnapshotSerializer.class.getName()) - .isEqualTo( - "au.com.origin.snapshots.jackson.serializers.DeterministicJacksonSnapshotSerializer"); - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/ReflectionUtilities.java b/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/ReflectionUtilities.java deleted file mode 100644 index 161a4a4..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/ReflectionUtilities.java +++ /dev/null @@ -1,30 +0,0 @@ -package au.com.origin.snapshots.jackson; - -import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import java.lang.reflect.Method; -import java.util.Optional; -import java.util.stream.Stream; - -public class ReflectionUtilities { - - // FIXME consider guava reflection instead - public static Method getMethod(Class clazz, String methodName) { - try { - return Stream.of(clazz.getDeclaredMethods()) - .filter(method -> method.getName().equals(methodName)) - .findFirst() - .orElseThrow(() -> new NoSuchMethodException("Not Found")); - } catch (NoSuchMethodException e) { - return Optional.ofNullable(clazz.getSuperclass()) - .map(superclass -> getMethod(superclass, methodName)) - .orElseThrow( - () -> - new SnapshotMatchException( - "Could not find method " - + methodName - + " on class " - + clazz - + "\nPlease annotate your test method with @Test and make it without any parameters!")); - } - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/CustomSerializerTest.java b/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/CustomSerializerTest.java deleted file mode 100644 index 7a7b83f..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/CustomSerializerTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package au.com.origin.snapshots.jackson.docs; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.SnapshotVerifier; -import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig; -import java.time.Instant; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public class CustomSerializerTest { - - @Test - public void test1(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier( - new PropertyResolvingSnapshotConfig(), testInfo.getTestClass().get(), false); - - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect - .serializer(HibernateSnapshotSerializer.class) - .toMatchSnapshot(new BaseEntity(1L, Instant.now(), Instant.now(), "This should render")); - - snapshotVerifier.validateSnapshots(); - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/HibernateSnapshotSerializer.java b/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/HibernateSnapshotSerializer.java deleted file mode 100644 index 455b5b9..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/HibernateSnapshotSerializer.java +++ /dev/null @@ -1,38 +0,0 @@ -package au.com.origin.snapshots.jackson.docs; - -import au.com.origin.snapshots.jackson.serializers.DeterministicJacksonSnapshotSerializer; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreType; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.time.Instant; -import java.util.List; -import java.util.Set; - -public class HibernateSnapshotSerializer extends DeterministicJacksonSnapshotSerializer { - - @Override - public void configure(ObjectMapper objectMapper) { - super.configure(objectMapper); - - // Ignore Hibernate Lists to prevent infinite recursion - objectMapper.addMixIn(List.class, IgnoreTypeMixin.class); - objectMapper.addMixIn(Set.class, IgnoreTypeMixin.class); - - // Ignore Fields that Hibernate generates for us automatically - objectMapper.addMixIn(BaseEntity.class, IgnoreHibernateEntityFields.class); - } - - @JsonIgnoreType - class IgnoreTypeMixin {} - - abstract class IgnoreHibernateEntityFields { - @JsonIgnore - abstract Long getId(); - - @JsonIgnore - abstract Instant getCreatedDate(); - - @JsonIgnore - abstract Instant getLastModifiedDate(); - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/JsonAssertReporter.java b/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/JsonAssertReporter.java deleted file mode 100644 index 9d89ac5..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/JsonAssertReporter.java +++ /dev/null @@ -1,21 +0,0 @@ -package au.com.origin.snapshots.jackson.docs; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.reporters.SnapshotReporter; -import au.com.origin.snapshots.serializers.SerializerType; -import lombok.SneakyThrows; -import org.skyscreamer.jsonassert.JSONAssert; -import org.skyscreamer.jsonassert.JSONCompareMode; - -public class JsonAssertReporter implements SnapshotReporter { - @Override - public boolean supportsFormat(String outputFormat) { - return SerializerType.JSON.name().equalsIgnoreCase(outputFormat); - } - - @Override - @SneakyThrows - public void report(Snapshot previous, Snapshot current) { - JSONAssert.assertEquals(previous.getBody(), current.getBody(), JSONCompareMode.STRICT); - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/JsonObjectComparator.java b/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/JsonObjectComparator.java deleted file mode 100644 index 90a094b..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/JsonObjectComparator.java +++ /dev/null @@ -1,19 +0,0 @@ -package au.com.origin.snapshots.jackson.docs; - -import au.com.origin.snapshots.Snapshot; -import au.com.origin.snapshots.comparators.SnapshotComparator; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.SneakyThrows; - -public class JsonObjectComparator implements SnapshotComparator { - @Override - public boolean matches(Snapshot previous, Snapshot current) { - return asObject(previous.getName(), previous.getBody()) - .equals(asObject(current.getName(), current.getBody())); - } - - @SneakyThrows - private static Object asObject(String snapshotName, String json) { - return new ObjectMapper().readValue(json.replaceFirst(snapshotName + "=", ""), Object.class); - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/__snapshots__/CustomSerializerTest.snap b/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/__snapshots__/CustomSerializerTest.snap deleted file mode 100644 index 22d9cec..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/__snapshots__/CustomSerializerTest.snap +++ /dev/null @@ -1,5 +0,0 @@ -au.com.origin.snapshots.jackson.docs.CustomSerializerTest.test1=[ - { - "somethingElse": "This should render" - } -] \ No newline at end of file diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/DeterministicJacksonSnapshotSerializerTest.java b/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/DeterministicJacksonSnapshotSerializerTest.java deleted file mode 100644 index e6728fb..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/DeterministicJacksonSnapshotSerializerTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package au.com.origin.snapshots.jackson.serializers; - -import au.com.origin.snapshots.Expect; -import au.com.origin.snapshots.SnapshotVerifier; -import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig; -import au.com.origin.snapshots.serializers.SerializerType; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZonedDateTime; -import java.util.*; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public class DeterministicJacksonSnapshotSerializerTest { - - @Test - public void shouldSerializeDifferentTypes(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier( - new PropertyResolvingSnapshotConfig(), testInfo.getTestClass().get(), false); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.serializer("orderedJson").toMatchSnapshot(new TypeDummy()); - snapshotVerifier.validateSnapshots(); - } - - @Test - void shouldSupportJsonFormat() { - Assertions.assertThat(new DeterministicJacksonSnapshotSerializer().getOutputFormat()) - .isEqualTo(SerializerType.JSON.name()); - } - - private Map nonDeterministicMap(Map target) { - final List items = - new ArrayList() { - { - add("f"); - add("a"); - add("d"); - add("e"); - add("g"); - add("b"); - add("c"); - } - }; - - int size = items.size(); - for (int i = 0; i < size; i++) { - String random = pluckRandom(items); - target.put(random, (int) random.charAt(0)); - } - return target; - } - - private Collection nonDeterministicCollection(Collection target) { - final List items = - new ArrayList() { - { - add("f"); - add("a"); - add("d"); - add("e"); - add("g"); - add("b"); - add("c"); - } - }; - - int size = items.size(); - for (int i = 0; i < size; i++) { - target.add(pluckRandom(items)); - } - - return target; - } - - private String pluckRandom(List array) { - int rnd = new Random().nextInt(array.size()); - return array.remove(rnd); - } - - private enum AnEnum { - F, - A, - D, - E, - G, - B, - C; - } - - private final class TypeDummy { - private final Void aNull = null; - private final Object anObject = new Object(); - private final byte aByte = "A".getBytes()[0]; - private final short aShort = 32767; - private final int anInt = 2147483647; - private final long aLong = 9223372036854775807L; - private final float aFloat = 0.1234567F; - private final double aDouble = 1.123456789123456D; - private final boolean aBoolean = true; - private final char aChar = 'A'; - private final String string = "Hello World"; - private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); - private final LocalDate localDate = LocalDate.parse("2020-10-19"); - private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); - private final ZonedDateTime zonedDateTime = - ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); - private final AnEnum anEnum = AnEnum.A; - private final Optional presentOptional = Optional.of("Hello World"); - private final Optional emptyOptional = Optional.empty(); - private final String[] stringArray = {"f", "a", "d", "e", "g", "b", "c"}; - private final Object[] anEnumArray = Arrays.stream(AnEnum.values()).toArray(); - - // Maps - private final Map hashMap = nonDeterministicMap(new HashMap<>()); - private final Map treeMap = nonDeterministicMap(new TreeMap<>()); - private final Map linkedHashMap = nonDeterministicMap(new LinkedHashMap<>()); - - // Sets - private final Collection linkedHashSet = - nonDeterministicCollection(new LinkedHashSet<>()); - private final Collection hashSet = nonDeterministicCollection(new HashSet<>()); - private final Collection treeSet = nonDeterministicCollection(new TreeSet<>()); - - // Lists - private final Collection arrayList = nonDeterministicCollection(new ArrayList<>()); - private final Collection linkedList = nonDeterministicCollection(new LinkedList<>()); - - // Mixed Maps, Sets, Lists - private final Collection listOfCollections = - new ArrayList() { - { - add(nonDeterministicMap(new LinkedHashMap<>())); - add(nonDeterministicCollection(new LinkedHashSet<>())); - add(nonDeterministicCollection(new LinkedList<>())); - } - }; - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/JacksonSnapshotSerializerTest.java b/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/JacksonSnapshotSerializerTest.java deleted file mode 100644 index d54550b..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/JacksonSnapshotSerializerTest.java +++ /dev/null @@ -1,155 +0,0 @@ -package au.com.origin.snapshots.jackson.serializers; - -import au.com.origin.snapshots.*; -import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig; -import au.com.origin.snapshots.config.SnapshotConfig; -import au.com.origin.snapshots.serializers.SerializerType; -import au.com.origin.snapshots.serializers.SnapshotSerializer; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZonedDateTime; -import java.util.*; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; - -public class JacksonSnapshotSerializerTest { - - private static final SnapshotConfig DEFAULT_CONFIG = - new PropertyResolvingSnapshotConfig() { - @Override - public SnapshotSerializer getSerializer() { - return new JacksonSnapshotSerializer(); - } - }; - - private SnapshotSerializerContext gen = - new SnapshotSerializerContext( - "test", null, new SnapshotHeader(), JacksonSnapshotSerializerTest.class, null); - - @Test - public void shouldSerializeMap() { - Map map = new HashMap<>(); - map.put("name", "John Doe"); - map.put("age", 40); - - SnapshotSerializer serializer = new JacksonSnapshotSerializer(); - Snapshot result = serializer.apply(map, gen); - Assertions.assertThat(result.getBody()) - .isEqualTo( - "[\n" - + " {\n" - + " \"age\": 40,\n" - + " \"name\": \"John Doe\"\n" - + " }\n" - + "]"); - } - - @Test - public void shouldSerializeDifferentTypes(TestInfo testInfo) { - SnapshotVerifier snapshotVerifier = - new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); - Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); - expect.toMatchSnapshot(new TypeDummy()); - snapshotVerifier.validateSnapshots(); - } - - @Test - void shouldSupportJsonFormat() { - Assertions.assertThat(new JacksonSnapshotSerializer().getOutputFormat()) - .isEqualTo(SerializerType.JSON.name()); - } - - private Map deterministicMap(Map target) { - final List items = - new ArrayList() { - { - add("f"); - add("a"); - add("d"); - add("e"); - add("g"); - add("b"); - add("c"); - } - }; - items.forEach(it -> target.put(it, (int) it.charAt(0))); - return target; - } - - private Collection deterministicCollection(Collection target) { - final List items = - new ArrayList() { - { - add("f"); - add("a"); - add("d"); - add("e"); - add("g"); - add("b"); - add("c"); - } - }; - target.addAll(items); - return target; - } - - private enum AnEnum { - F, - A, - D, - E, - G, - B, - C; - } - - private final class TypeDummy { - private final Void aNull = null; - private final Object anObject = new Object(); - private final byte aByte = "A".getBytes()[0]; - private final short aShort = 32767; - private final int anInt = 2147483647; - private final long aLong = 9223372036854775807L; - private final float aFloat = 0.1234567F; - private final double aDouble = 1.123456789123456D; - private final boolean aBoolean = true; - private final char aChar = 'A'; - private final String string = "Hello World"; - private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); - private final LocalDate localDate = LocalDate.parse("2020-10-19"); - private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); - private final ZonedDateTime zonedDateTime = - ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); - private final AnEnum anEnum = AnEnum.A; - private final Optional presentOptional = Optional.of("Hello World"); - private final Optional emptyOptional = Optional.empty(); - private final String[] stringArray = {"f", "a", "d", "e", "g", "b", "c"}; - private final Object[] anEnumArray = Arrays.stream(AnEnum.values()).toArray(); - - // Maps - private final Map hashMap = deterministicMap(new HashMap<>()); - private final Map treeMap = deterministicMap(new TreeMap<>()); - private final Map linkedHashMap = deterministicMap(new LinkedHashMap<>()); - - // Sets - private final Collection linkedHashSet = deterministicCollection(new LinkedHashSet<>()); - private final Collection hashSet = deterministicCollection(new HashSet<>()); - private final Collection treeSet = deterministicCollection(new TreeSet<>()); - - // Lists - private final Collection arrayList = deterministicCollection(new ArrayList<>()); - private final Collection linkedList = deterministicCollection(new LinkedList<>()); - - // Mixed Maps, Sets, Lists - private final Collection listOfCollections = - new ArrayList() { - { - add(deterministicMap(new LinkedHashMap<>())); - add(deterministicCollection(new LinkedHashSet<>())); - add(deterministicCollection(new LinkedList<>())); - } - }; - } -} diff --git a/java-snapshot-testing-plugin-jackson/src/test/resources/snapshot.properties b/java-snapshot-testing-plugin-jackson/src/test/resources/snapshot.properties deleted file mode 100644 index 141beb8..0000000 --- a/java-snapshot-testing-plugin-jackson/src/test/resources/snapshot.properties +++ /dev/null @@ -1,8 +0,0 @@ -serializer=au.com.origin.snapshots.jackson.serializers.JacksonSnapshotSerializer -serializer.orderedJson=au.com.origin.snapshots.jackson.serializers.DeterministicJacksonSnapshotSerializer -comparator=au.com.origin.snapshots.comparators.PlainTextEqualsComparator -reporters=au.com.origin.snapshots.reporters.PlainTextSnapshotReporter -snapshot-dir=__snapshots__ -output-dir=src/test/java -ci-env-var=CI -update-snapshot=none \ No newline at end of file diff --git a/java-snapshot-testing-spock/build.gradle b/java-snapshot-testing-spock/build.gradle deleted file mode 100644 index 9f05cbf..0000000 --- a/java-snapshot-testing-spock/build.gradle +++ /dev/null @@ -1,34 +0,0 @@ -plugins { - id 'groovy' -} - -apply from: "../gradle/publishing.gradle" -apply from: "../gradle/spotless-groovy.gradle" - -dependencies { - implementation project(':java-snapshot-testing-core') - - // User supplied Spock Version - compileOnly 'org.codehaus.groovy:groovy-all:2.5.8' - compileOnly 'org.spockframework:spock-core:1.3-groovy-2.5' - - // Testing - testImplementation 'org.slf4j:slf4j-simple:2.0.0-alpha0' - testImplementation 'org.codehaus.groovy:groovy-all:2.5.8' - testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5' - testImplementation 'org.assertj:assertj-core:3.11.1' - - // Required java-snapshot-testing peer dependencies - testImplementation 'com.fasterxml.jackson.core:jackson-core:2.11.3' - testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.11.3' -} - -publishing { - publications { - myPublication(MavenPublication) { - artifact shadowJar - groupId 'io.github.origin-energy' - artifactId 'java-snapshot-testing-spock' - } - } -} \ No newline at end of file diff --git a/java-snapshot-testing-spock/src/main/groovy/au/com/origin/snapshots/spock/EnableSnapshots.groovy b/java-snapshot-testing-spock/src/main/groovy/au/com/origin/snapshots/spock/EnableSnapshots.groovy deleted file mode 100644 index 9379c8c..0000000 --- a/java-snapshot-testing-spock/src/main/groovy/au/com/origin/snapshots/spock/EnableSnapshots.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package au.com.origin.snapshots.spock - -import org.spockframework.runtime.extension.ExtensionAnnotation - -import java.lang.annotation.ElementType -import java.lang.annotation.Retention -import java.lang.annotation.RetentionPolicy -import java.lang.annotation.Target - -@Retention(RetentionPolicy.RUNTIME) -@Target([ElementType.TYPE, ElementType.METHOD]) -@ExtensionAnnotation(SnapshotExtension) -@interface EnableSnapshots {} diff --git a/java-snapshot-testing-spock/src/main/groovy/au/com/origin/snapshots/spock/SnapshotExtension.groovy b/java-snapshot-testing-spock/src/main/groovy/au/com/origin/snapshots/spock/SnapshotExtension.groovy deleted file mode 100644 index 1ec0ae4..0000000 --- a/java-snapshot-testing-spock/src/main/groovy/au/com/origin/snapshots/spock/SnapshotExtension.groovy +++ /dev/null @@ -1,28 +0,0 @@ -package au.com.origin.snapshots.spock - -import au.com.origin.snapshots.SnapshotVerifier -import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig -import au.com.origin.snapshots.config.SnapshotConfig -import au.com.origin.snapshots.config.SnapshotConfigInjector -import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension -import org.spockframework.runtime.model.SpecInfo - -class SnapshotExtension extends AbstractAnnotationDrivenExtension implements SnapshotConfigInjector { - - SnapshotVerifier snapshotVerifier; - - void visitSpecAnnotation(EnableSnapshots annotation, SpecInfo spec) { - this.snapshotVerifier = new SnapshotVerifier(getSnapshotConfig(), spec.reflection, false) - } - - void visitSpec(SpecInfo spec) { - def snapshotMethodInterceptor = new SnapshotMethodInterceptor(snapshotVerifier) - spec.allFeatures.featureMethod*.addInterceptor(snapshotMethodInterceptor) - spec.addCleanupSpecInterceptor(snapshotMethodInterceptor) - } - - @Override - SnapshotConfig getSnapshotConfig() { - return new PropertyResolvingSnapshotConfig() - } -} \ No newline at end of file diff --git a/java-snapshot-testing-spock/src/main/groovy/au/com/origin/snapshots/spock/SnapshotMethodInterceptor.groovy b/java-snapshot-testing-spock/src/main/groovy/au/com/origin/snapshots/spock/SnapshotMethodInterceptor.groovy deleted file mode 100644 index ac94958..0000000 --- a/java-snapshot-testing-spock/src/main/groovy/au/com/origin/snapshots/spock/SnapshotMethodInterceptor.groovy +++ /dev/null @@ -1,58 +0,0 @@ -package au.com.origin.snapshots.spock - -import au.com.origin.snapshots.Expect -import au.com.origin.snapshots.utils.ReflectionUtils -import au.com.origin.snapshots.SnapshotVerifier -import au.com.origin.snapshots.logging.LoggingHelper -import org.slf4j.LoggerFactory -import org.spockframework.runtime.extension.AbstractMethodInterceptor -import org.spockframework.runtime.extension.IMethodInvocation - -import java.lang.reflect.Method - -// Based on this issue: https://github.com/spockframework/spock/issues/652 -class SnapshotMethodInterceptor extends AbstractMethodInterceptor { - private log = LoggerFactory.getLogger( SnapshotMethodInterceptor.class ) - private final SnapshotVerifier snapshotVerifier; - - SnapshotMethodInterceptor(SnapshotVerifier snapshotVerifier) { - this.snapshotVerifier = snapshotVerifier - } - - @Override - void interceptFeatureMethod(IMethodInvocation invocation) throws Throwable { - updateInstanceVariable(invocation.instance, invocation.feature.featureMethod.reflection) - - def parameterCount = invocation.method.reflection.parameterCount - if (parameterCount > invocation.arguments.length) { - def newArguments = new Object[parameterCount] - System.arraycopy invocation.arguments, 0, newArguments, 0, invocation.arguments.length - invocation.arguments = newArguments - } - invocation.method.reflection.parameterTypes.eachWithIndex { type, i -> - if (Expect.class == type) { - LoggingHelper.deprecatedV5(log, "Injecting 'Expect' via method a argument is no longer recommended. Consider using instance variable injection instead.") - invocation.arguments[i] = new Expect(snapshotVerifier, invocation.feature.featureMethod.reflection) - } - } - invocation.proceed() - } - - private void updateInstanceVariable(Object testInstance, Method testMethod) { - ReflectionUtils.findFieldByPredicate(testInstance.class, { field -> field.getType() == Expect.class }) - .ifPresent({ field -> - Expect expect = Expect.of(snapshotVerifier, testMethod); - ReflectionUtils.makeAccessible(field); - try { - field.set(testInstance, expect); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }); - } - - @Override - void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { - this.snapshotVerifier.validateSnapshots(); - } -} diff --git a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/SpecificationBase.groovy b/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/SpecificationBase.groovy deleted file mode 100644 index c5f7389..0000000 --- a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/SpecificationBase.groovy +++ /dev/null @@ -1,10 +0,0 @@ -package au.com.origin.snapshots - -import org.junit.runner.RunWith -import org.spockframework.runtime.Sputnik -import spock.lang.Specification - -@RunWith(Sputnik.class) -class SpecificationBase extends Specification { - Expect expect; -} diff --git a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/SpockExtensionUsedSpec.groovy b/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/SpockExtensionUsedSpec.groovy deleted file mode 100644 index e9bd9ea..0000000 --- a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/SpockExtensionUsedSpec.groovy +++ /dev/null @@ -1,79 +0,0 @@ -package au.com.origin.snapshots - -import au.com.origin.snapshots.annotations.SnapshotName -import au.com.origin.snapshots.spock.EnableSnapshots -import spock.lang.Specification -import spock.lang.Unroll - -@EnableSnapshots -class SpockExtensionUsedSpec extends Specification { - - Expect expect - - @SnapshotName("Should use extension") - def "Should use extension"(Expect expect) { - when: - expect.toMatchSnapshot("Hello World") - - then: - true - } - - @SnapshotName("Should use extension again") - def "Should use extension again"(Expect expect) { - when: - expect.toMatchSnapshot("Hello World") - - then: - true - } - - @SnapshotName("Should use extension via instance variable") - def "Should use extension via instance variable"() { - when: - expect.toMatchSnapshot("Hello World") - - then: - true - } - - @SnapshotName("DataTable example 1") - @Unroll - def 'DataTable example 1: #letter'(def letter) { - given: 'I use an @Unroll function' - String result = letter.toUpperCase() - - when: 'I snapshot the letter' - expect.scenario("letter $letter").toMatchSnapshot(result) - - then: - true - - where: - [letter] << [['A'],['B'],['C']] - } - - - @SnapshotName("DataTable example 2") - def 'DataTable example 2: #scenario to uppercase'() { - when: 'I convert to uppercase' - String result = value.toUpperCase(); - then: 'Should convert letters to uppercase' - // Check you snapshot against your output using a unique scenario - expect.scenario(scenario).toMatchSnapshot(result) - where: - scenario | value - 'letter' | 'a' - 'number' | '1' - } - - @SnapshotName("Can run a non snapshot test") - def "Can run a non snapshot test"() { - when: - def isTrue = true - - then: - isTrue - } - -} diff --git a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/TestBaseSpec.groovy b/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/TestBaseSpec.groovy deleted file mode 100644 index 9383a83..0000000 --- a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/TestBaseSpec.groovy +++ /dev/null @@ -1,18 +0,0 @@ -package au.com.origin.snapshots - -import au.com.origin.snapshots.annotations.SnapshotName -import au.com.origin.snapshots.spock.EnableSnapshots - -@EnableSnapshots -class TestBaseSpec extends SpecificationBase { - - @SnapshotName("Should use extension") - def "Should use extension"() { - when: - expect.toMatchSnapshot("Hello World") - - then: - true - } - -} diff --git a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/__snapshots__/SpockExtensionUsedSpec.snap b/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/__snapshots__/SpockExtensionUsedSpec.snap deleted file mode 100644 index e29808b..0000000 --- a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/__snapshots__/SpockExtensionUsedSpec.snap +++ /dev/null @@ -1,38 +0,0 @@ -DataTable example 1[letter A]=[ -A -] - - -DataTable example 1[letter B]=[ -B -] - - -DataTable example 1[letter C]=[ -C -] - - -DataTable example 2[letter]=[ -A -] - - -DataTable example 2[number]=[ -1 -] - - -Should use extension again=[ -Hello World -] - - -Should use extension via instance variable=[ -Hello World -] - - -Should use extension=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/__snapshots__/TestBaseSpec.snap b/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/__snapshots__/TestBaseSpec.snap deleted file mode 100644 index 7cf9614..0000000 --- a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/__snapshots__/TestBaseSpec.snap +++ /dev/null @@ -1,3 +0,0 @@ -Should use extension=[ -Hello World -] \ No newline at end of file diff --git a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/SpockExample.groovy b/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/SpockExample.groovy deleted file mode 100644 index 2eff02c..0000000 --- a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/SpockExample.groovy +++ /dev/null @@ -1,34 +0,0 @@ -package au.com.origin.snapshots.docs - -import au.com.origin.snapshots.Expect -import au.com.origin.snapshots.annotations.SnapshotName -import au.com.origin.snapshots.spock.EnableSnapshots -import spock.lang.Specification - -// Ensure you enable snapshot testing support -@EnableSnapshots -class SpockExample extends Specification { - - // Option 1: inject Expect as an instance variable - private Expect expect - - // With spock tests you should always use @SnapshotName - otherwise they become coupled to test order - @SnapshotName("should_use_extension") - def "Should use extension"() { - when: - expect.toMatchSnapshot("Hello World") - - then: - true - } - - @SnapshotName("should_use_extension_as_method_argument") - // Option 2: inject Expect into the method signature - def "Should use extension as method argument"(Expect expect) { - when: - expect.toMatchSnapshot("Hello World") - - then: - true - } -} diff --git a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/SpockWithParametersExample.groovy b/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/SpockWithParametersExample.groovy deleted file mode 100644 index 855bbcd..0000000 --- a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/SpockWithParametersExample.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package au.com.origin.snapshots.docs - -import au.com.origin.snapshots.Expect -import au.com.origin.snapshots.annotations.SnapshotName -import au.com.origin.snapshots.spock.EnableSnapshots -import spock.lang.Specification - -@EnableSnapshots -class SpockWithParametersExample extends Specification { - - private Expect expect - - @SnapshotName("convert_to_uppercase") - def 'Convert #scenario to uppercase'() { - when: 'I convert to uppercase' - String result = value.toUpperCase(); - then: 'Should convert letters to uppercase' - // Check you snapshot against your output using a unique scenario - expect.scenario(scenario).toMatchSnapshot(result) - where: - scenario | value - 'letter' | 'a' - 'number' | '1' - } -} \ No newline at end of file diff --git a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/__snapshots__/SpockExample.snap b/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/__snapshots__/SpockExample.snap deleted file mode 100644 index 1a47743..0000000 --- a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/__snapshots__/SpockExample.snap +++ /dev/null @@ -1,8 +0,0 @@ -should_use_extension=[ -Hello World -] - - -should_use_extension_as_method_argument=[ -Hello World -] diff --git a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/__snapshots__/SpockWithParametersExample.snap b/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/__snapshots__/SpockWithParametersExample.snap deleted file mode 100644 index 9cf3a21..0000000 --- a/java-snapshot-testing-spock/src/test/groovy/au/com/origin/snapshots/docs/__snapshots__/SpockWithParametersExample.snap +++ /dev/null @@ -1,8 +0,0 @@ -convert_to_uppercase[letter]=[ -A -] - - -convert_to_uppercase[number]=[ -1 -] \ No newline at end of file diff --git a/java-snapshot-testing-spock/src/test/resources/snapshot.properties b/java-snapshot-testing-spock/src/test/resources/snapshot.properties deleted file mode 100644 index 6c14caa..0000000 --- a/java-snapshot-testing-spock/src/test/resources/snapshot.properties +++ /dev/null @@ -1,7 +0,0 @@ -serializer=au.com.origin.snapshots.serializers.v1.ToStringSnapshotSerializer -comparator=au.com.origin.snapshots.comparators.v1.PlainTextEqualsComparator -reporters=au.com.origin.snapshots.reporters.v1.PlainTextSnapshotReporter -snapshot-dir=__snapshots__ -output-dir=src/test/groovy -ci-env-var=CI -update-snapshot=none \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100755 index 0000000..b7f0646 --- /dev/null +++ b/mvnw @@ -0,0 +1,287 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.1.1 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + printf '%s' "$(cd "$basedir"; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname $0)") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $wrapperUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + QUIET="--quiet" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + elif command -v curl > /dev/null; then + QUIET="--silent" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=`cygpath --path --windows "$javaSource"` + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..474c9d6 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,187 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.1.1 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..73cf7c2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,249 @@ + + + 4.0.0 + + + parent + io.github.finoid + 0.11.1 + + + io.github.finoid + snapshot-testing-parent + ${revision} + finoid-snapshot-testing-parent + A Java library designed to streamline testing by snapshot testing. + pom + + + 21 + 21 + 21 + 21 + UTF-8 + + + 0.10.0 + + 3.11.1 + 3.51.1 + 0.12.8 + true + 1.7.3 + 2.20.1 + 3.0.3 + 1.0.0 + 5.12.2 + 6.0.2 + 1.18.42 + 2.0.17 + + + + snapshot-testing-core + snapshot-testing-jackson2 + snapshot-testing-jackson3 + snapshot-testing-junit5 + snapshot-testing-junit6 + + + + + + io.github.finoid + snapshot-testing-core + ${revision} + + + io.github.finoid + snapshot-testing-jackson2 + ${revision} + + + io.github.finoid + snapshot-testing-jackson3 + ${revision} + + + io.github.finoid + snapshot-testing-junit5 + ${revision} + + + io.github.finoid + snapshot-testing-junit6 + ${revision} + + + org.checkerframework + checker-qual + ${checker-qual.version} + + + + + + + org.jspecify + jspecify + ${jspecify.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + + nicklaswallgren + Nicklas Wallgren + nicklas.wallgren@gmail.com + + + + + + The MIT License + https://opensource.org/licenses/MIT + repo + + + + + https://github.com/finoid/snapshot-testing + scm:git:https://github.com/finoid/snapshot-testing.git + scm:git:ssh://git@github.com/finoid/snapshot-testing.git + HEAD + + + https://github.com/finoid/snapshot-testing + + + + + io.github.finoid + codequality-maven-plugin + ${codequality-maven-plugin.version} + + true + + + maven-code-quality + verify + + code-quality + + + + + + ${code-quality.feature.enabled} + DEBUG + DIFF_COVERAGE + + ${code-quality.feature.checkstyle} + ${code-quality.feature.checkstyle.permissive} + + ${code-quality.configuration.checkstyle.suppression-location} + + + + ${code-quality.configuration.checkstyle.suppression-location} + + + + + ${code-quality.feature.error-prone} + ${code-quality.feature.error-prone.permissive} + ${code-quality.feature.null-away} + + + ${code-quality.feature.checker-framework} + ${code-quality.feature.checker-framework.permissive} + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-versions + + enforce + + + + + true + + module-info + + + + + + + + + + org.codehaus.mojo + flatten-maven-plugin + ${flatten-maven-plugin.version} + + + oss + true + + + + flatten + process-resources + + flatten + + + + + flatten.clean + clean + + clean + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source} + ${maven.compiler.target} + ${maven.compiler.release} + + + + -parameters + + + + + org.projectlombok + lombok + ${lombok.version} + + + + + + + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 05e3e32..0000000 --- a/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = 'java-snapshot-testing' - -include 'java-snapshot-testing-core', 'java-snapshot-testing-junit4', 'java-snapshot-testing-junit5', 'java-snapshot-testing-spock', 'java-snapshot-testing-plugin-jackson' \ No newline at end of file diff --git a/snapshot-testing-core/.gitignore b/snapshot-testing-core/.gitignore new file mode 100644 index 0000000..fa1ce7d --- /dev/null +++ b/snapshot-testing-core/.gitignore @@ -0,0 +1,4 @@ +src/test/java/io/github/finoid/snapshots/__snapshots__/ +src/test/some-folder/ +anyFilePath.debug +blah \ No newline at end of file diff --git a/snapshot-testing-core/pom.xml b/snapshot-testing-core/pom.xml new file mode 100644 index 0000000..d500061 --- /dev/null +++ b/snapshot-testing-core/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + io.github.finoid + snapshot-testing-parent + ${revision} + ../pom.xml + + + snapshot-testing-core + finoid-snapshot-testing-core + Core library for snapshot-testing + + + 21 + 21 + 21 + 21 + UTF-8 + + 2.21.0 + 5.21.0 + + + + + org.assertj + assertj-core + ${assertj.version} + + + org.checkerframework + checker-qual + + + org.opentest4j + opentest4j + 1.3.0 + + + org.slf4j + slf4j-api + ${slf4j.version} + provided + + + + commons-io + commons-io + ${commons-io.version} + test + + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.junit.jupiter + junit-jupiter-api + ${junit5.version} + + + org.junit.jupiter + junit-jupiter-engine + ${junit5.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit5.version} + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + \ No newline at end of file diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/Expect.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/Expect.java new file mode 100644 index 0000000..1fd827a --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/Expect.java @@ -0,0 +1,173 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.comparators.SnapshotComparator; +import io.github.finoid.snapshots.reporters.SnapshotReporter; +import io.github.finoid.snapshots.serializers.Base64SnapshotSerializer; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import io.github.finoid.snapshots.serializers.ToStringSnapshotSerializer; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@SuppressWarnings("NullAway") // TODO (nw) refactor +@RequiredArgsConstructor +public class Expect { + private final SnapshotVerifier snapshotVerifier; + private final Method testMethod; + private final Map headers = new HashMap<>(); + private SnapshotSerializer snapshotSerializer; + private SnapshotComparator snapshotComparator; + private List snapshotReporters; + private String scenario; + + public static Expect of(SnapshotVerifier snapshotVerifier, Method method) { + return new Expect(snapshotVerifier, method); + } + + /** + * Make an assertion on the given input parameters against what already exists. + * + *

If you were previously using varargs and see an error - you can fix the error using + * "toMatchSnapshotLegacy", however, a better approach is to use the ".scenario()" feature as + * future versions of this library will most likely remove the legacy implementation completely. + * + * @param object snapshot object + */ + public void toMatchSnapshot(Object object) { + SnapshotContext snapshotContext = snapshotVerifier.expectCondition(testMethod, object); + if (snapshotSerializer != null) { + snapshotContext.setSnapshotSerializer(snapshotSerializer); + } + if (snapshotComparator != null) { + snapshotContext.setSnapshotComparator(snapshotComparator); + } + if (snapshotReporters != null) { + snapshotContext.setSnapshotReporters(snapshotReporters); + } + if (scenario != null) { + snapshotContext.setScenario(scenario); + } + snapshotContext.getHeader().putAll(headers); + + snapshotContext.checkValidContext(); + + snapshotContext.toMatchSnapshot(); + } + + /** + * Normally a snapshot can be applied only once to a test method. + * + *

For Parameterized tests where the same method is executed multiple times you can supply the + * scenario() to overcome this restriction. Ensure each scenario is unique. + * + * @param scenario - unique scenario description + * @return Snapshot + */ + public Expect scenario(String scenario) { + this.scenario = scenario; + return this; + } + + /** + * Apply a custom serializer for this snapshot. + * + * @param serializer your custom serializer + * @return Snapshot + */ + public Expect serializer(SnapshotSerializer serializer) { + this.snapshotSerializer = serializer; + return this; + } + + /** + * Apply a custom serializer for this snapshot. + * + * @param name - the {name} attribute serializer.{name} from snapshot.properties + * @return Snapshot + */ + public Expect serializer(String name) { + this.snapshotSerializer = SnapshotProperties.getInstance("serializer." + name); + return this; + } + + /** + * Apply a custom serializer for this snapshot. + * + * @param serializer your custom serializer + * @return this + * @see SnapshotSerializer + * @see ToStringSnapshotSerializer + * @see Base64SnapshotSerializer + */ + @SneakyThrows + public Expect serializer(Class serializer) { + this.snapshotSerializer = serializer.getConstructor().newInstance(); + return this; + } + + /** + * Apply a custom comparator for this snapshot. + * + * @param comparator your custom comparator + * @return Snapshot + */ + public Expect comparator(SnapshotComparator comparator) { + this.snapshotComparator = comparator; + return this; + } + + /** + * Apply a custom comparator for this snapshot. + * + * @param name the {name} attribute comparator.{name} from snapshot.properties + * @return Snapshot + */ + public Expect comparator(String name) { + this.snapshotComparator = SnapshotProperties.getInstance("comparator." + name); + return this; + } + + /** + * Apply a list of custom reporters for this snapshot This will replace the default reporters + * defined in the config. + * + * @param reporters your custom reporters + * @return Snapshot + */ + public Expect reporters(SnapshotReporter... reporters) { + this.snapshotReporters = Arrays.asList(reporters); + return this; + } + + /** + * Apply a list of custom reporters for this snapshot This will replace the default reporters + * defined in the config. + * + * @param name the {name} attribute reporters.{name} from snapshot.properties + * @return Snapshot + */ + public Expect reporters(String name) { + this.snapshotReporters = SnapshotProperties.getInstances("reporters." + name); + return this; + } + + /** + * Add anything you like to the snapshot header. + * + *

These custom headers can be used in serializers, comparators or reporters to change how they + * behave. + * + * @param key key + * @param value value + * @return Expect + */ + public Expect header(String key, String value) { + headers.put(key, value); + return this; + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/Snapshot.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/Snapshot.java new file mode 100644 index 0000000..a0e5e8c --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/Snapshot.java @@ -0,0 +1,81 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.exceptions.LogGithubIssueException; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@EqualsAndHashCode +@Builder +@Getter +@RequiredArgsConstructor +public class Snapshot implements Comparable { + + private final String name; + private final String scenario; + private final SnapshotHeader header; + private final String body; + + public static Snapshot parse(String rawText) { + String regex = + "^(?.*?)(\\[(?[^]]*)])?=(?

\\{[^}]*?})?(?(.*)$)"; + Pattern p = Pattern.compile(regex, Pattern.DOTALL); + Matcher m = p.matcher(rawText); + boolean found = m.find(); + if (!found) { + throw new LogGithubIssueException( + "Corrupt Snapshot (REGEX matches = 0): possibly due to manual editing or our REGEX failing\n" + + "Possible Solutions\n" + + "1. Ensure you have not accidentally manually edited the snapshot file!\n" + + "2. Compare the snapshot with GIT history"); + } + + String name = m.group("name"); + String scenario = m.group("scenario"); + String header = m.group("header"); + String snapshot = m.group("snapshot"); + + if (name == null || snapshot == null) { + throw new LogGithubIssueException( + "Corrupt Snapshot (REGEX name or snapshot group missing): possibly due to manual editing or our REGEX failing\n" + + "Possible Solutions\n" + + "1. Ensure you have not accidentally manually edited the snapshot file\n" + + "2. Compare the snapshot with your version control history"); + } + + return Snapshot.builder() + .name(name) + .scenario(scenario) + .header(SnapshotHeader.fromJson(header)) + .body(snapshot) + .build(); + } + + @Override + public int compareTo(Snapshot other) { + return (name + scenario).compareTo(other.name + other.scenario); + } + + public String getIdentifier() { + return scenario == null ? name : String.format("%s[%s]", name, scenario); + } + + /** + * The raw string representation of the snapshot as it would appear in the *.snap file. + * + * @return raw snapshot + */ + public String raw() { + String headerJson = (header == null) || (header.size() == 0) ? "" : header.toJson(); + return getIdentifier() + "=" + headerJson + body; + } + + @Override + public String toString() { + return raw(); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotContext.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotContext.java new file mode 100644 index 0000000..812b48f --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotContext.java @@ -0,0 +1,184 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.annotations.SnapshotName; +import io.github.finoid.snapshots.comparators.SnapshotComparator; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.exceptions.ReservedWordException; +import io.github.finoid.snapshots.exceptions.SnapshotExtensionException; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import io.github.finoid.snapshots.reporters.SnapshotReporter; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@SuppressWarnings("NullAway") // TODO (nw) refactor +public class SnapshotContext { + + private static final List RESERVED_WORDS = Arrays.asList("=", "[", "]"); + + private final SnapshotConfig snapshotConfig; + private final SnapshotFile snapshotFile; + private final boolean isCI; + + @Getter + private final Class testClass; + @Getter + private final Method testMethod; + private final Object current; + + @Setter + @Getter + private String scenario; + @Getter + private SnapshotHeader header = new SnapshotHeader(); + @Setter + private SnapshotSerializer snapshotSerializer; + @Setter + private SnapshotComparator snapshotComparator; + @Setter + private List snapshotReporters; + + @SuppressWarnings("NullAway") // TODO (nw) refactor + SnapshotContext( + SnapshotConfig snapshotConfig, + SnapshotFile snapshotFile, + Class testClass, + Method testMethod, + Object current) { + this.snapshotConfig = snapshotConfig; + this.snapshotFile = snapshotFile; + this.testClass = testClass; + this.testMethod = testMethod; + this.current = current; + + this.isCI = snapshotConfig.isCI(); + this.snapshotSerializer = snapshotConfig.getSerializer(); + this.snapshotComparator = snapshotConfig.getComparator(); + this.snapshotReporters = snapshotConfig.getReporters(); + this.scenario = null; + } + + public void toMatchSnapshot() { + + Set rawSnapshots = snapshotFile.getSnapshots(); + Snapshot previousSnapshot = getRawSnapshot(rawSnapshots); + Snapshot currentSnapshot = takeSnapshot(); + + if (previousSnapshot != null && shouldUpdateSnapshot()) { + snapshotFile.getSnapshots().remove(previousSnapshot); + previousSnapshot = null; + } + + if (previousSnapshot != null) { + snapshotFile.pushDebugSnapshot(currentSnapshot); + + // Match existing Snapshot + if (!snapshotComparator.matches(previousSnapshot, currentSnapshot)) { + snapshotFile.createDebugFile(currentSnapshot); + + List reporters = + snapshotReporters.stream() + .filter(reporter -> reporter.supportsFormat(snapshotSerializer.getOutputFormat())) + .collect(Collectors.toList()); + + if (reporters.isEmpty()) { + String comparator = snapshotComparator.getClass().getSimpleName(); + throw new IllegalStateException( + "No compatible reporters found for comparator " + comparator); + } + + List errors = new ArrayList<>(); + + for (SnapshotReporter reporter : reporters) { + try { + reporter.report(previousSnapshot, currentSnapshot); + } catch (Throwable t) { + errors.add(t); + } + } + + if (!errors.isEmpty()) { + throw new SnapshotMatchException("Error(s) matching snapshot(s)", errors); + } + } + } else { + if (this.isCI) { + log.error( + "We detected you are running on a CI Server - if this is incorrect please override the isCI() method in SnapshotConfig"); + throw new SnapshotMatchException( + "Snapshot [" + + resolveSnapshotIdentifier() + + "] not found. Has this snapshot been committed ?"); + } else { + log.warn( + "We detected you are running on a developer machine - if this is incorrect please override the isCI() method in SnapshotConfig"); + // Create New Snapshot + snapshotFile.pushSnapshot(currentSnapshot); + snapshotFile.pushDebugSnapshot(currentSnapshot); + } + } + } + + private boolean shouldUpdateSnapshot() { + if (snapshotConfig.updateSnapshot().isPresent() && snapshotConfig.isCI()) { + throw new SnapshotExtensionException( + "isCI=true & update-snapshot=" + + snapshotConfig.updateSnapshot() + + ". Updating snapshots on CI is not allowed"); + } + if (snapshotConfig.updateSnapshot().isPresent()) { + return resolveSnapshotIdentifier().contains(snapshotConfig.updateSnapshot().get()); + } else { + return false; + } + } + + private Snapshot getRawSnapshot(Collection rawSnapshots) { + synchronized (rawSnapshots) { + for (Snapshot rawSnapshot : rawSnapshots) { + if (rawSnapshot.getIdentifier().equals(resolveSnapshotIdentifier())) { + return rawSnapshot; + } + } + } + return null; + } + + private Snapshot takeSnapshot() { + SnapshotSerializerContext sg = SnapshotSerializerContext.from(this); + return snapshotSerializer.apply(current, sg); + } + + String resolveSnapshotIdentifier() { + String scenarioFormat = scenario == null ? "" : "[" + scenario + "]"; + return snapshotName() + scenarioFormat; + } + + private String snapshotName() { + SnapshotName snapshotName = testMethod.getAnnotation(SnapshotName.class); + return snapshotName == null + ? testClass.getName() + "." + testMethod.getName() + : snapshotName.value(); + } + + void checkValidContext() { + for (String rw : RESERVED_WORDS) { + if (snapshotName().contains(rw)) { + throw new ReservedWordException("snapshot name", rw, RESERVED_WORDS); + } + if (scenario != null && scenario.contains(rw)) { + throw new ReservedWordException("scenario name", rw, RESERVED_WORDS); + } + } + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotFile.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotFile.java new file mode 100644 index 0000000..cc6b296 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotFile.java @@ -0,0 +1,173 @@ +package io.github.finoid.snapshots; + +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Slf4j +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class SnapshotFile { + + public static final String SPLIT_STRING = "\n\n\n"; + + private final String fileName; + private final Class testClass; + @Getter + private Set snapshots = Collections.synchronizedSortedSet(new TreeSet<>()); + private Set debugSnapshots = Collections.synchronizedSortedSet(new TreeSet<>()); + + public SnapshotFile(String srcDirPath, String fileName, Class testClass) throws IOException { + this.testClass = testClass; + this.fileName = srcDirPath + File.separator + fileName; + log.info("Snapshot File: " + this.fileName); + + StringBuilder fileContent = new StringBuilder(); + + try (BufferedReader br = + new BufferedReader( + new InputStreamReader(new FileInputStream(this.fileName), StandardCharsets.UTF_8))) { + + String currentLine; + + while ((currentLine = br.readLine()) != null) { + fileContent.append(currentLine + "\n"); + } + + String fileText = fileContent.toString(); + if (!"".equals(fileText.trim())) { + snapshots = + Collections.synchronizedSortedSet( + Stream.of(fileContent.toString().split(SPLIT_STRING)) + .map(String::trim) + .map(Snapshot::parse) + .collect(Collectors.toCollection(TreeSet::new))); + } + } catch (IOException e) { + // ... + } + + deleteDebugFile(); + } + + private String getDebugFilename() { + return this.fileName + ".debug"; + } + + public File createDebugFile(Snapshot snapshot) { + File file = null; + try { + file = new File(getDebugFilename()); + file.getParentFile().mkdirs(); + file.createNewFile(); + + try (FileOutputStream fileStream = new FileOutputStream(file, false)) { + fileStream.write(snapshot.raw().getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException("Unable to create debug file ", e); + } + } catch (IOException e) { + throw new RuntimeException("Unable to create debug file ", e); + } + + return file; + } + + @SneakyThrows + public void deleteDebugFile() { + Files.deleteIfExists(Paths.get(getDebugFilename())); + } + + @SneakyThrows + public void delete() { + Files.deleteIfExists(Paths.get(this.fileName)); + } + + @SneakyThrows + public synchronized File createFileIfNotExists(String fileName) { + Path path = Paths.get(fileName); + if (!Files.exists(path)) { + Files.createDirectories(path.getParent()); + Files.createFile(path); + } + return path.toFile(); + } + + @SuppressWarnings("SynchronizeOnNonFinalField") // TODO (nw) rewrite + public void pushSnapshot(Snapshot snapshot) { + synchronized (snapshots) { + snapshots.add(snapshot); + Set rawSnapshots = + snapshots.stream().map(Snapshot::raw).collect(Collectors.toCollection(TreeSet::new)); + updateFile(this.fileName, rawSnapshots); + } + } + + public synchronized void pushDebugSnapshot(Snapshot snapshot) { + debugSnapshots.add(snapshot); + Set rawDebugSnapshots = + debugSnapshots.stream().map(Snapshot::raw).collect(Collectors.toCollection(TreeSet::new)); + updateFile(getDebugFilename(), rawDebugSnapshots); + } + + private void updateFile(String fileName, Set rawSnapshots) { + File file = createFileIfNotExists(fileName); + try (FileOutputStream fileStream = new FileOutputStream(file, false)) { + byte[] myBytes = String.join(SPLIT_STRING, rawSnapshots).getBytes(StandardCharsets.UTF_8); + fileStream.write(myBytes); + } catch (IOException e) { + throw new RuntimeException("Unable to write debug file ", e); + } + } + + @SneakyThrows + public void cleanup() { + Path path = Paths.get(this.fileName); + if (Files.exists(path)) { + if (Files.size(path) == 0 || snapshotsAreTheSame()) { + deleteDebugFile(); + } + + if (Files.size(path) == 0) { + delete(); + } else { + String content = + new String(Files.readAllBytes(Paths.get(this.fileName)), StandardCharsets.UTF_8); + Files.write( + path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING); + } + } + } + + @SneakyThrows + private boolean snapshotsAreTheSame() { + Path path = Paths.get(this.getDebugFilename()); + if (Files.exists(path)) { + List snapshotFileContent = + Files.readAllLines(Paths.get(this.fileName), StandardCharsets.UTF_8); + List debugSnapshotFileContent = + Files.readAllLines(Paths.get(this.getDebugFilename()), StandardCharsets.UTF_8); + return Objects.equals(snapshotFileContent, debugSnapshotFileContent); + } + + return false; + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotHeader.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotHeader.java new file mode 100644 index 0000000..3f3870c --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotHeader.java @@ -0,0 +1,50 @@ +package io.github.finoid.snapshots; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@RequiredArgsConstructor +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class SnapshotHeader extends HashMap { + + @SneakyThrows + public static SnapshotHeader fromJson(String json) { + SnapshotHeader snapshotHeader = new SnapshotHeader(); + + if (json == null) { + return snapshotHeader; + } + + String regex = "\\\"(?.*)\\\": \\\"(?.*)\\\""; + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(json); + while (m.find()) { + snapshotHeader.put(m.group("key"), m.group("value")); + } + return snapshotHeader; + } + + // + // Manual JSON serialization/deserialization as I don't want to + // include another dependency for it + // + @SneakyThrows + public String toJson() { + StringBuilder b = new StringBuilder(); + b.append("{\n"); + final int lastIndex = this.size(); + int currIndex = 0; + for (Map.Entry entry : this.entrySet()) { + currIndex++; + String format = currIndex == lastIndex ? " \"%s\": \"%s\"\n" : " \"%s\": \"%s\",\n"; + b.append(String.format(format, entry.getKey(), entry.getValue())); + } + b.append("}"); + return b.toString(); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotProperties.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotProperties.java new file mode 100644 index 0000000..a9550a9 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotProperties.java @@ -0,0 +1,61 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.exceptions.MissingSnapshotPropertiesKeyException; +import lombok.extern.slf4j.Slf4j; + +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; + +@Slf4j +@SuppressWarnings({"checkstyle:all", "ImmutableEnumChecker"}) // TODO (nw) rewrite +public enum SnapshotProperties { + INSTANCE; + + Properties snapshotProperties = new Properties(); + + SnapshotProperties() { + try { + InputStream in = + SnapshotProperties.class.getClassLoader().getResourceAsStream("snapshot.properties"); + snapshotProperties.load(in); + } catch (Exception e) { + // It's ok, if the SnapshotConfig implementation attempts to get a property they will receive + // a MissingSnapshotPropertiesKeyException + } + } + + public static String getOrThrow(String key) { + Object value = INSTANCE.snapshotProperties.get(key); + if (value == null) { + throw new MissingSnapshotPropertiesKeyException(key); + } + return value.toString(); + } + + @SuppressWarnings("TypeParameterUnusedInFormals") // TODO (nw) rewrite + public static T getInstance(String key) { + String value = SnapshotProperties.getOrThrow(key); + return createInstance(value); + } + + public static List getInstances(String key) { + String value = SnapshotProperties.getOrThrow(key); + return Arrays.stream(value.split(",")) + .map(String::trim) + .map(it -> (T) createInstance(it)) + .collect(Collectors.toList()); + } + + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) // TODO (nw) rewrite + private static T createInstance(String className) { + try { + Class clazz = Class.forName(className); + return (T) clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException("Unable to instantiate class " + className, e); + } + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotSerializerContext.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotSerializerContext.java new file mode 100644 index 0000000..6d2ca61 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotSerializerContext.java @@ -0,0 +1,48 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.annotations.SnapshotName; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.lang.reflect.Method; + +/** + * Contains details of the pending snapshot that can be modified in the Serializer prior to calling + * toSnapshot(). + */ +@AllArgsConstructor +public class SnapshotSerializerContext { + + @Getter + @Setter + private final String name; + @Getter + @Setter + private final String scenario; + @Getter + @Setter + private final SnapshotHeader header; + @Getter + private final Class testClass; + @Getter + private final Method testMethod; + + public static SnapshotSerializerContext from(SnapshotContext context) { + SnapshotName snapshotName = context.getTestMethod().getAnnotation(SnapshotName.class); + String name = + snapshotName == null + ? context.getTestClass().getName() + "." + context.getTestMethod().getName() + : snapshotName.value(); + return new SnapshotSerializerContext( + name, + context.getScenario(), + context.getHeader(), + context.getTestClass(), + context.getTestMethod()); + } + + public Snapshot toSnapshot(String body) { + return Snapshot.builder().name(name).scenario(scenario).header(header).body(body).build(); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotVerifier.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotVerifier.java new file mode 100644 index 0000000..fc7c8af --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/SnapshotVerifier.java @@ -0,0 +1,155 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.annotations.SnapshotName; +import io.github.finoid.snapshots.annotations.UseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.exceptions.SnapshotExtensionException; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +@Slf4j +@RequiredArgsConstructor +public class SnapshotVerifier { + + private final Class testClass; + private final SnapshotFile snapshotFile; + private final SnapshotConfig config; + private final boolean failOnOrphans; + + private final Collection calledSnapshots = + Collections.synchronizedCollection(new ArrayList<>()); + + public SnapshotVerifier(SnapshotConfig frameworkSnapshotConfig, Class testClass) { + this(frameworkSnapshotConfig, testClass, false); + } + + /** + * Instantiate before any tests have run for a given class. + * + * @param frameworkSnapshotConfig configuration to use + * @param failOnOrphans should the test break if snapshots exist with no matching method in the + * test class + * @param testClass reference to class under test + */ + @SneakyThrows + @SuppressWarnings("NullAway") // TODO (nw) refactor + public SnapshotVerifier( + SnapshotConfig frameworkSnapshotConfig, Class testClass, boolean failOnOrphans) { + try { + verifyNoConflictingSnapshotNames(testClass); + + UseSnapshotConfig customConfig = testClass.getAnnotation(UseSnapshotConfig.class); + SnapshotConfig snapshotConfig = + customConfig == null ? frameworkSnapshotConfig : customConfig.value().getDeclaredConstructor().newInstance(); + + // Matcher.quoteReplacement required for Windows + String testFilename = + testClass.getName().replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + ".snap"; + + File fileUnderTest = new File(testFilename); + File snapshotDir = new File(fileUnderTest.getParentFile(), snapshotConfig.getSnapshotDir()); + + // Support legacy trailing space syntax + String testSrcDir = snapshotConfig.getOutputDir(); + String testSrcDirNoTrailing = + testSrcDir.endsWith("/") ? testSrcDir.substring(0, testSrcDir.length() - 1) : testSrcDir; + SnapshotFile snapshotFile = + new SnapshotFile( + testSrcDirNoTrailing, + snapshotDir.getPath() + File.separator + fileUnderTest.getName(), + testClass); + + this.testClass = testClass; + this.snapshotFile = snapshotFile; + this.config = snapshotConfig; + this.failOnOrphans = failOnOrphans; + + } catch (IOException | InstantiationException | IllegalAccessException e) { + throw new SnapshotExtensionException(e.getMessage()); + } + } + + private void verifyNoConflictingSnapshotNames(Class testClass) { + Map> allSnapshotAnnotationNames = + Arrays.stream(testClass.getDeclaredMethods()) + .filter(it -> it.isAnnotationPresent(SnapshotName.class)) + .map(it -> it.getAnnotation(SnapshotName.class)) + .map(SnapshotName::value) + .collect(Collectors.groupingBy(String::toString)); + + boolean hasDuplicateSnapshotNames = + allSnapshotAnnotationNames.entrySet().stream() + .filter(it -> it.getValue().size() > 1) + .peek( + it -> + log.error( + "Oops, looks like you set the same name of two separate snapshots @SnapshotName(\"{}\") in class {}", + it.getKey(), + testClass.getName())) + .count() + > 0; + if (hasDuplicateSnapshotNames) { + throw new SnapshotExtensionException("Duplicate @SnapshotName annotations found!"); + } + } + + @SneakyThrows + public SnapshotContext expectCondition(Method testMethod, Object object) { + SnapshotContext snapshotContext = + new SnapshotContext(config, snapshotFile, testClass, testMethod, object); + calledSnapshots.add(snapshotContext); + return snapshotContext; + } + + public void validateSnapshots() { + Set rawSnapshots = snapshotFile.getSnapshots(); + Set snapshotNames = + calledSnapshots.stream() + .map(SnapshotContext::resolveSnapshotIdentifier) + .collect(Collectors.toSet()); + List unusedSnapshots = new ArrayList<>(); + + for (Snapshot rawSnapshot : rawSnapshots) { + boolean foundSnapshot = false; + for (String snapshotName : snapshotNames) { + if (rawSnapshot.getIdentifier().equals(snapshotName)) { + foundSnapshot = true; + break; + } + } + if (!foundSnapshot) { + unusedSnapshots.add(rawSnapshot); + } + } + if (unusedSnapshots.size() > 0) { + List unusedRawSnapshots = + unusedSnapshots.stream().map(Snapshot::raw).collect(Collectors.toList()); + String errorMessage = + "All unused Snapshots:\n" + + String.join("\n", unusedRawSnapshots) + + "\n\nHave you deleted tests? Have you renamed a test method?"; + if (failOnOrphans) { + log.error(errorMessage); + throw new SnapshotMatchException("ERROR: Found orphan snapshots"); + } else { + log.warn(errorMessage); + } + } + snapshotFile.cleanup(); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/annotations/SnapshotName.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/annotations/SnapshotName.java new file mode 100644 index 0000000..de73ce7 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/annotations/SnapshotName.java @@ -0,0 +1,16 @@ +package io.github.finoid.snapshots.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Target({ElementType.METHOD}) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +public @interface SnapshotName { + String value(); +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/annotations/UseSnapshotConfig.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/annotations/UseSnapshotConfig.java new file mode 100644 index 0000000..800a335 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/annotations/UseSnapshotConfig.java @@ -0,0 +1,18 @@ +package io.github.finoid.snapshots.annotations; + +import io.github.finoid.snapshots.config.SnapshotConfig; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Target({ElementType.TYPE}) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +public @interface UseSnapshotConfig { + Class value(); +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/comparators/PlainTextEqualsComparator.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/comparators/PlainTextEqualsComparator.java new file mode 100644 index 0000000..10ab573 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/comparators/PlainTextEqualsComparator.java @@ -0,0 +1,17 @@ +package io.github.finoid.snapshots.comparators; + +import io.github.finoid.snapshots.logging.LoggingHelper; +import lombok.extern.slf4j.Slf4j; + +@Deprecated +@Slf4j +public class PlainTextEqualsComparator + extends io.github.finoid.snapshots.comparators.v1.PlainTextEqualsComparator { + + public PlainTextEqualsComparator() { + super(); + LoggingHelper.deprecatedV5( + log, + "Update to `v1.comparators.io.github.finoid.snapshots.PlainTextEqualsComparator` in `snapshot.properties`"); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/comparators/SnapshotComparator.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/comparators/SnapshotComparator.java new file mode 100644 index 0000000..e4e85a3 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/comparators/SnapshotComparator.java @@ -0,0 +1,7 @@ +package io.github.finoid.snapshots.comparators; + +import io.github.finoid.snapshots.Snapshot; + +public interface SnapshotComparator { + boolean matches(Snapshot previous, Snapshot current); +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/comparators/v1/PlainTextEqualsComparator.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/comparators/v1/PlainTextEqualsComparator.java new file mode 100644 index 0000000..deb37d0 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/comparators/v1/PlainTextEqualsComparator.java @@ -0,0 +1,12 @@ +package io.github.finoid.snapshots.comparators.v1; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.comparators.SnapshotComparator; + +public class PlainTextEqualsComparator implements SnapshotComparator { + + @Override + public boolean matches(Snapshot previous, Snapshot current) { + return previous.getBody().equals(current.getBody()); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/config/PropertyResolvingSnapshotConfig.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/config/PropertyResolvingSnapshotConfig.java new file mode 100644 index 0000000..7f40607 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/config/PropertyResolvingSnapshotConfig.java @@ -0,0 +1,78 @@ +package io.github.finoid.snapshots.config; + +import io.github.finoid.snapshots.SnapshotProperties; +import io.github.finoid.snapshots.comparators.SnapshotComparator; +import io.github.finoid.snapshots.exceptions.MissingSnapshotPropertiesKeyException; +import io.github.finoid.snapshots.logging.LoggingHelper; +import io.github.finoid.snapshots.reporters.SnapshotReporter; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Optional; + +@Slf4j +public class PropertyResolvingSnapshotConfig implements SnapshotConfig { + + @Override + public String getOutputDir() { + return SnapshotProperties.getOrThrow("output-dir"); + } + + @Override + public String getSnapshotDir() { + return SnapshotProperties.getOrThrow("snapshot-dir"); + } + + @Override + public Optional updateSnapshot() { + // This was the original way to update snapshots + Optional legacyFlag = + Optional.ofNullable(System.getProperty(JVM_UPDATE_SNAPSHOTS_PARAMETER)); + if (legacyFlag.isPresent()) { + LoggingHelper.deprecatedV5( + log, + "Passing -PupdateSnapshot will be removed in a future release. Consider using snapshot.properties 'update-snapshot' toggle instead"); + if ("false".equals(legacyFlag.get())) { + return Optional.empty(); + } + return legacyFlag; + } + + try { + String updateSnapshot = SnapshotProperties.getOrThrow("update-snapshot"); + if ("all".equals(updateSnapshot)) { + return Optional.of(""); + } else if ("none".equals(updateSnapshot)) { + return Optional.empty(); + } + return Optional.of(updateSnapshot); + } catch (MissingSnapshotPropertiesKeyException ex) { + LoggingHelper.deprecatedV5( + log, + "You do not have 'update-snapshot=none' defined in your snapshot.properties - consider adding it now"); + return Optional.empty(); + } + } + + @Override + public SnapshotSerializer getSerializer() { + return SnapshotProperties.getInstance("serializer"); + } + + @Override + public SnapshotComparator getComparator() { + return SnapshotProperties.getInstance("comparator"); + } + + @Override + public List getReporters() { + return SnapshotProperties.getInstances("reporters"); + } + + @Override + public boolean isCI() { + String envVariable = SnapshotProperties.getOrThrow("ci-env-var"); + return System.getenv(envVariable) != null; + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/config/SnapshotConfig.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/config/SnapshotConfig.java new file mode 100644 index 0000000..9d1ed6f --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/config/SnapshotConfig.java @@ -0,0 +1,88 @@ +package io.github.finoid.snapshots.config; + +import io.github.finoid.snapshots.comparators.SnapshotComparator; +import io.github.finoid.snapshots.reporters.SnapshotReporter; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; + +import java.util.List; +import java.util.Optional; + +/** + * Snapshot Configuration. + * + *

Implement this interface when integrating `java-snapshot-testing` with a custom testing + * library + */ +public interface SnapshotConfig { + @Deprecated + String JVM_UPDATE_SNAPSHOTS_PARAMETER = "updateSnapshot"; + + /** + * The base directory where files get written (excluding package directories) default: + * "src/test/java". + * + *

You might want to override if you have tests under "src/test/integration" for example + * + * @return snapshot output folder + */ + String getOutputDir(); + + /** + * Subdirectory to store snapshots in. + * + * @return name of subdirectory + */ + String getSnapshotDir(); + + /** + * Optional. + * + * @return snapshots should be updated automatically without verification + */ + default Optional updateSnapshot() { + return Optional.ofNullable(System.getProperty(JVM_UPDATE_SNAPSHOTS_PARAMETER)); + } + + /** + * Optional Override to supply your own custom serialization function. + * + * @return custom serialization function + */ + SnapshotSerializer getSerializer(); + + /** + * Optional Override to supply your own custom comparator function. + * + * @return custom comparator function + */ + SnapshotComparator getComparator(); + + /** + * Optional Override to supply your own custom reporter functions Reporters will run in the same + * sequence as provided. Reporters should throw exceptions to indicate comparison failure. + * Exceptions thrown from reporters are aggregated and reported together. Reporters that wish to + * leverage IDE comparison tools can use standard assertion libraries like assertj, junit jupiter + * assertions (or) opentest4j. + * + * @return custom reporter functions + */ + List getReporters(); + + /** + * Optional This method is meant to detect if we're running on a CI environment. This is used to + * determine the action to be taken when a snapshot is not found. + * + *

If this method returns false, meaning we're NOT running on a CI environment (probably a dev + * machine), a new snapshot is created when not found. + * + *

If this method returns true, meaning we're running on a CI environment, no new snapshots are + * created and an error is thrown instead to prevent tests from silently passing when snapshots + * are not found. + * + *

Often to determine if running on a CI environment is to check for the presence of a 'CI' env + * variable + * + * @return boolean indicating if we're running on a CI environment or not + */ + boolean isCI(); +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/config/SnapshotConfigInjector.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/config/SnapshotConfigInjector.java new file mode 100644 index 0000000..404be51 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/config/SnapshotConfigInjector.java @@ -0,0 +1,5 @@ +package io.github.finoid.snapshots.config; + +public interface SnapshotConfigInjector { + SnapshotConfig getSnapshotConfig(); +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/LogGithubIssueException.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/LogGithubIssueException.java new file mode 100644 index 0000000..c9481d5 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/LogGithubIssueException.java @@ -0,0 +1,12 @@ +package io.github.finoid.snapshots.exceptions; + +public class LogGithubIssueException extends RuntimeException { + + private static final String LOG_SUPPORT_TICKET = + "\n\n*** This exception should never be thrown ***\n" + + "Log a support ticket at https://github.com/origin-energy/java-snapshot-testing/issues with details of the exception\n"; + + public LogGithubIssueException(String message) { + super(message + LOG_SUPPORT_TICKET); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/MissingSnapshotPropertiesKeyException.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/MissingSnapshotPropertiesKeyException.java new file mode 100644 index 0000000..b477b50 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/MissingSnapshotPropertiesKeyException.java @@ -0,0 +1,8 @@ +package io.github.finoid.snapshots.exceptions; + +public class MissingSnapshotPropertiesKeyException extends RuntimeException { + + public MissingSnapshotPropertiesKeyException(String key) { + super("\"snapshot.properties\" is missing required key=" + key); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/ReservedWordException.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/ReservedWordException.java new file mode 100644 index 0000000..d4247b4 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/ReservedWordException.java @@ -0,0 +1,18 @@ +package io.github.finoid.snapshots.exceptions; + +import java.util.List; +import java.util.stream.Collectors; + +public class ReservedWordException extends RuntimeException { + + public ReservedWordException(String message) { + super(message); + } + + public ReservedWordException(String element, String reservedWord, List reservedWords) { + super( + String.format( + "You cannot use the '%s' character inside '%s'. Reserved characters are %s", + reservedWord, element, reservedWords.stream().collect(Collectors.joining(",")))); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/SnapshotExtensionException.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/SnapshotExtensionException.java new file mode 100644 index 0000000..86043a4 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/SnapshotExtensionException.java @@ -0,0 +1,12 @@ +package io.github.finoid.snapshots.exceptions; + +public class SnapshotExtensionException extends RuntimeException { + + public SnapshotExtensionException(String message) { + super(message); + } + + public SnapshotExtensionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/SnapshotMatchException.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/SnapshotMatchException.java new file mode 100644 index 0000000..78a2a11 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/exceptions/SnapshotMatchException.java @@ -0,0 +1,21 @@ +package io.github.finoid.snapshots.exceptions; + +import org.opentest4j.MultipleFailuresError; + +import java.util.Collections; +import java.util.List; + +public class SnapshotMatchException extends MultipleFailuresError { + + public SnapshotMatchException(String message) { + super(message, Collections.emptyList()); + } + + public SnapshotMatchException(String message, Throwable cause) { + super(message, Collections.singletonList(cause)); + } + + public SnapshotMatchException(String message, List causes) { + super(message, causes); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/logging/LoggingHelper.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/logging/LoggingHelper.java new file mode 100644 index 0000000..8c31a20 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/logging/LoggingHelper.java @@ -0,0 +1,16 @@ +package io.github.finoid.snapshots.logging; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.slf4j.Logger; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class LoggingHelper { + + public static void deprecatedV5(Logger log, String message) { + log.warn( + "\n\n** Deprecation Warning **\nThis feature will be removed in version 5.X\n" + + message + + "\n\n"); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/reporters/PlainTextSnapshotReporter.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/reporters/PlainTextSnapshotReporter.java new file mode 100644 index 0000000..8b11cb4 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/reporters/PlainTextSnapshotReporter.java @@ -0,0 +1,17 @@ +package io.github.finoid.snapshots.reporters; + +import io.github.finoid.snapshots.logging.LoggingHelper; +import lombok.extern.slf4j.Slf4j; + +@Deprecated +@Slf4j +public class PlainTextSnapshotReporter + extends io.github.finoid.snapshots.reporters.v1.PlainTextSnapshotReporter { + + public PlainTextSnapshotReporter() { + super(); + LoggingHelper.deprecatedV5( + log, + "Update to `v1.reporters.io.github.finoid.snapshots.PlainTextSnapshotReporter` in `snapshot.properties`"); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/reporters/SnapshotReporter.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/reporters/SnapshotReporter.java new file mode 100644 index 0000000..30f68fe --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/reporters/SnapshotReporter.java @@ -0,0 +1,10 @@ +package io.github.finoid.snapshots.reporters; + +import io.github.finoid.snapshots.Snapshot; + +public interface SnapshotReporter { + + boolean supportsFormat(String outputFormat); + + void report(Snapshot previous, Snapshot current); +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/reporters/v1/PlainTextSnapshotReporter.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/reporters/v1/PlainTextSnapshotReporter.java new file mode 100644 index 0000000..bfb33b9 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/reporters/v1/PlainTextSnapshotReporter.java @@ -0,0 +1,42 @@ +package io.github.finoid.snapshots.reporters.v1; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.reporters.SnapshotReporter; +import org.assertj.core.util.diff.DiffUtils; +import org.assertj.core.util.diff.Patch; +import org.opentest4j.AssertionFailedError; + +import java.util.Arrays; +import java.util.function.Supplier; + +public class PlainTextSnapshotReporter implements SnapshotReporter { + + @SuppressWarnings("UnnecessaryLambda") // TODO (nw) rewrite + private static final Supplier NO_DIFF_EXCEPTION_SUPPLIER = + () -> + new IllegalStateException( + "No differences found. Potential mismatch between comparator and reporter"); + + public static String getDiffString(Patch patch) { + return patch.getDeltas().stream() + .map(delta -> delta.toString() + "\n") + .reduce(String::concat) + .orElseThrow(NO_DIFF_EXCEPTION_SUPPLIER); + } + + @Override + public boolean supportsFormat(String outputFormat) { + return true; // always true + } + + @Override + public void report(Snapshot previous, Snapshot current) { + Patch patch = + DiffUtils.diff( + Arrays.asList(previous.raw().split("\n")), Arrays.asList(current.raw().split("\n"))); + + String message = "Error on: \n" + current.raw() + "\n\n" + getDiffString(patch); + + throw new AssertionFailedError(message, previous.raw(), current.raw()); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/Base64SnapshotSerializer.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/Base64SnapshotSerializer.java new file mode 100644 index 0000000..b4a4530 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/Base64SnapshotSerializer.java @@ -0,0 +1,20 @@ +package io.github.finoid.snapshots.serializers; + +import io.github.finoid.snapshots.logging.LoggingHelper; +import lombok.extern.slf4j.Slf4j; + +/** + * This Serializer converts a byte[] into a base64 encoded string. If the input is not a byte[] it + * will be converted using `.getBytes(StandardCharsets.UTF_8)` method + */ +@Slf4j +@Deprecated +public class Base64SnapshotSerializer + extends io.github.finoid.snapshots.serializers.v1.Base64SnapshotSerializer { + public Base64SnapshotSerializer() { + super(); + LoggingHelper.deprecatedV5( + log, + "Update to `v1.serializers.io.github.finoid.snapshots.Base64SnapshotSerializer` in `snapshot.properties`"); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/SerializerType.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/SerializerType.java new file mode 100644 index 0000000..780ae0b --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/SerializerType.java @@ -0,0 +1,7 @@ +package io.github.finoid.snapshots.serializers; + +public enum SerializerType { + TEXT, + JSON, + BASE64; +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/SnapshotSerializer.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/SnapshotSerializer.java new file mode 100644 index 0000000..d1ee3e8 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/SnapshotSerializer.java @@ -0,0 +1,11 @@ +package io.github.finoid.snapshots.serializers; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; + +import java.util.function.BiFunction; + +public interface SnapshotSerializer + extends BiFunction { + String getOutputFormat(); +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/ToStringSnapshotSerializer.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/ToStringSnapshotSerializer.java new file mode 100644 index 0000000..5925b06 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/ToStringSnapshotSerializer.java @@ -0,0 +1,21 @@ +package io.github.finoid.snapshots.serializers; + +import io.github.finoid.snapshots.logging.LoggingHelper; +import lombok.extern.slf4j.Slf4j; + +/** + * This Serializer does a snapshot of the {@link Object#toString()} method. + * + *

Will render each toString() on a separate line + */ +@Slf4j +@Deprecated +public class ToStringSnapshotSerializer + extends io.github.finoid.snapshots.serializers.v1.ToStringSnapshotSerializer { + public ToStringSnapshotSerializer() { + super(); + LoggingHelper.deprecatedV5( + log, + "Update to `v1.serializers.io.github.finoid.snapshots.ToStringSnapshotSerializer` in `snapshot.properties`"); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/v1/Base64SnapshotSerializer.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/v1/Base64SnapshotSerializer.java new file mode 100644 index 0000000..85ad1d4 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/v1/Base64SnapshotSerializer.java @@ -0,0 +1,35 @@ +package io.github.finoid.snapshots.serializers.v1; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * This Serializer converts a byte[] into a base64 encoded string. If the input is not a byte[] it + * will be converted using `.getBytes(StandardCharsets.UTF_8)` method + */ +public class Base64SnapshotSerializer implements SnapshotSerializer { + private static final ToStringSnapshotSerializer TO_STRING_SNAPSHOT_SERIALIZER = new ToStringSnapshotSerializer(); + + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + if (object == null) { + TO_STRING_SNAPSHOT_SERIALIZER.apply("", gen); + } + byte[] bytes = + object instanceof byte[] + ? (byte[]) object + : object.toString().getBytes(StandardCharsets.UTF_8); + String encoded = Base64.getEncoder().encodeToString(bytes); + return TO_STRING_SNAPSHOT_SERIALIZER.apply(encoded, gen); + } + + @Override + public String getOutputFormat() { + return SerializerType.BASE64.name(); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/v1/ToStringSnapshotSerializer.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/v1/ToStringSnapshotSerializer.java new file mode 100644 index 0000000..8222850 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/serializers/v1/ToStringSnapshotSerializer.java @@ -0,0 +1,33 @@ +package io.github.finoid.snapshots.serializers.v1; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotFile; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import lombok.extern.slf4j.Slf4j; + +/** + * This Serializer does a snapshot of the {@link Object#toString()} method. + * + *

Will render each toString() on a separate line + */ +@Slf4j +public class ToStringSnapshotSerializer implements SnapshotSerializer { + + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + String body = "[\n" + object.toString() + "\n]"; + if (body.contains(SnapshotFile.SPLIT_STRING)) { + log.warn( + "Found 3 consecutive lines in your snapshot \\n\\n\\n. This sequence is reserved as the snapshot separator - replacing with \\n.\\n.\\n"); + body = body.replaceAll(SnapshotFile.SPLIT_STRING, "\n.\n.\n"); + } + return gen.toSnapshot(body); + } + + @Override + public String getOutputFormat() { + return SerializerType.TEXT.name(); + } +} diff --git a/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/utils/ReflectionUtils.java b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/utils/ReflectionUtils.java new file mode 100644 index 0000000..6571ff8 --- /dev/null +++ b/snapshot-testing-core/src/main/java/io/github/finoid/snapshots/utils/ReflectionUtils.java @@ -0,0 +1,49 @@ +package io.github.finoid.snapshots.utils; + +import lombok.experimental.UtilityClass; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Optional; +import java.util.function.Predicate; + +@UtilityClass +public class ReflectionUtils { + + /** + * Find {@link Field} by given predicate. + * + *

Invoke the given predicate on all fields in the target class, going up the class hierarchy + * to get all declared fields. + * + * @param clazz the target class to analyze + * @param predicate the predicate + * @return the field or empty optional + */ + public static Optional findFieldByPredicate( + final Class clazz, final Predicate predicate) { + Class targetClass = clazz; + + do { + final Field[] fields = targetClass.getDeclaredFields(); + for (final Field field : fields) { + if (!predicate.test(field)) { + continue; + } + return Optional.of(field); + } + targetClass = targetClass.getSuperclass(); + } while (targetClass != null && targetClass != Object.class); + + return Optional.empty(); + } + + public static void makeAccessible(final Field field) { + if ((!Modifier.isPublic(field.getModifiers()) + || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) + && !field.isAccessible()) { + field.setAccessible(true); + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/CustomFolderSnapshotTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/CustomFolderSnapshotTest.java new file mode 100644 index 0000000..4290de6 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/CustomFolderSnapshotTest.java @@ -0,0 +1,64 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class CustomFolderSnapshotTest { + + private static final String OUTPUT_FILE = + "src/test/some-folder/io/github/finoid/snapshots/__snapshots__/CustomFolderSnapshotTest.snap"; + + @BeforeEach + public void beforeEach() throws IOException { + Path path = Paths.get(OUTPUT_FILE); + Files.deleteIfExists(path); + } + + @Test + void shouldBeAbleToChangeSnapshotFolder(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new CustomFolderSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(FakeObject.builder().id("shouldBeAbleToChangeSnapshotFolder").build()); + snapshotVerifier.validateSnapshots(); + + Path path = Paths.get(OUTPUT_FILE); + Assertions.assertThat(Files.exists(path)); + } + + @Test + void shouldBeAbleToChangeSnapshotFolderLegacyTrailginSlash(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new CustomFolderSnapshotConfigLegacy(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(FakeObject.builder().id("shouldBeAbleToChangeSnapshotFolder").build()); + snapshotVerifier.validateSnapshots(); + + Path path = Paths.get(OUTPUT_FILE); + Assertions.assertThat(Files.exists(path)); + } + + public static class CustomFolderSnapshotConfig extends BaseSnapshotConfig { + + @Override + public String getOutputDir() { + return "src/test/some-folder"; + } + } + + public static class CustomFolderSnapshotConfigLegacy extends BaseSnapshotConfig { + + @Override + public String getOutputDir() { + return "src/test/some-folder/"; + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/DebugSnapshotLineEndingsTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/DebugSnapshotLineEndingsTest.java new file mode 100644 index 0000000..0f0db76 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/DebugSnapshotLineEndingsTest.java @@ -0,0 +1,97 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import lombok.SneakyThrows; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class DebugSnapshotLineEndingsTest { + private static final SnapshotConfig CONFIG = + new BaseSnapshotConfig() { + @Override + public SnapshotSerializer getSerializer() { + return new MultiLineSnapshotSerializer(); + } + }; + private static final String DEBUG_FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap.debug"; + private static final String SNAPSHOT_FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap"; + + @BeforeAll + static void beforeAll() { + SnapshotUtils.copyTestSnapshots(); + } + + @SneakyThrows + @BeforeEach + void beforeEach() { + Files.deleteIfExists(Paths.get(DEBUG_FILE_PATH)); + } + + /** + * Scenario: - An existing snapshot file checked out from git will have CR LF line endings on + * Windows OS - A newly created snapshot file will have LF line endings on any OS (see + * MultiLineSnapshotSerializer) Expectation: - As snapshot file content is identical (except for + * line endings), the debug file should not be created. + */ + @DisplayName( + "Debug file should not be created when snapshots match the existing snapshot regardless of line endings") + @Test + void existingSnapshotDifferentLineEndings(TestInfo testInfo) { + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); + assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); + + SnapshotVerifier snapshotVerifier = new SnapshotVerifier(CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(Arrays.asList("a", "b")); + snapshotVerifier.validateSnapshots(); + + assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH)), "Debug file should not be created"); + } + + private static class MultiLineSnapshotSerializer implements SnapshotSerializer { + @Override + public String getOutputFormat() { + return SerializerType.TEXT.name(); + } + + @Override + public Snapshot apply(Object object, SnapshotSerializerContext snapshotSerializerContext) { + Object body = + "[\n" + + Arrays.asList(object).stream() + .flatMap( + o -> { + if (o instanceof Collection) { + return ((Collection) o).stream(); + } + return Stream.of(o); + }) + .map(Object::toString) + .collect(Collectors.joining("\n")) + + "\n]"; + + return Snapshot.builder() + .name( + "io.github.finoid.snapshots.DebugSnapshotLineEndingsTest.existingSnapshotDifferentLineEndings") + .body(body.toString()) + .build(); + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/DebugSnapshotTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/DebugSnapshotTest.java new file mode 100644 index 0000000..366f395 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/DebugSnapshotTest.java @@ -0,0 +1,116 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import io.github.finoid.snapshots.serializers.ToStringSnapshotSerializer; +import lombok.SneakyThrows; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(MockitoExtension.class) +public class DebugSnapshotTest { + + private static final SnapshotConfig DEFAULT_CONFIG = + new BaseSnapshotConfig() { + @Override + public SnapshotSerializer getSerializer() { + return new ToStringSnapshotSerializer(); + } + }; + + private static final String DEBUG_FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/DebugSnapshotTest.snap.debug"; + private static final String SNAPSHOT_FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/DebugSnapshotTest.snap"; + + @BeforeAll + static void beforeAll() { + SnapshotUtils.copyTestSnapshots(); + } + + @SneakyThrows + @BeforeEach + public void beforeEach() { + Files.deleteIfExists(Paths.get(DEBUG_FILE_PATH)); + } + + @DisplayName("Debug file should be created when snapshots don't match") + @Test + void createDebugFile(TestInfo testInfo) { + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); + assertThrows(SnapshotMatchException.class, () -> expect.toMatchSnapshot(new TestObjectBad())); + assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); + } + + @DisplayName("Debug file should be created when snapshots match for a new snapshot") + @Test + void debugFileCreatedNewSnapshot(TestInfo testInfo) { + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); + expect.toMatchSnapshot(new TestObjectGood()); + assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); + } + + @DisplayName("Debug file should be created when snapshots match for an existing snapshot") + @Test + void debugFileCreatedExistingSnapshot(TestInfo testInfo) { + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); + expect.toMatchSnapshot(new TestObjectGood()); + assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); + } + + @SneakyThrows + @DisplayName("Existing debug file should not be deleted once snapshots match") + @Test + void debugFileCreatedSnapshotMatch(TestInfo testInfo) { + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); + Files.createFile(Paths.get(DEBUG_FILE_PATH)); + assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(new TestObjectGood()); + assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); + } + + private static class TestObjectBad { + @Override + public String toString() { + return "Bad Snapshot"; + } + } + + private static class TestObjectGood { + @Override + public String toString() { + return "Good Snapshot"; + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/EmptySnapshotFileTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/EmptySnapshotFileTest.java new file mode 100644 index 0000000..9c674ab --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/EmptySnapshotFileTest.java @@ -0,0 +1,63 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.ToStringSnapshotConfig; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class EmptySnapshotFileTest { + + private static final String SNAPSHOT_FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/EmptySnapshotFileTest.snap"; + private static final String DEBUG_FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/EmptySnapshotFileTest.snap.debug"; + + @BeforeAll + static void beforeAll() { + SnapshotUtils.copyTestSnapshots(); + } + + @DisplayName("Should remove empty snapshots") + @Test + public void shouldRemoveEmptySnapshots(TestInfo testInfo) { + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); + assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new ToStringSnapshotConfig(), testInfo.getTestClass().get()); + snapshotVerifier.validateSnapshots(); + + assertTrue(Files.notExists(Paths.get(SNAPSHOT_FILE_PATH))); + assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); + } + + @Nested + public class NestedClass { + + private static final String SNAPSHOT_FILE_PATH_NESTED = + "src/test/java/io/github/finoid/snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap"; + private static final String DEBUG_FILE_PATH_NESTED = + "src/test/java/io/github/finoid/snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap.debug"; + + @DisplayName("Should remove empty nested snapshots") + @Test + public void shouldRemoveEmptyNestedSnapshots(TestInfo testInfo) { + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH_NESTED))); + assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH_NESTED))); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new ToStringSnapshotConfig(), testInfo.getTestClass().get()); + snapshotVerifier.validateSnapshots(); + + assertTrue(Files.notExists(Paths.get(SNAPSHOT_FILE_PATH_NESTED))); + assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH_NESTED))); + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/EqualDebugSnapshotFileTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/EqualDebugSnapshotFileTest.java new file mode 100644 index 0000000..3b251b9 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/EqualDebugSnapshotFileTest.java @@ -0,0 +1,39 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.ToStringSnapshotConfig; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class EqualDebugSnapshotFileTest { + + private static final String SNAPSHOT_FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap"; + private static final String DEBUG_FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap.debug"; + + @BeforeAll + static void beforeAll() { + SnapshotUtils.copyTestSnapshots(); + } + + @DisplayName("Should remove equal debug snapshots") + @Test + public void shouldRemoveEmptySnapshots(TestInfo testInfo) { + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); + assertTrue(Files.exists(Paths.get(DEBUG_FILE_PATH))); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new ToStringSnapshotConfig(), testInfo.getTestClass().get()); + snapshotVerifier.validateSnapshots(); + + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); + assertTrue(Files.notExists(Paths.get(DEBUG_FILE_PATH))); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/FakeObject.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/FakeObject.java new file mode 100644 index 0000000..0c62841 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/FakeObject.java @@ -0,0 +1,34 @@ +package io.github.finoid.snapshots; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.util.List; + +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FakeObject { + + private String id; + + private Integer value; + + private String name; + + @Setter + private FakeObject fakeObject; + + public void fakeMethod(String fakeName, Long fakeNumber, List fakeList) { + } + + public void fakeMethodWithComplexObject(Object fakeObj) { + } + + public void fakeMethodWithComplexFakeObject(FakeObject fakeObj) { + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/NoNameChangeTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/NoNameChangeTest.java new file mode 100644 index 0000000..00529ec --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/NoNameChangeTest.java @@ -0,0 +1,40 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.comparators.PlainTextEqualsComparator; +import io.github.finoid.snapshots.reporters.PlainTextSnapshotReporter; +import io.github.finoid.snapshots.serializers.Base64SnapshotSerializer; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.ToStringSnapshotSerializer; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * These classes are likely defined in snapshot.properties as a string. + * + *

The clients IDE will not complain if they change so ensure they don't + */ +public class NoNameChangeTest { + + @Test + public void serializersApiShouldNotChange() { + assertThat(Base64SnapshotSerializer.class.getName()) + .isEqualTo("io.github.finoid.snapshots.serializers.Base64SnapshotSerializer"); + assertThat(ToStringSnapshotSerializer.class.getName()) + .isEqualTo("io.github.finoid.snapshots.serializers.ToStringSnapshotSerializer"); + assertThat(SerializerType.class.getName()) + .isEqualTo("io.github.finoid.snapshots.serializers.SerializerType"); + } + + @Test + public void reportersApiShouldNotChange() { + assertThat(PlainTextSnapshotReporter.class.getName()) + .isEqualTo("io.github.finoid.snapshots.reporters.PlainTextSnapshotReporter"); + } + + @Test + public void comparatorsApiShouldNotChange() { + assertThat(PlainTextEqualsComparator.class.getName()) + .isEqualTo("io.github.finoid.snapshots.comparators.PlainTextEqualsComparator"); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/OnLoadSnapshotFileTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/OnLoadSnapshotFileTest.java new file mode 100644 index 0000000..43b5d3a --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/OnLoadSnapshotFileTest.java @@ -0,0 +1,73 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.serializers.ToStringSnapshotSerializer; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class OnLoadSnapshotFileTest { + + private static final String SNAPSHOT_FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/OnLoadSnapshotFileTest.snap"; + + private static final SnapshotConfig CUSTOM_SNAPSHOT_CONFIG = new BaseSnapshotConfig(); + + @BeforeAll + static void beforeAll() throws IOException { + Files.deleteIfExists(Paths.get(SNAPSHOT_FILE_PATH)); + String snapshotFileContent = + "io.github.finoid.snapshots.OnLoadSnapshotFileTest.shouldLoadFileWithCorrectEncodingForCompare=[\n" + + "any special characters that need correct encoding äöüèéàè\n" + + "]"; + createSnapshotFile(snapshotFileContent); + } + + private static void createSnapshotFile(String snapshot) { + try { + File file = new File(SNAPSHOT_FILE_PATH); + file.getParentFile().mkdirs(); + file.createNewFile(); + try (FileOutputStream fileStream = new FileOutputStream(file, false)) { + fileStream.write(snapshot.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException("Unable to write debug file ", e); + } + } catch (IOException e) { + throw new RuntimeException("Unable to write debug file ", e); + } + } + + @DisplayName("Should load snapshots with correct encoding") + @Test + public void shouldLoadFileWithCorrectEncodingForCompare(TestInfo testInfo) throws IOException { + assertTrue(Files.exists(Paths.get(SNAPSHOT_FILE_PATH))); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(CUSTOM_SNAPSHOT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect + .serializer(ToStringSnapshotSerializer.class) + .toMatchSnapshot("any special characters that need correct encoding äöüèéàè"); + snapshotVerifier.validateSnapshots(); + + File f = new File(SNAPSHOT_FILE_PATH); + assertThat(String.join("\n", Files.readAllLines(f.toPath()))) + .isEqualTo( + "io.github.finoid.snapshots.OnLoadSnapshotFileTest.shouldLoadFileWithCorrectEncodingForCompare=[\n" + + "any special characters that need correct encoding äöüèéàè\n" + + "]"); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/OrphanSnapshotTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/OrphanSnapshotTest.java new file mode 100644 index 0000000..d2bbb15 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/OrphanSnapshotTest.java @@ -0,0 +1,76 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class OrphanSnapshotTest { + + private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); + + @BeforeAll + static void beforeAll() { + SnapshotUtils.copyTestSnapshots(); + } + + @DisplayName("should fail the build when failOnOrphans=true") + @Test + void orphanSnapshotsShouldFailTheBuild(TestInfo testInfo) throws IOException { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get(), true); + FakeObject fakeObject1 = FakeObject.builder().id("anyId1").value(1).name("anyName1").build(); + final Path snapshotFile = + Paths.get("src/test/java/io/github/finoid/snapshots/__snapshots__/OrphanSnapshotTest.snap"); + + long bytesBefore = Files.size(snapshotFile); + + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(fakeObject1); + + Throwable exceptionThatWasThrown = + assertThrows( + SnapshotMatchException.class, + () -> { + snapshotVerifier.validateSnapshots(); + }); + + assertThat(exceptionThatWasThrown.getMessage()).isEqualTo("ERROR: Found orphan snapshots"); + + // Ensure file has not changed + long bytesAfter = Files.size(snapshotFile); + assertThat(bytesAfter).isGreaterThan(bytesBefore); + } + + @DisplayName("should not fail the build when failOnOrphans=false") + @Test + void orphanSnapshotsShouldNotFailTheBuild(TestInfo testInfo) throws IOException { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get(), false); + FakeObject fakeObject1 = FakeObject.builder().id("anyId1").value(1).name("anyName1").build(); + final Path snapshotFile = + Paths.get("src/test/java/io/github/finoid/snapshots/__snapshots__/OrphanSnapshotTest.snap"); + + long bytesBefore = Files.size(snapshotFile); + + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(fakeObject1); + + snapshotVerifier.validateSnapshots(); + + // Ensure file has not changed + long bytesAfter = Files.size(snapshotFile); + assertThat(bytesAfter).isGreaterThan(bytesBefore); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/PrivateCalledMethodTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/PrivateCalledMethodTest.java new file mode 100644 index 0000000..542c11a --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/PrivateCalledMethodTest.java @@ -0,0 +1,24 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +class PrivateCalledMethodTest { + + @Test + void testName(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + testBasedOnArgs("testContent", testInfo); + snapshotVerifier.validateSnapshots(); + } + + private void testBasedOnArgs(String arg, TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(arg); + snapshotVerifier.validateSnapshots(); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/ReflectionUtilities.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/ReflectionUtilities.java new file mode 100644 index 0000000..82cc559 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/ReflectionUtilities.java @@ -0,0 +1,34 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.stream.Stream; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ReflectionUtilities { + + // FIXME consider guava reflection instead + public static Method getMethod(Class clazz, String methodName) { + try { + return Stream.of(clazz.getDeclaredMethods()) + .filter(method -> method.getName().equals(methodName)) + .findFirst() + .orElseThrow(() -> new NoSuchMethodException("Not Found")); + } catch (NoSuchMethodException e) { + return Optional.ofNullable(clazz.getSuperclass()) + .map(superclass -> getMethod(superclass, methodName)) + .orElseThrow( + () -> + new SnapshotMatchException( + "Could not find method " + + methodName + + " on class " + + clazz + + "\nPlease annotate your test method with @Test and make it without any parameters!")); + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/ScenarioTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/ScenarioTest.java new file mode 100644 index 0000000..149e5d4 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/ScenarioTest.java @@ -0,0 +1,56 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ScenarioTest { + + private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); + + @Test + void canTakeMultipleSnapshotsUsingScenario(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("Default Snapshot"); + expect.scenario("additional").toMatchSnapshot("Additional Snapshot"); + snapshotVerifier.validateSnapshots(); + } + + @Test + void canTakeTheSameSnapshotTwice(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("Default Snapshot"); + expect.toMatchSnapshot("Default Snapshot"); + expect.scenario("scenario").toMatchSnapshot("Scenario Snapshot"); + expect.scenario("scenario").toMatchSnapshot("Scenario Snapshot"); + snapshotVerifier.validateSnapshots(); + } + + @Test + void cannotTakeDifferentSnapshotsAtDefaultLevel(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("Default Snapshot"); + assertThrows(SnapshotMatchException.class, () -> expect.toMatchSnapshot("Default Snapshot 2")); + } + + @Test + void cannotTakeDifferentSnapshotsAtScenarioLevel(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.scenario("scenario").toMatchSnapshot("Default Snapshot"); + assertThrows( + SnapshotMatchException.class, + () -> expect.scenario("scenario").toMatchSnapshot("Default Snapshot 2")); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotCaptor.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotCaptor.java new file mode 100644 index 0000000..bd70a18 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotCaptor.java @@ -0,0 +1,105 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.exceptions.SnapshotExtensionException; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; + +class SnapshotCaptor { + + private Class parameterClass; + + private Class argumentClass; + + private String[] ignore; + + public SnapshotCaptor(Class parameterClass, String... ignore) { + this.parameterClass = parameterClass; + this.argumentClass = parameterClass; + this.ignore = ignore; + } + + public SnapshotCaptor(Class parameterClass, Class argumentClass, String... ignore) { + this.parameterClass = parameterClass; + this.argumentClass = argumentClass; + this.ignore = ignore; + } + + public Class getParameterClass() { + return parameterClass; + } + + public Object removeIgnored(Object value) { + Object newValue = value; + if (ignore != null && ignore.length > 0) { + newValue = shallowCopy(value); + for (String each : ignore) { + try { + Field field = this.argumentClass.getDeclaredField(each); + field.setAccessible(true); + if (field.getType().isPrimitive()) { + field.setByte(newValue, Integer.valueOf(0).byteValue()); + } else { + field.set(newValue, null); + } + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new SnapshotExtensionException("Invalid Ignore value " + each, e.getCause()); + } + } + } + return newValue; + } + + private Object shallowCopy(Object value) { + try { + Object newValue = constructCopy(this.argumentClass); + + Field[] fields = this.argumentClass.getDeclaredFields(); + + for (Field field : fields) { + field.setAccessible(true); + try { + field.set(newValue, field.get(value)); + } catch (Exception e) { + // ignore + } + } + return newValue; + } catch (Exception e) { + throw new SnapshotExtensionException( + "Class " + + this.argumentClass.getSimpleName() + + " must have a default empty constructor!"); + } + } + + private Object constructCopy(Class argumentClass) + throws InstantiationException, IllegalAccessException, + java.lang.reflect.InvocationTargetException, NoSuchMethodException { + + try { + return argumentClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + // Ignore - should log + } + + Constructor[] constructors = argumentClass.getDeclaredConstructors(); + + if (constructors.length == 0) { + return argumentClass.getDeclaredConstructor().newInstance(); + } + + int i = 0; + Class[] types = constructors[i].getParameterTypes(); + Object[] paramValues = new Object[types.length]; + + for (int j = 0; j < types.length; j++) { + if (types[j].isPrimitive()) { + paramValues[j] = Integer.valueOf(0).byteValue(); + } else { + paramValues[j] = constructCopy(types[j]); + } + } + return constructors[i].newInstance(paramValues); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotContextTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotContextTest.java new file mode 100644 index 0000000..f17786b --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotContextTest.java @@ -0,0 +1,231 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import io.github.finoid.snapshots.reporters.SnapshotReporter; +import io.github.finoid.snapshots.serializers.ToStringSnapshotSerializer; +import lombok.SneakyThrows; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opentest4j.AssertionFailedError; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(MockitoExtension.class) +class SnapshotContextTest { + + private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); + private static final String FILE_PATH = "src/test/java/anyFilePath"; + private static final String SNAPSHOT_NAME = "java.lang.String.toString"; + private static final String SNAPSHOT = "java.lang.String.toString=[\nanyObject\n]"; + + private SnapshotFile snapshotFile; + + private SnapshotContext snapshotContext; + + @BeforeEach + void setUp() throws NoSuchMethodException, IOException { + snapshotFile = + new SnapshotFile(DEFAULT_CONFIG.getOutputDir(), "anyFilePath", SnapshotContextTest.class); + snapshotContext = + new SnapshotContext( + DEFAULT_CONFIG, + snapshotFile, + String.class, + String.class.getDeclaredMethod("toString"), + "anyObject"); + } + + @AfterEach + void tearDown() throws IOException { + Files.deleteIfExists(Paths.get(FILE_PATH)); + } + + @Test + void shouldGetSnapshotNameSuccessfully() { + String snapshotName = snapshotContext.resolveSnapshotIdentifier(); + assertThat(snapshotName).isEqualTo(SNAPSHOT_NAME); + } + + @Test + void shouldMatchSnapshotSuccessfully() { + snapshotContext.toMatchSnapshot(); + assertThat(snapshotFile.getSnapshots().stream().findFirst().get().raw()).isEqualTo(SNAPSHOT); + } + + @Test + void shouldMatchSnapshotWithException() { + snapshotFile.pushSnapshot(Snapshot.parse(SNAPSHOT_NAME + "=anyWrongSnapshot")); + assertThrows(SnapshotMatchException.class, snapshotContext::toMatchSnapshot); + } + + @SneakyThrows + @Test + void shouldRenderScenarioNameWhenSupplied() { + SnapshotContext snapshotContextWithScenario = + new SnapshotContext( + DEFAULT_CONFIG, + snapshotFile, + String.class, + String.class.getDeclaredMethod("toString"), + "anyObject"); + snapshotContextWithScenario.setScenario("hello world"); + assertThat(snapshotContextWithScenario.resolveSnapshotIdentifier()) + .isEqualTo("java.lang.String.toString[hello world]"); + } + + @SneakyThrows + @Test + void shouldNotRenderScenarioNameWhenNull() { + SnapshotContext snapshotContextWithoutScenario = + new SnapshotContext( + DEFAULT_CONFIG, + snapshotFile, + String.class, + String.class.getDeclaredMethod("toString"), + "anyObject"); + assertThat(snapshotContextWithoutScenario.resolveSnapshotIdentifier()) + .isEqualTo("java.lang.String.toString"); + } + + @SneakyThrows + @Test + void shouldOverwriteSnapshotsWhenParamIsPassed() { + SnapshotConfig mockConfig = Mockito.mock(SnapshotConfig.class); + Mockito.when(mockConfig.updateSnapshot()).thenReturn(Optional.of("")); + Mockito.when(mockConfig.getSerializer()).thenReturn(new ToStringSnapshotSerializer()); + SnapshotFile snapshotFile = Mockito.mock(SnapshotFile.class); + Set set = new HashSet<>(); + set.add(Snapshot.parse("java.lang.String.toString[hello world]=[{" + "\"a\": \"b\"" + "}]")); + Mockito.when(snapshotFile.getSnapshots()).thenReturn(set); + + SnapshotContext snapshotContext = + new SnapshotContext( + mockConfig, + snapshotFile, + String.class, + String.class.getDeclaredMethod("toString"), + "anyObject"); + snapshotContext.setScenario("hello world"); + snapshotContext.toMatchSnapshot(); + Mockito.verify(snapshotFile) + .pushSnapshot(Snapshot.parse("java.lang.String.toString[hello world]=[\nanyObject\n]")); + } + + @SneakyThrows + @Test + void shouldFailWhenRunningOnCiWithoutExistingSnapshot() { + BaseSnapshotConfig ciSnapshotConfig = + new BaseSnapshotConfig() { + @Override + public boolean isCI() { + return true; + } + }; + + SnapshotContext ciSnapshotContext = + new SnapshotContext( + ciSnapshotConfig, + new SnapshotFile(ciSnapshotConfig.getOutputDir(), "blah", SnapshotContextTest.class), + String.class, + String.class.getDeclaredMethod("toString"), + "anyObject"); + + Assertions.assertThatThrownBy(ciSnapshotContext::toMatchSnapshot) + .hasMessage( + "Snapshot [java.lang.String.toString] not found. Has this snapshot been committed ?"); + } + + @SneakyThrows + @Test + void shouldAggregateMultipleFailures() { + SnapshotFile snapshotFile = Mockito.mock(SnapshotFile.class); + Set set = new HashSet<>(); + set.add(Snapshot.parse("java.lang.String.toString=[\n \"hello\"\n]")); + Mockito.when(snapshotFile.getSnapshots()).thenReturn(set); + + Stream> reportingFunctions = + Stream.of( + (rawSnapshot, currentObject) -> + assertThat(currentObject).isEqualTo(rawSnapshot), // assertj + org.junit.jupiter.api.Assertions::assertEquals, // junit jupiter + (previous, current) -> { + String message = + String.join( + System.lineSeparator(), + "Expected : ", + previous.raw(), + "Actual : ", + current.raw()); + throw new AssertionFailedError(message, previous, current); // opentest4j + }); + + Stream reporters = + reportingFunctions.map( + consumer -> + new SnapshotReporter() { + @Override + public boolean supportsFormat(String outputFormat) { + return true; + } + + @Override + public void report(Snapshot previous, Snapshot current) { + consumer.accept(previous, current); + } + }); + + SnapshotContext failingSnapshotContext = + new SnapshotContext( + DEFAULT_CONFIG, + snapshotFile, + String.class, + String.class.getDeclaredMethod("toString"), + "hola"); + failingSnapshotContext.setSnapshotSerializer(new ToStringSnapshotSerializer()); + failingSnapshotContext.setSnapshotReporters(reporters.collect(Collectors.toList())); + + try { + failingSnapshotContext.toMatchSnapshot(); + } catch (Throwable m) { + String cleanMessage = + m.getMessage() + .replace("<\"", "") + .replace("<", "") + .replaceAll("\n", "") + .replaceAll("\r", "") + .replaceAll("\t", "") + .replace("\">", " ") + .replace(">", " ") + .replace("]", "") + .replace("java.lang.String.toString=[", "") + .replaceAll(" +", " "); + + assertThat(cleanMessage) + .containsPattern("Expecting.*hola.*to be equal to.*hello.*but was not"); // assertj + assertThat(cleanMessage).containsPattern("expected.*hello.*but was.*hola"); // junit jupiter + assertThat(cleanMessage).containsPattern("Expected.*hello.*Actual.*hola"); // opentest4j + + return; + } + + Assertions.fail("Expected an error to be thrown"); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotHeaders.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotHeaders.java new file mode 100644 index 0000000..696bb8e --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotHeaders.java @@ -0,0 +1,60 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.serializers.LowercaseToStringSerializer; +import io.github.finoid.snapshots.serializers.SerializerType; +import lombok.NoArgsConstructor; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +@ExtendWith(MockitoExtension.class) +public class SnapshotHeaders { + + private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); + + static SnapshotVerifier snapshotVerifier; + + @BeforeAll + static void beforeAll() { + SnapshotUtils.copyTestSnapshots(); + snapshotVerifier = new SnapshotVerifier(DEFAULT_CONFIG, SnapshotHeaders.class); + } + + @AfterAll + static void afterAll() { + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldBeAbleToSnapshotASingleCustomHeader(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.serializer(CustomHeadersSerializer.class).toMatchSnapshot("Hello World"); + } + + @Test + void shouldBeAbleToSnapshotMultipleCustomHeader(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.serializer(CustomHeadersSerializer.class).toMatchSnapshot("Hello World"); + } + + @NoArgsConstructor + private static class CustomHeadersSerializer extends LowercaseToStringSerializer { + @Override + public String getOutputFormat() { + return SerializerType.JSON.name(); + } + + @Override + public Snapshot apply(Object object, SnapshotSerializerContext snapshotSerializerContext) { + snapshotSerializerContext.getHeader().put("custom", "anything"); + snapshotSerializerContext.getHeader().put("custom2", "anything2"); + return super.apply(object, snapshotSerializerContext); + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotIntegrationTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotIntegrationTest.java new file mode 100644 index 0000000..86ce81c --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotIntegrationTest.java @@ -0,0 +1,92 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import io.github.finoid.snapshots.serializers.UppercaseToStringSerializer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +@ExtendWith(MockitoExtension.class) +public class SnapshotIntegrationTest { + private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); + static SnapshotVerifier snapshotVerifier; + + @BeforeAll + static void beforeAll() { + SnapshotUtils.copyTestSnapshots(); + snapshotVerifier = new SnapshotVerifier(DEFAULT_CONFIG, SnapshotIntegrationTest.class); + } + + @AfterAll + static void afterAll() { + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldMatchSnapshotOne(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(FakeObject.builder().id("anyId1").value(1).name("anyName1").build()); + } + + @Test + void shouldMatchSnapshotTwo(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(FakeObject.builder().id("anyId2").value(2).name("anyName2").build()); + } + + @Test + void shouldMatchSnapshotThree(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(FakeObject.builder().id("anyId3").value(3).name("anyName3").build()); + } + + @Test + void shouldMatchSnapshotFour(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot( + FakeObject.builder().id("anyId4").value(4).name("any\n\n\nName4").build()); + } + + @Test + void shouldMatchSnapshotInsidePrivateMethod(TestInfo testInfo) { + matchInsidePrivate(testInfo); + } + + @Test + void shouldThrowSnapshotMatchException(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertThrows( + SnapshotMatchException.class, + () -> + expect.toMatchSnapshot( + FakeObject.builder().id("anyId5").value(6).name("anyName5").build()), + "Error on: \n" + + "io.github.finoid.snapshots.SnapshotIntegrationTest.shouldThrowSnapshotMatchException=["); + } + + @Test + void shouldSnapshotUsingSerializerClass(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.serializer(UppercaseToStringSerializer.class).toMatchSnapshot("Hello World"); + } + + @Test + void shouldSnapshotUsingSerializerPropertyName(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.serializer("lowercase").toMatchSnapshot("Hello World"); + } + + private void matchInsidePrivate(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot( + FakeObject.builder().id("anyPrivate").value(5).name("anyPrivate").build()); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotMatcherScenarioTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotMatcherScenarioTest.java new file mode 100644 index 0000000..f612c2c --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotMatcherScenarioTest.java @@ -0,0 +1,67 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +@ExtendWith(MockitoExtension.class) +class SnapshotMatcherScenarioTest { + + private static final String FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/SnapshotMatcherScenarioTest.snap"; + + static SnapshotVerifier snapshotVerifier; + + @BeforeAll + static void beforeAll() { + snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), SnapshotMatcherScenarioTest.class); + } + + @AfterAll + static void afterAll() throws IOException { + snapshotVerifier.validateSnapshots(); + File f = new File(FILE_PATH); + assertThat(String.join("\n", Files.readAllLines(f.toPath()))) + .isEqualTo( + "io.github.finoid.snapshots.SnapshotMatcherScenarioTest.should1ShowSnapshotSuccessfully[Scenario A]=[\n" + + "any type of object\n" + + "]\n\n\n" + + "io.github.finoid.snapshots.SnapshotMatcherScenarioTest.should2SecondSnapshotExecutionSuccessfully[Scenario B]=[\n" + + "any second type of object\n" + + "]"); + Files.delete(Paths.get(FILE_PATH)); + } + + @Test + void should1ShowSnapshotSuccessfully(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.scenario("Scenario A").toMatchSnapshot("any type of object"); + File f = new File(FILE_PATH); + if (!f.exists() || f.isDirectory()) { + throw new RuntimeException("File should exist here"); + } + } + + @Test + void should2SecondSnapshotExecutionSuccessfully(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.scenario("Scenario B").toMatchSnapshot("any second type of object"); + File f = new File(FILE_PATH); + if (!f.exists() || f.isDirectory()) { + throw new RuntimeException("File should exist here"); + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotMatcherTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotMatcherTest.java new file mode 100644 index 0000000..57d41ab --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotMatcherTest.java @@ -0,0 +1,64 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +@ExtendWith(MockitoExtension.class) +class SnapshotMatcherTest { + private static final String FILE_PATH = + "src/test/java/io/github/finoid/snapshots/__snapshots__/SnapshotMatcherTest.snap"; + static SnapshotVerifier snapshotVerifier; + + @BeforeAll + static void beforeAll() { + snapshotVerifier = new SnapshotVerifier(new BaseSnapshotConfig(), SnapshotMatcherTest.class); + } + + @AfterAll + static void afterAll() throws IOException { + snapshotVerifier.validateSnapshots(); + File f = new File(FILE_PATH); + assertThat(String.join("\n", Files.readAllLines(f.toPath()))) + .isEqualTo( + "io.github.finoid.snapshots.SnapshotMatcherTest.should1ShowSnapshotSuccessfully=[\n" + + "any type of object\n" + + "]\n\n\n" + + "io.github.finoid.snapshots.SnapshotMatcherTest.should2SecondSnapshotExecutionSuccessfully=[\n" + + "any second type of object\n" + + "]"); + Files.delete(Paths.get(FILE_PATH)); + } + + @Test + void should1ShowSnapshotSuccessfully(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("any type of object"); + File f = new File(FILE_PATH); + if (!f.exists() || f.isDirectory()) { + throw new RuntimeException("File should exist here"); + } + } + + @Test + void should2SecondSnapshotExecutionSuccessfully(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("any second type of object"); + File f = new File(FILE_PATH); + if (!f.exists() || f.isDirectory()) { + throw new RuntimeException("File should exist here"); + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotNameAnnotationTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotNameAnnotationTest.java new file mode 100644 index 0000000..f9e52c2 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotNameAnnotationTest.java @@ -0,0 +1,95 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.annotations.SnapshotName; +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.exceptions.ReservedWordException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class SnapshotNameAnnotationTest { + + @BeforeEach + void beforeEach() { + SnapshotUtils.copyTestSnapshots(); + } + + @SnapshotName("can_use_snapshot_name") + @Test + void canUseSnapshotNameAnnotation(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("Hello World"); + snapshotVerifier.validateSnapshots(); + } + + @SnapshotName("can use snapshot name with spaces") + @Test + void canUseSnapshotNameAnnotationWithSpaces(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("Hello World"); + snapshotVerifier.validateSnapshots(); + } + + @SnapshotName("can't use '=' character in snapshot name") + @Test + void cannotUseEqualsInsideSnapshotName(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertThrows(ReservedWordException.class, () -> expect.toMatchSnapshot("FooBar")); + } + + @SnapshotName("can't use '[' character in snapshot name") + @Test + void cannotUseOpeningSquareBracketInsideSnapshotName(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertThrows(ReservedWordException.class, () -> expect.toMatchSnapshot("FooBar")); + } + + @SnapshotName("can't use ']' character in snapshot name") + @Test + void cannotUseClosingSquareBracketInsideSnapshotName(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertThrows(ReservedWordException.class, () -> expect.toMatchSnapshot("FooBar")); + } + + @Test + void cannotUseEqualsInsideScenarioName(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertThrows( + ReservedWordException.class, + () -> expect.scenario("can't use = symbol in scenario").toMatchSnapshot("FooBar")); + } + + @Test + void cannotUseOpeningSquareBracketInsideScenarioName(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertThrows( + ReservedWordException.class, + () -> expect.scenario("can't use [ symbol in scenario").toMatchSnapshot("FooBar")); + } + + @Test + void cannotUseClosingSquareBracketInsideScenarioName(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertThrows( + ReservedWordException.class, + () -> expect.scenario("can't use ] symbol in scenario").toMatchSnapshot("FooBar")); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotNameAnnotationWithDuplicatesTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotNameAnnotationWithDuplicatesTest.java new file mode 100644 index 0000000..2bb9731 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotNameAnnotationWithDuplicatesTest.java @@ -0,0 +1,26 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.annotations.SnapshotName; +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.exceptions.SnapshotExtensionException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class SnapshotNameAnnotationWithDuplicatesTest { + + @SnapshotName("hello_world") + @Test + void canUseSnapshotNameAnnotation(TestInfo testInfo) { + assertThrows( + SnapshotExtensionException.class, + () -> new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()), + "Oops, looks like you set the same name of two separate snapshots @SnapshotName(\"hello_world\") in " + + "class io.github.finoid.snapshots.SnapshotNameAnnotationTest"); + } + + @SnapshotName("hello_world") + private void anotherMethodWithSameSnapshotName() { + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotOverrideClassTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotOverrideClassTest.java new file mode 100644 index 0000000..bca77b1 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotOverrideClassTest.java @@ -0,0 +1,24 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public class SnapshotOverrideClassTest extends SnapshotSuperClassTest { + + @BeforeEach + void beforeEach() { + snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), SnapshotOverrideClassTest.class); + } + + @AfterEach + void afterEach() { + snapshotVerifier.validateSnapshots(); + } + + @Override + public String getName() { + return "anyName"; + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotSuperClassTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotSuperClassTest.java new file mode 100644 index 0000000..6f113bd --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotSuperClassTest.java @@ -0,0 +1,20 @@ +package io.github.finoid.snapshots; + +import lombok.Getter; +import lombok.Setter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +public abstract class SnapshotSuperClassTest { + + @Getter + @Setter + static SnapshotVerifier snapshotVerifier; + + public abstract String getName(); + + @Test + void shouldMatchSnapshotOne(TestInfo testInfo) { + Expect.of(snapshotVerifier, testInfo.getTestMethod().get()).toMatchSnapshot(getName()); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotTest.java new file mode 100644 index 0000000..66b9b93 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotTest.java @@ -0,0 +1,94 @@ +package io.github.finoid.snapshots; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +class SnapshotTest { + + @Test + public void shouldParseSnapshot() { + Snapshot snapshot = + Snapshot.parse( + Snapshot.builder().name("io.github.finoid.snapshots.Test").body("body").build().raw()); + assertThat(snapshot.getIdentifier()).isEqualTo("io.github.finoid.snapshots.Test"); + assertThat(snapshot.getName()).isEqualTo("io.github.finoid.snapshots.Test"); + assertThat(snapshot.getHeader()).isEmpty(); + assertThat(snapshot.getScenario()).isBlank(); + assertThat(snapshot.getBody()).isEqualTo("body"); + } + + @Test + public void shouldParseSnapshotWithHeaders() { + SnapshotHeader header = new SnapshotHeader(); + header.put("header1", "value1"); + Snapshot snapshot = + Snapshot.parse( + Snapshot.builder() + .name("io.github.finoid.snapshots.Test") + .header(header) + .body("body") + .build() + .raw()); + assertThat(snapshot.getIdentifier()).isEqualTo("io.github.finoid.snapshots.Test"); + assertThat(snapshot.getName()).isEqualTo("io.github.finoid.snapshots.Test"); + assertThat(snapshot.getHeader()).containsExactly(entry("header1", "value1")); + assertThat(snapshot.getScenario()).isBlank(); + assertThat(snapshot.getBody()).isEqualTo("body"); + } + + @Test + public void shouldParseSnapshotWithScenario() { + Snapshot snapshot = + Snapshot.parse( + Snapshot.builder() + .name("io.github.finoid.snapshots.Test") + .scenario("scenario") + .body("body") + .build() + .raw()); + assertThat(snapshot.getIdentifier()).isEqualTo("io.github.finoid.snapshots.Test[scenario]"); + assertThat(snapshot.getName()).isEqualTo("io.github.finoid.snapshots.Test"); + assertThat(snapshot.getHeader()).isEmpty(); + assertThat(snapshot.getScenario()).isEqualTo("scenario"); + assertThat(snapshot.getBody()).isEqualTo("body"); + } + + @Test + public void shouldParseSnapshotWithScenarioAndHeaders() { + SnapshotHeader header = new SnapshotHeader(); + header.put("header1", "value1"); + Snapshot snapshot = + Snapshot.parse( + Snapshot.builder() + .name("io.github.finoid.snapshots.Test") + .scenario("scenario") + .header(header) + .body("body") + .build() + .raw()); + assertThat(snapshot.getIdentifier()).isEqualTo("io.github.finoid.snapshots.Test[scenario]"); + assertThat(snapshot.getName()).isEqualTo("io.github.finoid.snapshots.Test"); + assertThat(snapshot.getHeader()).containsExactly(entry("header1", "value1")); + assertThat(snapshot.getScenario()).isEqualTo("scenario"); + assertThat(snapshot.getBody()).isEqualTo("body"); + } + + @Test + public void shouldParseSnapshotWithScenarioAndBodyWithSomethingSimilarToAnScenarioToConfuseRegex() { + Snapshot snapshot = + Snapshot.parse( + Snapshot.builder() + .name("io.github.finoid.snapshots.Test") + .scenario("scenario") + .body("[xxx]=yyy") + .build() + .raw()); + assertThat(snapshot.getIdentifier()).isEqualTo("io.github.finoid.snapshots.Test[scenario]"); + assertThat(snapshot.getName()).isEqualTo("io.github.finoid.snapshots.Test"); + assertThat(snapshot.getHeader()).isEmpty(); + assertThat(snapshot.getScenario()).isEqualTo("scenario"); + assertThat(snapshot.getBody()).isEqualTo("[xxx]=yyy"); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotUtils.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotUtils.java new file mode 100644 index 0000000..80c1eae --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotUtils.java @@ -0,0 +1,109 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.io.FileUtils; +import org.mockito.ArgumentCaptor; + +import java.io.IOException; +import java.lang.reflect.Parameter; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SnapshotUtils { + + public static Map>> extractArgs( + T object, String methodName, SnapshotCaptor... snapshotCaptors) { + List captors = new ArrayList<>(); + Class[] classes = new Class[snapshotCaptors.length]; + + int i = 0; + for (SnapshotCaptor snapshotCaptor : snapshotCaptors) { + classes[i] = snapshotCaptor.getParameterClass(); + captors.add(ArgumentCaptor.forClass(snapshotCaptor.getParameterClass())); + i++; + } + + return process(object, methodName, captors, classes, snapshotCaptors); + } + + public static Map>> extractArgs( + T object, String methodName, Class... classes) { + List captors = new ArrayList<>(); + + for (Class clazz : classes) { + captors.add(ArgumentCaptor.forClass(clazz)); + } + + return process(object, methodName, captors, classes, null); + } + + private static Map>> process( + T object, + String methodName, + List captors, + Class[] classes, + SnapshotCaptor[] snapshotCaptors) { + Map>> result = new HashMap<>(); + try { + Parameter[] parameters = + object.getClass().getDeclaredMethod(methodName, classes).getParameters(); + + object + .getClass() + .getDeclaredMethod(methodName, classes) + .invoke( + verify(object, atLeastOnce()), + captors.stream().map(ArgumentCaptor::capture).toArray()); + + List> extractedObjects = new ArrayList<>(); + + int numberOfCall; + + if (captors.size() > 0) { + numberOfCall = captors.get(0).getAllValues().size(); + + for (int i = 0; i < numberOfCall; i++) { + Map objectMap = new LinkedHashMap<>(); + + int j = 0; + for (ArgumentCaptor captor : captors) { + Object value = captor.getAllValues().get(i); + if (snapshotCaptors != null) { + value = snapshotCaptors[j].removeIgnored(value); + } + objectMap.put(parameters[j].getName(), value); + j++; + } + extractedObjects.add(objectMap); + } + } + + result.put( + object.getClass().getSuperclass().getSimpleName() + "." + methodName, extractedObjects); + } catch (Exception e) { + throw new SnapshotMatchException(e.getMessage(), e.getCause()); + } + + return result; + } + + public static void copyTestSnapshots() { + try { + FileUtils.copyDirectory( + Paths.get("src/test/java/io/github/finoid/snapshots/existing-snapshots").toFile(), + Paths.get("src/test/java/io/github/finoid/snapshots").toFile()); + } catch (IOException e) { + throw new RuntimeException("Can't move files to __snapshots__ folder"); + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotUtilsTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotUtilsTest.java new file mode 100644 index 0000000..8078b3e --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/SnapshotUtilsTest.java @@ -0,0 +1,77 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.List; + +import static io.github.finoid.snapshots.SnapshotUtils.extractArgs; + +@ExtendWith(MockitoExtension.class) +class SnapshotUtilsTest { + + @Mock + private FakeObject fakeObject; + + @Test + void shouldExtractArgsFromFakeMethod(TestInfo testInfo) { + fakeObject.fakeMethod("test1", 1L, Arrays.asList("listTest1")); + fakeObject.fakeMethod("test2", 2L, Arrays.asList("listTest1", "listTest2")); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot( + extractArgs( + fakeObject, + "fakeMethod", + new SnapshotCaptor(String.class), + new SnapshotCaptor(Long.class), + new SnapshotCaptor(List.class))); + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldExtractArgsFromFakeMethodWithComplexObject(TestInfo testInfo) { + FakeObject fake = new FakeObject.FakeObjectBuilder().id("idMock").name("nameMock").build(); + + // With Ignore + fakeObject.fakeMethodWithComplexFakeObject(fake); + Object fakeMethodWithComplexObjectWithIgnore = + extractArgs( + fakeObject, + "fakeMethodWithComplexFakeObject", + new SnapshotCaptor(FakeObject.class, "name")); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(fakeMethodWithComplexObjectWithIgnore); + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldExtractArgsFromFakeMethodWithComplexFakeObject(TestInfo testInfo) { + + FakeObject fake = new FakeObject.FakeObjectBuilder().id("idMock").name("nameMock").build(); + + // With Ignore + fakeObject.fakeMethodWithComplexObject(fake); + Object fakeMethodWithComplexObjectWithIgnore = + extractArgs( + fakeObject, + "fakeMethodWithComplexObject", + new SnapshotCaptor(Object.class, FakeObject.class, "name")); + + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(fakeMethodWithComplexObjectWithIgnore); + snapshotVerifier.validateSnapshots(); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UpdateSnapshotPropertyTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UpdateSnapshotPropertyTest.java new file mode 100644 index 0000000..046dccc --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UpdateSnapshotPropertyTest.java @@ -0,0 +1,94 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Deprecated +@ExtendWith(MockitoExtension.class) +public class UpdateSnapshotPropertyTest { + + @AfterAll + static void afterAll() { + System.clearProperty(SnapshotConfig.JVM_UPDATE_SNAPSHOTS_PARAMETER); + } + + @BeforeEach + public void beforeEach() throws Exception { + File file = + new File( + "src/test/java/io/github/finoid/snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap"); + String content = + "io.github.finoid.snapshots.UpdateSnapshotPropertyTest.shouldNotUpdateSnapshot=[\n" + + "FakeObject(id=ERROR, value=1, name=anyName1, fakeObject=null)\n" + + "]\n" + + "\n" + + "\n" + + "io.github.finoid.snapshots.UpdateSnapshotPropertyTest.shouldUpdateSnapshot=[\n" + + "FakeObject(id=ERROR, value=2, name=anyName2, fakeObject=null)\n" + + "]"; + Path parentDir = file.getParentFile().toPath(); + if (!Files.exists(parentDir)) { + Files.createDirectories(parentDir); + } + Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8)); + } + + @Test + void shouldUpdateSnapshot(TestInfo testInfo) throws IOException { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get(), false); + System.setProperty(SnapshotConfig.JVM_UPDATE_SNAPSHOTS_PARAMETER, ""); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(FakeObject.builder().id("anyId2").value(2).name("anyName2").build()); + snapshotVerifier.validateSnapshots(); + + String content = + new String( + Files.readAllBytes( + Paths.get( + "src/test/java/io/github/finoid/snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap")), + StandardCharsets.UTF_8); + Assertions.assertThat(content) + .isEqualTo( + "io.github.finoid.snapshots.UpdateSnapshotPropertyTest.shouldNotUpdateSnapshot=[\n" + + "FakeObject(id=ERROR, value=1, name=anyName1, fakeObject=null)\n" + + "]\n" + + "\n" + + "\n" + + "io.github.finoid.snapshots.UpdateSnapshotPropertyTest.shouldUpdateSnapshot=[\n" + + "FakeObject(id=anyId2, value=2, name=anyName2, fakeObject=null)\n" + + "]"); + } + + @Test + void shouldNotUpdateSnapshot(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(new BaseSnapshotConfig(), testInfo.getTestClass().get(), false); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + System.setProperty(SnapshotConfig.JVM_UPDATE_SNAPSHOTS_PARAMETER, "true"); + assertThrows( + SnapshotMatchException.class, + () -> + expect.toMatchSnapshot( + FakeObject.builder().id("anyId1").value(1).name("anyName1").build()), + "Error on: \n" + + "io.github.finoid.snapshots.UpdateSnapshotPropertyTest.shouldNotUpdateSnapshot=["); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UpdateSnapshotTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UpdateSnapshotTest.java new file mode 100644 index 0000000..e182f7e --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UpdateSnapshotTest.java @@ -0,0 +1,154 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(MockitoExtension.class) +public class UpdateSnapshotTest { + + @BeforeEach + public void beforeEach() throws Exception { + File file = + new File("src/test/java/io/github/finoid/snapshots/__snapshots__/UpdateSnapshotTest.snap"); + String content = + "io.github.finoid.snapshots.UpdateSnapshotTest.canUpdateAllSnapshots=[\n" + + "OLD\n" + + "]\n" + + "\n" + + "\n" + + "io.github.finoid.snapshots.UpdateSnapshotTest.canUpdateClassNameSnapshots=[\n" + + "OLD\n" + + "]\n" + + "\n" + + "\n" + + "io.github.finoid.snapshots.UpdateSnapshotTest.canUpdateNoSnapshots=[\n" + + "OLD\n" + + "]"; + Path parentDir = file.getParentFile().toPath(); + if (!Files.exists(parentDir)) { + Files.createDirectories(parentDir); + } + Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8)); + } + + @Test + void canUpdateAllSnapshots(TestInfo testInfo) throws IOException { + SnapshotConfig config = + new BaseSnapshotConfig() { + @Override + public Optional updateSnapshot() { + return Optional.of(""); + } + }; + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(config, testInfo.getTestClass().get(), false); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("NEW"); + snapshotVerifier.validateSnapshots(); + + String content = + new String( + Files.readAllBytes( + Paths.get( + "src/test/java/io/github/finoid/snapshots/__snapshots__/UpdateSnapshotTest.snap")), + StandardCharsets.UTF_8); + Assertions.assertThat(content) + .isEqualTo( + "io.github.finoid.snapshots.UpdateSnapshotTest.canUpdateAllSnapshots=[\n" + + "NEW\n" + + "]\n" + + "\n" + + "\n" + + "io.github.finoid.snapshots.UpdateSnapshotTest.canUpdateClassNameSnapshots=[\n" + + "OLD\n" + + "]\n" + + "\n" + + "\n" + + "io.github.finoid.snapshots.UpdateSnapshotTest.canUpdateNoSnapshots=[\n" + + "OLD\n" + + "]"); + } + + @Test + void canUpdateNoSnapshots(TestInfo testInfo) { + SnapshotConfig config = + new BaseSnapshotConfig() { + @Override + public Optional updateSnapshot() { + return Optional.empty(); + } + }; + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(config, testInfo.getTestClass().get(), false); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + assertThrows(SnapshotMatchException.class, () -> expect.toMatchSnapshot("FOOBAR")); + } + + @Test + public void canUpdateNewSnapshots() { + SnapshotConfig config = + new BaseSnapshotConfig() { + @Override + public Optional updateSnapshot() { + return Optional.of("new"); + } + }; + + // TODO Pending Implementation + } + + @Test + public void canUpdateClassNameSnapshots(TestInfo testInfo) throws IOException { + SnapshotConfig config = + new BaseSnapshotConfig() { + @Override + public Optional updateSnapshot() { + return Optional.of("UpdateSnapshotTest"); + } + }; + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(config, testInfo.getTestClass().get(), false); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("NEW"); + snapshotVerifier.validateSnapshots(); + + String content = + new String( + Files.readAllBytes( + Paths.get( + "src/test/java/io/github/finoid/snapshots/__snapshots__/UpdateSnapshotTest.snap")), + StandardCharsets.UTF_8); + Assertions.assertThat(content) + .isEqualTo( + "io.github.finoid.snapshots.UpdateSnapshotTest.canUpdateAllSnapshots=[\n" + + "OLD\n" + + "]\n" + + "\n" + + "\n" + + "io.github.finoid.snapshots.UpdateSnapshotTest.canUpdateClassNameSnapshots=[\n" + + "NEW\n" + + "]\n" + + "\n" + + "\n" + + "io.github.finoid.snapshots.UpdateSnapshotTest.canUpdateNoSnapshots=[\n" + + "OLD\n" + + "]"); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UseCustomConfigTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UseCustomConfigTest.java new file mode 100644 index 0000000..2585772 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UseCustomConfigTest.java @@ -0,0 +1,39 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.annotations.UseSnapshotConfig; +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.config.ToStringSnapshotConfig; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@UseSnapshotConfig(ToStringSnapshotConfig.class) +@ExtendWith(MockitoExtension.class) +public class UseCustomConfigTest { + + private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); + + @BeforeAll + static void beforeAll() { + SnapshotUtils.copyTestSnapshots(); + } + + @Test + void canUseSnapshotConfigAnnotationAtClassLevel(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(new TestObject()); + snapshotVerifier.validateSnapshots(); + } + + private class TestObject { + @Override + public String toString() { + return "This is a snapshot of the toString() method"; + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UseCustomSerializerTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UseCustomSerializerTest.java new file mode 100644 index 0000000..60f131e --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/UseCustomSerializerTest.java @@ -0,0 +1,49 @@ +package io.github.finoid.snapshots; + +import io.github.finoid.snapshots.config.BaseSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.serializers.UppercaseToStringSerializer; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class UseCustomSerializerTest { + + private static final SnapshotConfig DEFAULT_CONFIG = new BaseSnapshotConfig(); + + @BeforeAll + static void beforeEach() { + SnapshotUtils.copyTestSnapshots(); + } + + @DisplayName("@SnapshotSerializer on a method via new instance") + @Test + public void canUseSnapshotSerializerAnnotationAtMethodLevelUsingNewInstance(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.serializer(new UppercaseToStringSerializer()).toMatchSnapshot(new TestObject()); + snapshotVerifier.validateSnapshots(); + } + + @DisplayName("@SnapshotSerializer on a method via class name") + @Test + public void canUseSnapshotSerializerAnnotationAtMethodLevelUsingClassName(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.serializer(new UppercaseToStringSerializer()).toMatchSnapshot(new TestObject()); + snapshotVerifier.validateSnapshots(); + } + + private class TestObject { + @Override + public String toString() { + return "This is a snapshot of the toString() method"; + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/comparators/PlainTextEqualsComparatorTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/comparators/PlainTextEqualsComparatorTest.java new file mode 100644 index 0000000..b61844e --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/comparators/PlainTextEqualsComparatorTest.java @@ -0,0 +1,24 @@ +package io.github.finoid.snapshots.comparators; + +import io.github.finoid.snapshots.Snapshot; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class PlainTextEqualsComparatorTest { + + private static final PlainTextEqualsComparator COMPARATOR = new PlainTextEqualsComparator(); + + @Test + void successfulComparison() { + Snapshot snap1 = Snapshot.builder().name("snap1").scenario("A").body("foo").build(); + Snapshot snap2 = Snapshot.builder().name("snap1").scenario("A").body("foo").build(); + Assertions.assertThat(COMPARATOR.matches(snap1, snap2)).isTrue(); + } + + @Test + void failingComparison() { + Snapshot snap1 = Snapshot.builder().name("snap1").scenario("A").body("foo").build(); + Snapshot snap2 = Snapshot.builder().name("snap1").scenario("A").body("bar").build(); + Assertions.assertThat(COMPARATOR.matches(snap1, snap2)).isFalse(); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/config/BaseSnapshotConfig.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/config/BaseSnapshotConfig.java new file mode 100644 index 0000000..bb0d37f --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/config/BaseSnapshotConfig.java @@ -0,0 +1,44 @@ +package io.github.finoid.snapshots.config; + +import io.github.finoid.snapshots.comparators.PlainTextEqualsComparator; +import io.github.finoid.snapshots.comparators.SnapshotComparator; +import io.github.finoid.snapshots.reporters.PlainTextSnapshotReporter; +import io.github.finoid.snapshots.reporters.SnapshotReporter; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import io.github.finoid.snapshots.serializers.ToStringSnapshotSerializer; + +import java.util.Collections; +import java.util.List; + +public class BaseSnapshotConfig implements SnapshotConfig { + + @Override + public String getOutputDir() { + return "src/test/java"; + } + + @Override + public String getSnapshotDir() { + return "__snapshots__"; + } + + @Override + public SnapshotSerializer getSerializer() { + return new ToStringSnapshotSerializer(); + } + + @Override + public SnapshotComparator getComparator() { + return new PlainTextEqualsComparator(); + } + + @Override + public List getReporters() { + return Collections.singletonList(new PlainTextSnapshotReporter()); + } + + @Override + public boolean isCI() { + return false; + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/config/ToStringSnapshotConfig.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/config/ToStringSnapshotConfig.java new file mode 100644 index 0000000..42948f2 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/config/ToStringSnapshotConfig.java @@ -0,0 +1,24 @@ +package io.github.finoid.snapshots.config; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; + +public class ToStringSnapshotConfig extends BaseSnapshotConfig { + + @Override + public SnapshotSerializer getSerializer() { + return new SnapshotSerializer() { + @Override + public String getOutputFormat() { + return SerializerType.TEXT.name(); + } + + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + return gen.toSnapshot(object.toString()); + } + }; + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap new file mode 100644 index 0000000..fcf576c --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/DebugSnapshotLineEndingsTest.snap @@ -0,0 +1,4 @@ +io.github.finoid.snapshots.DebugSnapshotLineEndingsTest.existingSnapshotDifferentLineEndings=[ +a +b +] \ No newline at end of file diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/DebugSnapshotTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/DebugSnapshotTest.snap new file mode 100644 index 0000000..00c27a7 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/DebugSnapshotTest.snap @@ -0,0 +1,13 @@ +io.github.finoid.snapshots.DebugSnapshotTest.createDebugFile=[ +Good Snapshot +] + + +io.github.finoid.snapshots.DebugSnapshotTest.debugFileCreatedSnapshotMatch=[ +Good Snapshot +] + + +io.github.finoid.snapshots.DebugSnapshotTest.debugFileCreatedExistingSnapshot=[ +Good Snapshot +] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap similarity index 100% rename from java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap rename to snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap.debug b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap.debug similarity index 100% rename from java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap.debug rename to snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest$NestedClass.snap.debug diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest.snap similarity index 100% rename from java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest.snap rename to snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest.snap diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest.snap.debug b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest.snap.debug similarity index 100% rename from java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest.snap.debug rename to snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EmptySnapshotFileTest.snap.debug diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap new file mode 100644 index 0000000..00c27a7 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap @@ -0,0 +1,13 @@ +io.github.finoid.snapshots.DebugSnapshotTest.createDebugFile=[ +Good Snapshot +] + + +io.github.finoid.snapshots.DebugSnapshotTest.debugFileCreatedSnapshotMatch=[ +Good Snapshot +] + + +io.github.finoid.snapshots.DebugSnapshotTest.debugFileCreatedExistingSnapshot=[ +Good Snapshot +] \ No newline at end of file diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap.debug b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap.debug new file mode 100644 index 0000000..00c27a7 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/EqualDebugSnapshotFileTest.snap.debug @@ -0,0 +1,13 @@ +io.github.finoid.snapshots.DebugSnapshotTest.createDebugFile=[ +Good Snapshot +] + + +io.github.finoid.snapshots.DebugSnapshotTest.debugFileCreatedSnapshotMatch=[ +Good Snapshot +] + + +io.github.finoid.snapshots.DebugSnapshotTest.debugFileCreatedExistingSnapshot=[ +Good Snapshot +] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/OrphanSnapshotTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/OrphanSnapshotTest.snap similarity index 51% rename from java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/OrphanSnapshotTest.snap rename to snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/OrphanSnapshotTest.snap index d0aff78..88c8742 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/OrphanSnapshotTest.snap +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/OrphanSnapshotTest.snap @@ -1,4 +1,4 @@ -au.com.origin.snapshots.OrphanSnapshotTest.orphanMethod=[ +io.github.finoid.snapshots.OrphanSnapshotTest.orphanMethod=[ { "message": "This orphan method should fail the test" } diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/PrivateCalledMethodTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/PrivateCalledMethodTest.snap new file mode 100644 index 0000000..fb4393e --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/PrivateCalledMethodTest.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.PrivateCalledMethodTest.testName=[ +testContent +] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/README.md b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/README.md similarity index 100% rename from java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/README.md rename to snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/README.md diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/ScenarioTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/ScenarioTest.snap new file mode 100644 index 0000000..8733f21 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/ScenarioTest.snap @@ -0,0 +1,28 @@ +io.github.finoid.snapshots.ScenarioTest.canTakeMultipleSnapshotsUsingScenario=[ +Default Snapshot +] + + +io.github.finoid.snapshots.ScenarioTest.canTakeMultipleSnapshotsUsingScenario[additional]=[ +Additional Snapshot +] + + +io.github.finoid.snapshots.ScenarioTest.canTakeTheSameSnapshotTwice=[ +Default Snapshot +] + + +io.github.finoid.snapshots.ScenarioTest.canTakeTheSameSnapshotTwice[scenario]=[ +Scenario Snapshot +] + + +io.github.finoid.snapshots.ScenarioTest.cannotTakeDifferentSnapshotsAtDefaultLevel=[ +Default Snapshot +] + + +io.github.finoid.snapshots.ScenarioTest.cannotTakeDifferentSnapshotsAtScenarioLevel[scenario]=[ +Default Snapshot +] \ No newline at end of file diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotHeaders.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotHeaders.snap new file mode 100644 index 0000000..bffe457 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotHeaders.snap @@ -0,0 +1,10 @@ +io.github.finoid.snapshots.SnapshotHeaders.shouldBeAbleToSnapshotASingleCustomHeader={ + "custom": "anything", + "custom2": "anything2" +}hello world + + +io.github.finoid.snapshots.SnapshotHeaders.shouldBeAbleToSnapshotMultipleCustomHeader={ + "custom": "anything", + "custom2": "anything2" +}hello world \ No newline at end of file diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotIntegrationTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotIntegrationTest.snap new file mode 100644 index 0000000..d048e0f --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotIntegrationTest.snap @@ -0,0 +1,37 @@ +io.github.finoid.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotFour=[ +FakeObject(id=anyId4, value=4, name=any +. +. +Name4, fakeObject=null) +] + + +io.github.finoid.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotInsidePrivateMethod=[ +FakeObject(id=anyPrivate, value=5, name=anyPrivate, fakeObject=null) +] + + +io.github.finoid.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotOne=[ +FakeObject(id=anyId1, value=1, name=anyName1, fakeObject=null) +] + + +io.github.finoid.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotThree=[ +FakeObject(id=anyId3, value=3, name=anyName3, fakeObject=null) +] + + +io.github.finoid.snapshots.SnapshotIntegrationTest.shouldMatchSnapshotTwo=[ +FakeObject(id=anyId2, value=2, name=anyName2, fakeObject=null) +] + + +io.github.finoid.snapshots.SnapshotIntegrationTest.shouldSnapshotUsingSerializerClass=HELLO WORLD + + +io.github.finoid.snapshots.SnapshotIntegrationTest.shouldSnapshotUsingSerializerPropertyName=hello world + + +io.github.finoid.snapshots.SnapshotIntegrationTest.shouldThrowSnapshotMatchException=[ +FakeObject(id=anyId5, value=7, name=anyName5, fakeObject=null) +] \ No newline at end of file diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotNameAnnotationTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotNameAnnotationTest.snap similarity index 100% rename from java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/existing-snapshots/__snapshots__/SnapshotNameAnnotationTest.snap rename to snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotNameAnnotationTest.snap diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotOverrideClassTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotOverrideClassTest.snap new file mode 100644 index 0000000..6db8f95 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotOverrideClassTest.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.SnapshotOverrideClassTest.shouldMatchSnapshotOne=[ +anyName +] \ No newline at end of file diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotUtilsTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/SnapshotUtilsTest.snap new file mode 100644 index 0000000..e69de29 diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap new file mode 100644 index 0000000..42513dc --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/UpdateSnapshotPropertyTest.snap @@ -0,0 +1,8 @@ +io.github.finoid.snapshots.UpdateSnapshotPropertyTest.shouldNotUpdateSnapshot=[ +FakeObject(id=ERROR, value=1, name=anyName1, fakeObject=null) +] + + +io.github.finoid.snapshots.UpdateSnapshotPropertyTest.shouldUpdateSnapshot=[ +FakeObject(id=ERROR, value=1, name=anyName2, fakeObject=null) +] \ No newline at end of file diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/UseCustomConfigTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/UseCustomConfigTest.snap new file mode 100644 index 0000000..c343c13 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/UseCustomConfigTest.snap @@ -0,0 +1,4 @@ +io.github.finoid.snapshots.UseCustomConfigTest.canUseSnapshotConfigAnnotationAtClassLevel=This is a snapshot of the toString() method + + +io.github.finoid.snapshots.UseCustomConfigTest.canUseSnapshotConfigAnnotationAtMethodLevel=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD \ No newline at end of file diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/UseCustomSerializerTest.snap b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/UseCustomSerializerTest.snap new file mode 100644 index 0000000..7ed515a --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/existing-snapshots/__snapshots__/UseCustomSerializerTest.snap @@ -0,0 +1,10 @@ +io.github.finoid.snapshots.UseCustomSerializerTest.canUseSnapshotSerializerAnnotationAtClassLevel=this is a snapshot of the tostring() method + + +io.github.finoid.snapshots.UseCustomSerializerTest.canUseSnapshotSerializerAnnotationAtMethodLevel=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD + + +io.github.finoid.snapshots.UseCustomSerializerTest.canUseSnapshotSerializerAnnotationAtMethodLevelUsingClassName=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD + + +io.github.finoid.snapshots.UseCustomSerializerTest.canUseSnapshotSerializerAnnotationAtMethodLevelUsingNewInstance=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD \ No newline at end of file diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/reporters/PlainTextSnapshotReporterTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/reporters/PlainTextSnapshotReporterTest.java new file mode 100644 index 0000000..62208c1 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/reporters/PlainTextSnapshotReporterTest.java @@ -0,0 +1,34 @@ +package io.github.finoid.snapshots.reporters; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.serializers.SerializerType; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +class PlainTextSnapshotReporterTest { + private static final PlainTextSnapshotReporter REPORTER = new PlainTextSnapshotReporter(); + + @Test + void shouldSupportAllFormats() { + Assertions.assertThat(REPORTER.supportsFormat(SerializerType.TEXT.name())).isTrue(); + Assertions.assertThat(REPORTER.supportsFormat(SerializerType.JSON.name())).isTrue(); + + Assertions.assertThat(REPORTER.supportsFormat("xml")).isTrue(); + Assertions.assertThat(REPORTER.supportsFormat("blah")).isTrue(); + } + + @Test + void doReport() { + Snapshot snap1 = Snapshot.builder().name("snap1").scenario("A").body("[\nfoo\n]").build(); + Snapshot snap2 = Snapshot.builder().name("snap1").scenario("A").body("[\nbar\n]").build(); + assertThatExceptionOfType(AssertionFailedError.class) + .isThrownBy(() -> REPORTER.report(snap1, snap2)) + .withMessageContaining("expecting:") + .withMessageContaining("[\"foo\"]") + .withMessageContaining("but was:") + .withMessageContaining("[\"bar\"]"); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/Base64SnapshotSerializerTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/Base64SnapshotSerializerTest.java new file mode 100644 index 0000000..f89a08e --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/Base64SnapshotSerializerTest.java @@ -0,0 +1,56 @@ +package io.github.finoid.snapshots.serializers; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotHeader; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.nio.file.Files; + +import static org.assertj.core.api.Assertions.assertThat; + +public class Base64SnapshotSerializerTest { + + private SnapshotSerializerContext mockSnapshotGenerator = + new SnapshotSerializerContext( + "base64Test", + null, + new SnapshotHeader(), + Base64SnapshotSerializerTest.class, + null // it's not used in these scenarios + ); + + @Test + void shouldSnapshotAByteArray() { + Base64SnapshotSerializer serializer = new Base64SnapshotSerializer(); + Snapshot result = serializer.apply("John Doe".getBytes(), mockSnapshotGenerator); + assertThat(result.getBody()).isEqualTo("[\nSm9obiBEb2U=\n]"); + } + + @Test + void shouldSnapshotAnyString() { + Base64SnapshotSerializer serializer = new Base64SnapshotSerializer(); + Snapshot result = serializer.apply("John Doe", mockSnapshotGenerator); + assertThat(result.getBody()).isEqualTo("[\nSm9obiBEb2U=\n]"); + } + + @Test + @SuppressWarnings("checkstyle:LineLength") + void shouldSnapshotAFile() throws Exception { + Base64SnapshotSerializer serializer = new Base64SnapshotSerializer(); + File f = new File("src/test/resources/origin-logo.png"); + byte[] content = Files.readAllBytes(f.toPath()); + + Snapshot result = serializer.apply(content, mockSnapshotGenerator); + assertThat(result.getBody()) + .isEqualTo( + "[\niVBORw0KGgoAAAANSUhEUgAAAFgAAABYCAIAAAD+96djAAAKaklEQVR4nOxce3BcVRk/59zX3t3N5p3m1UeaNLQ2adKaFhDp9MFD3j7Qoqhg1VHpjEMR7CgKyghVQKa24wzSUhQcxwGLU0TsH0AphQLSxKSQJm02aRrSvDbPfd/HOddZ2LLJZp/n3F0aJr/Zf7J77nd+95fvfN85537n8oZhgHkAgD5pAhcK5oUIY16IMOaFCGNeiDD4rPVEpiZ1p1PvceLhITIyQlwuw+c1FMVQFGAYUJKAJEFZ5oqKUUkJKlnAL63mq5dxxcXZoQczmj7xqEtrflc7fUprbSGuEQoLMC9fbFzN1y4X1zRx5RUZ4Hi+o0wIQdxu5Y0jyuuv6p0dwDz73JIqacMmaf0mrrDILJsfw2Qh9L7ewMF/KEdfA5pmotkZQEhcd6l801eE5StNtGqaEHrfWf8z+9T/vWuiCyQGv2Kl7datwoo6U6yZI4Ty9lH/M0/i4UEzKKUB5Mi1bvm2ZfMXAM8a9VmFwMODvn171LZmRh4s4BYusf/gTqF2BYsRBiEwDhw66H/2L6H890kDIiRddb3tlq3QYqG0QCcEcU96//iw9l4LXa8ZAle5KOeu+7lSmixLI4Tu7PTseZCMuij6yzSg1WbftkNsXJf2hekKob7X7Nn1AFA/+eEQF4iz3bbNsunatC5KTwjl7SPevY8CPWNzBPMg33y79YZbUm+fRtZR3n7N+6ffhaYJkIpadhE48GcIoXz9lhTbpyqE2vqW78lHAJwbKnwE//NPAQ7J13w1lcYpCaF1d3gf3wkMAtHckeFDBA7sR3kF0qWbk7ZMLgSZHPM98RAg2hzdu/D9dTcqKhGW1SduliRY4vER3/5H9K73zaaXXUhS3kNPI7sjQZOEQhDi3nOv3tmaEXLZBbdoWe5dDwMp7rwzkbsHj76on24NNZn7H9zf5Xt+b4KbjSsEHh8JHHwqlCM+LR/l2EvaqbjeHTdYBl96xtAVYHaagILIV63ga1bxC2tQSQVy5ENeABACTSN+Dx45hwfO6N3va10njIDP3K5DSeTgPuHuPwDExSAWM0boHzg9j90JDGIiCW5hjXTZdWLDZVC2J2+tKWr7u8obL+rOEyZyAABYv7FdWnfl7O9jC+F54j6987hZfaOSSutN3xdWrKW4VjvTHvjXftzbCYA5G1+oYEHuz/YCLnooxBBC73d6dv3YlH6hbJO/vE1adRngBXorhGi9J/1/f4yMDZnACQDrLdultdFOESNYKq89F4ouzIGaW1ids323tGYDkwof7tYKS+sc23cLK9eZkkGCh58DBCcRgkyNqu3H2DsTVl7suONRrrCMSYJpgLLdfvsvpctvZOdGxvq17ujQEy2E2vwyMDBjouJr19i/dS8QJbNUOE+Ws974Q8umLeypVG15Jcp2dMzQuo4zrqygLdf6xTtmRyOzIG/cop8+jgd7WIzoPW0AY8BF8ugMj8DjQ7ivg0lsBK03b+cKy1lYJoFosd76c2i1sfA0vOPa2RkLqBlC6M5mxuEnXnKdcBFNmkwLXEGZfMOPGKnqXc1xhdC6W1hMw/xi65W3ZVqFjyA2bOSrG1jYat3xhCAE97ax+Ju86ZtAsmZHiFCwuHpraAVAy5a4eolvMoYQeKzf0ALUAqPCMrF+Q9ZUCA2QshrhIoaZBQR4sCuWEENOpujQeEXmMkU8iE3XsnDGg86PTUWo4+EelsQp1K1nvq/0O61qRPY8wz9FdzkeieTgiEcQ9zD1eENFFVx+JlNmPHA8t6SePky4R2IIYXhc1D7GLTKnSIEC/KJ6atqGJyJEZGgQ7yig3afmihebcE/UXdPSNhQP0BQgSDOEMDQfoA0RqCCDZV5Jus4vp6Yd+vcrPjRDCIIB0amlhdZEO+UZBbLmfrifSLt9ooefZp8XQldhSAVKbaGYvXlUNBAHJSnk4VQwooWAiNodQiA6w8XMgICe/PmN3PNCCFLIwWjLiAzVT0vEDBCVOkxAPrxpMm0uKEpAC9KZM7wuAJhquagRSnYsz+iFWUJAW67hphSCTJ2jJcIKMtHPMi6QFH62EBEC5ZRg7zCdQezqouXCCjzcQS0EyimaFSMAQI5iTLtdjodPAqxnf9EVSneDJ+gDRE6k9H+aEHmV9Isu3a8NtAoLmygvpwXxjxNXBzVtlBdZH0W8iiuuYVnS6l3R+8JZgOY8DAChXyIVVn1sappH5FayzFX1wVag+oBoY725dID73mLhjByRxy4Rj0D2YmgroHcK3ad0/pv1ztKBPtiGx04zeDFEhdUxhAiNjvJGltGhnXyeBCayJAPWlOP7WNiioqVIzostBF/ewPSwQPerLfuzo4PSfoBMnWVhy1fOCO0zEh5XtjqUAo3oB6SpQzv7OregTqi5muEekwO7Tqonn2UsYuErL57x5/Q/kJzHVzbhc++wdKC07IX2BXxpI4uRBCA+V/DYoxBgllUidCzkCmumfxNtTKi+isXfQh9DCx7bicczMtc0/KOBI/cZwTFGkkLVxijL0ULwpathTglLEAp9cDBw5Bf6cJu5KhDPgP/wDsM3wEqPF/glyYQAiBOqr2HtCQFAgsE3f612PDu7JIMOev+b/sN3G8FRdm784vVILoiyH6uGSvV6D30XYMqVaBS4guVC7Zf4skuoLeAJp+Z8Qe8/YgofAKC8eTfnWBT9bcxiMqX9aa3rgEkdh8CVNIq1X+OK0jupid29Wtc/9f6jLIksCnzF5y1r75n9fZwSZC3gP7zNCI6b1X24M8divmI9v6AJORJt/xPvOTzSrA8cI+OnzCqmCwPx8sY9yBajoCluLbb2watq224zSUyHlMc5qqC9AooOiMTQZAxrhuo2vOeI+4yhZGp6yi+9UfrM1pg/JSpKD/73V3j001CR/hGgvMB6+S7AyzF/TTQpEevuAILMOq24QD4ISvXb4qmQ/LyG1v+y2vEEIGo60l+IEJd/T1h8fYIGSTbXhMorIOKV9l0m84I8tBRBIQciCXASgBBgxcAK0H0kOAqIyWcpuaK1iVVI6SgTX74Buzv1/kMsVKBcyuXWopwa5KhG1jIo5SUYlYY6RQJDxNND3N3E7SS+syxvIODyV1oafpqcYUrnPokWbPsNmUizUF7M5Ys/h/JXoZylSC5J79ppMFQ38fTgiTY88qYRTO/4MbRWyGseBGJu8papHoDVA4HWew3vmRQ657jCJr50M1ewGiBz97UJnmjXB1/Bo2+lEragVCyt2YmkwlRMp3MSWJ0MnLjf8PXFbcDLfOkVfPm1SC5N1SYVDHVSGzikD/4HqO54baBUIK16AFlTrVdI70i0obmDrTuMYIznHyi3Tlr+EyjmpW6NFbpfcT6OXUdj/MRZLA2/RbY0ylfSf1sADgbadhj+iF9AqUhafg/KqU3PjkkggSH11O+J1zmNT6Glfie0pPfeJqr3R2hu5fRjeKotNBqKN4pV3wF8TtpGTATRtL6/aYMvAIMg+zLponuglPbbq6jfKEK0/gPItpTL/yzV5eYDe06RiXeEyq8DRHNOJrMv5JpDmJvnvTOAeSHCmBcijHkhwpgXIox5IcKYFyKM/wcAAP//h4bYYlJz5AYAAAAASUVORK5CYII=\n]"); + } + + @Test + void shouldSupportBase64SerializerType() { + Base64SnapshotSerializer serializer = new Base64SnapshotSerializer(); + assertThat(serializer.getOutputFormat()).isEqualTo("BASE64"); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/LowercaseToStringSerializer.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/LowercaseToStringSerializer.java new file mode 100644 index 0000000..e3ee1e0 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/LowercaseToStringSerializer.java @@ -0,0 +1,16 @@ +package io.github.finoid.snapshots.serializers; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; + +public class LowercaseToStringSerializer implements SnapshotSerializer { + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + return gen.toSnapshot(object.toString().toLowerCase()); + } + + @Override + public String getOutputFormat() { + return SerializerType.TEXT.name(); + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/ToStringSnapshotSerializerTest.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/ToStringSnapshotSerializerTest.java new file mode 100644 index 0000000..0dcf637 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/ToStringSnapshotSerializerTest.java @@ -0,0 +1,89 @@ +package io.github.finoid.snapshots.serializers; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotHeader; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ToStringSnapshotSerializerTest { + ToStringSnapshotSerializer serializer = new ToStringSnapshotSerializer(); + + private SnapshotSerializerContext mockSnapshotGenerator = + new SnapshotSerializerContext( + "base64Test", + null, + new SnapshotHeader(), + ToStringSnapshotSerializerTest.class, + null // it's not used in these scenarios + ); + + @Test + void shouldSnapshotAnyString() { + Snapshot result = serializer.apply("John Doe", mockSnapshotGenerator); + assertThat(result.getBody()).isEqualTo("[\nJohn Doe\n]"); + } + + @Test + void shouldSnapshotUnicode() { + Snapshot result = serializer.apply("🤔", mockSnapshotGenerator); + assertThat(result.getBody()).isEqualTo("[\n🤔\n]"); + } + + @Test + void shouldSnapshotAnyObject() { + Snapshot result = serializer.apply(new Dummy(1, "John Doe"), mockSnapshotGenerator); + assertThat(result.getBody()) + .isEqualTo("[\nToStringSerializerTest.Dummy(id=1, name=John Doe)\n]"); + } + + @Test + void shouldSnapshotMultipleObjects() { + Snapshot result = serializer.apply(new Dummy(1, "John Doe"), mockSnapshotGenerator); + assertThat(result.getBody()) + .isEqualTo("[\nToStringSerializerTest.Dummy(id=1, name=John Doe)\n]"); + } + + @Test + void shouldSupportBase64SerializerType() { + assertThat(serializer.getOutputFormat()).isEqualTo("TEXT"); + } + + @Test + void shouldReplaceThreeConsecutiveNewLines() { + Snapshot result = serializer.apply("John\n\n\nDoe", mockSnapshotGenerator); + assertThat(result.getBody()).isEqualTo("[\nJohn\n.\n.\nDoe\n]"); + } + + @Test + void shouldReplaceTwoConsecutiveNewLinesAtEnd() { + Snapshot result = serializer.apply("John Doe\n\n", mockSnapshotGenerator); + assertThat(result.getBody()).isEqualTo("[\nJohn Doe\n.\n.\n]"); + } + + @Test + void shouldReplaceTwoConsecutiveNewLinesAtBeginning() { + Snapshot result = serializer.apply("\n\nJohn Doe", mockSnapshotGenerator); + assertThat(result.getBody()).isEqualTo("[\n.\n.\nJohn Doe\n]"); + } + + @Test + void shouldReplaceIllegalNewlineSequencesEverywhere() { + Snapshot result = serializer.apply("\n\nJohn\n\n\nDoe\n\n", mockSnapshotGenerator); + assertThat(result.getBody()).isEqualTo("[\n.\n.\nJohn\n.\n.\nDoe\n.\n.\n]"); + } + + @AllArgsConstructor + @Data + private static class Dummy { + private int id; + private String name; + + public String toString() { + return "ToStringSerializerTest.Dummy(id=" + this.getId() + ", name=" + this.getName() + ")"; + } + } +} diff --git a/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/UppercaseToStringSerializer.java b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/UppercaseToStringSerializer.java new file mode 100644 index 0000000..a569551 --- /dev/null +++ b/snapshot-testing-core/src/test/java/io/github/finoid/snapshots/serializers/UppercaseToStringSerializer.java @@ -0,0 +1,16 @@ +package io.github.finoid.snapshots.serializers; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; + +public class UppercaseToStringSerializer implements SnapshotSerializer { + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + return gen.toSnapshot(object.toString().toUpperCase()); + } + + @Override + public String getOutputFormat() { + return SerializerType.TEXT.name(); + } +} diff --git a/java-snapshot-testing-core/src/test/resources/origin-logo.png b/snapshot-testing-core/src/test/resources/origin-logo.png similarity index 100% rename from java-snapshot-testing-core/src/test/resources/origin-logo.png rename to snapshot-testing-core/src/test/resources/origin-logo.png diff --git a/snapshot-testing-core/src/test/resources/snapshot.properties b/snapshot-testing-core/src/test/resources/snapshot.properties new file mode 100644 index 0000000..03ba77d --- /dev/null +++ b/snapshot-testing-core/src/test/resources/snapshot.properties @@ -0,0 +1 @@ +serializer.lowercase=io.github.finoid.snapshots.serializers.LowercaseToStringSerializer \ No newline at end of file diff --git a/snapshot-testing-jackson2/pom.xml b/snapshot-testing-jackson2/pom.xml new file mode 100644 index 0000000..28c1e63 --- /dev/null +++ b/snapshot-testing-jackson2/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + io.github.finoid + snapshot-testing-parent + ${revision} + ../pom.xml + + + snapshot-testing-jackson2 + finoid-snapshot-testing-jackson2 + Jackson2 library for snapshot-testing + + + 21 + 21 + 21 + 21 + UTF-8 + 1.5.3 + + + + + org.checkerframework + checker-qual + + + com.fasterxml.jackson.core + jackson-core + ${jackson2.version} + provided + + + com.fasterxml.jackson.core + jackson-databind + ${jackson2.version} + provided + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + ${jackson2.version} + provided + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson2.version} + provided + + + io.github.finoid + snapshot-testing-core + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.junit.jupiter + junit-jupiter-api + ${junit5.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit5.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit5.version} + test + + + org.skyscreamer + jsonassert + ${jsonassert.version} + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + \ No newline at end of file diff --git a/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/DeterministicCollectionModule.java b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/DeterministicCollectionModule.java new file mode 100644 index 0000000..6c86d52 --- /dev/null +++ b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/DeterministicCollectionModule.java @@ -0,0 +1,56 @@ +package io.github.finoid.snapshots.jackson2.serializers; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@SuppressWarnings({"checkstyle:all", "this-escape"}) // TODO (nw) rewrite +public class DeterministicCollectionModule extends SimpleModule { + + public DeterministicCollectionModule() { + addSerializer(Collection.class, new CollectionSerializer()); + } + + /** + * Collections gets converted into a sorted Object[]. This then gets serialized using the default + * Array serializer. + */ + private static class CollectionSerializer extends JsonSerializer { + + @Override + public void serialize(Collection value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + Object[] sorted = convert(value); + serializers.defaultSerializeValue(sorted, gen); + } + + private Object[] convert(Collection value) { + if (value == null || value.isEmpty()) { + return Collections.emptyList().toArray(); + } + + try { + return value.stream() + .filter(Objects::nonNull) + .sorted() + .collect(Collectors.toList()) + .toArray(); + } catch (ClassCastException ex) { + log.warn( + "Unable to sort() collection - this may result in a non deterministic snapshot.\n" + + "Consider adding a custom serializer for this type via the JacksonSnapshotSerializer#configure() method.\n" + + ex.getMessage()); + return value.toArray(); + } + } + } +} diff --git a/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/DeterministicJacksonSnapshotSerializer.java b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/DeterministicJacksonSnapshotSerializer.java new file mode 100644 index 0000000..e989251 --- /dev/null +++ b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/DeterministicJacksonSnapshotSerializer.java @@ -0,0 +1,24 @@ +package io.github.finoid.snapshots.jackson2.serializers; + +import io.github.finoid.snapshots.logging.LoggingHelper; +import lombok.extern.slf4j.Slf4j; + +/** + * Attempts to deterministically render a snapshot. + * + *

This can help in situations where collections are rendering in a different order on subsequent + * runs. + * + *

Note that collections will be ordered which mar or may not be desirable given your use case. + */ +@Deprecated +@Slf4j +public class DeterministicJacksonSnapshotSerializer + extends io.github.finoid.snapshots.jackson2.serializers.v1.DeterministicJacksonSnapshotSerializer { + public DeterministicJacksonSnapshotSerializer() { + super(); + LoggingHelper.deprecatedV5( + log, + "Update to `v1.serializers.jackson2.io.github.finoid.snapshots.DeterministicJacksonSnapshotSerializer` in `snapshot.properties`"); + } +} diff --git a/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/JacksonSnapshotSerializer.java b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/JacksonSnapshotSerializer.java new file mode 100644 index 0000000..7dda663 --- /dev/null +++ b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/JacksonSnapshotSerializer.java @@ -0,0 +1,17 @@ +package io.github.finoid.snapshots.jackson2.serializers; + +import io.github.finoid.snapshots.logging.LoggingHelper; +import lombok.extern.slf4j.Slf4j; + +@Deprecated +@Slf4j +public class JacksonSnapshotSerializer + extends io.github.finoid.snapshots.jackson2.serializers.v1.JacksonSnapshotSerializer { + + public JacksonSnapshotSerializer() { + super(); + LoggingHelper.deprecatedV5( + log, + "Update to `v1.serializers.jackson2.io.github.finoid.snapshots.JacksonSnapshotSerializer` in `snapshot.properties`"); + } +} diff --git a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/v1/DeterministicJacksonSnapshotSerializer.java b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/v1/DeterministicJacksonSnapshotSerializer.java similarity index 52% rename from java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/v1/DeterministicJacksonSnapshotSerializer.java rename to snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/v1/DeterministicJacksonSnapshotSerializer.java index f183fd9..ca1b7ce 100644 --- a/java-snapshot-testing-plugin-jackson/src/main/java/au/com/origin/snapshots/jackson/serializers/v1/DeterministicJacksonSnapshotSerializer.java +++ b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/v1/DeterministicJacksonSnapshotSerializer.java @@ -1,6 +1,6 @@ -package au.com.origin.snapshots.jackson.serializers.v1; +package io.github.finoid.snapshots.jackson2.serializers.v1; -import au.com.origin.snapshots.jackson.serializers.DeterministicCollectionModule; +import io.github.finoid.snapshots.jackson2.serializers.DeterministicCollectionModule; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; @@ -12,11 +12,11 @@ * *

Note that collections will be ordered which mar or may not be desirable given your use case. */ +@SuppressWarnings("deprecation") // TODO (nw) rewrite public class DeterministicJacksonSnapshotSerializer extends JacksonSnapshotSerializer { - - @Override - public void configure(ObjectMapper objectMapper) { - objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); - objectMapper.registerModule(new DeterministicCollectionModule()); - } + @Override + public void configure(ObjectMapper objectMapper) { + objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); + objectMapper.registerModule(new DeterministicCollectionModule()); + } } diff --git a/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/v1/JacksonSnapshotSerializer.java b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/v1/JacksonSnapshotSerializer.java new file mode 100644 index 0000000..94a733a --- /dev/null +++ b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/v1/JacksonSnapshotSerializer.java @@ -0,0 +1,76 @@ +package io.github.finoid.snapshots.jackson2.serializers.v1; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.exceptions.SnapshotExtensionException; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.PrettyPrinter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +import java.util.Collections; +import java.util.List; + +@SuppressWarnings({"checkstyle:all", "deprecation"}) // TODO (nw) rewrite +public class JacksonSnapshotSerializer implements SnapshotSerializer { + + private final PrettyPrinter pp = new SnapshotPrettyPrinter(); + private final ObjectMapper objectMapper = + new ObjectMapper() { + { + this.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); + this.enable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID); + this.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + this.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + this.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + if (shouldFindAndRegisterModules()) { + this.findAndRegisterModules(); + } + + this.setVisibility( + this.getSerializationConfig() + .getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withSetterVisibility(JsonAutoDetect.Visibility.NONE) + .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)); + JacksonSnapshotSerializer.this.configure(this); + } + }; + + /** + * Override to customize the Jackson objectMapper + * + * @param objectMapper existing ObjectMapper + */ + public void configure(ObjectMapper objectMapper) { + } + + /** + * Override to control the registration of all available jackson2 modules within the classpath + * which are locatable via JDK ServiceLoader facility, along with module-provided SPI. + */ + protected boolean shouldFindAndRegisterModules() { + return true; + } + + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + try { + List objects = Collections.singletonList(object); + String body = objectMapper.writer(pp).writeValueAsString(objects); + return gen.toSnapshot(body); + } catch (Exception e) { + throw new SnapshotExtensionException("Jackson Serialization failed", e); + } + } + + @Override + public String getOutputFormat() { + return SerializerType.JSON.name(); + } +} diff --git a/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/v1/SnapshotPrettyPrinter.java b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/v1/SnapshotPrettyPrinter.java new file mode 100644 index 0000000..b38cf1d --- /dev/null +++ b/snapshot-testing-jackson2/src/main/java/io/github/finoid/snapshots/jackson2/serializers/v1/SnapshotPrettyPrinter.java @@ -0,0 +1,25 @@ +package io.github.finoid.snapshots.jackson2.serializers.v1; + +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; + +class SnapshotPrettyPrinter extends DefaultPrettyPrinter { + + @SuppressWarnings("deprecation") // TODO (nw) rewrite + public SnapshotPrettyPrinter() { + super(""); + Indenter lfOnlyIndenter = new DefaultIndenter(" ", "\n"); + this.indentArraysWith(lfOnlyIndenter); + this.indentObjectsWith(lfOnlyIndenter); + + this._objectFieldValueSeparatorWithSpaces = + this._separators.getObjectFieldValueSeparator() + " "; + } + + // It's a requirement + // @see https://github.com/FasterXML/jackson-databind/issues/2203 + @Override + public DefaultPrettyPrinter createInstance() { + return new DefaultPrettyPrinter(this); + } +} diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/NoNameChangeTest.java b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/NoNameChangeTest.java new file mode 100644 index 0000000..c324d24 --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/NoNameChangeTest.java @@ -0,0 +1,24 @@ +package io.github.finoid.snapshots.jackson2; + +import io.github.finoid.snapshots.jackson2.serializers.DeterministicJacksonSnapshotSerializer; +import io.github.finoid.snapshots.jackson2.serializers.JacksonSnapshotSerializer; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * These classes are likely defined in snapshot.properties as a string. + * + *

The clients IDE will not complain if they change so ensure they don't + */ +public class NoNameChangeTest { + + @Test + public void serializersApiShouldNotChange() { + assertThat(JacksonSnapshotSerializer.class.getName()) + .isEqualTo("io.github.finoid.snapshots.jackson2.serializers.JacksonSnapshotSerializer"); + assertThat(DeterministicJacksonSnapshotSerializer.class.getName()) + .isEqualTo( + "io.github.finoid.snapshots.jackson2.serializers.DeterministicJacksonSnapshotSerializer"); + } +} diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/ReflectionUtilities.java b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/ReflectionUtilities.java new file mode 100644 index 0000000..126ba20 --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/ReflectionUtilities.java @@ -0,0 +1,32 @@ +package io.github.finoid.snapshots.jackson2; + +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; + +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.stream.Stream; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class ReflectionUtilities { + + // FIXME consider guava reflection instead + public static Method getMethod(Class clazz, String methodName) { + try { + return Stream.of(clazz.getDeclaredMethods()) + .filter(method -> method.getName().equals(methodName)) + .findFirst() + .orElseThrow(() -> new NoSuchMethodException("Not Found")); + } catch (NoSuchMethodException e) { + return Optional.ofNullable(clazz.getSuperclass()) + .map(superclass -> getMethod(superclass, methodName)) + .orElseThrow( + () -> + new SnapshotMatchException( + "Could not find method " + + methodName + + " on class " + + clazz + + "\nPlease annotate your test method with @Test and make it without any parameters!")); + } + } +} diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/BaseEntity.java b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/BaseEntity.java similarity index 51% rename from java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/BaseEntity.java rename to snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/BaseEntity.java index 0395a40..887f9ae 100644 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/docs/BaseEntity.java +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/BaseEntity.java @@ -1,15 +1,16 @@ -package au.com.origin.snapshots.jackson.docs; +package io.github.finoid.snapshots.jackson2.docs; -import java.time.Instant; import lombok.AllArgsConstructor; import lombok.Data; +import java.time.Instant; + // Example base class used by all hibernate entities @Data @AllArgsConstructor public class BaseEntity { - private Long id; - private Instant createdDate; - private Instant lastModifiedDate; - private String somethingElse; + private Long id; + private Instant createdDate; + private Instant lastModifiedDate; + private String somethingElse; } diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/CustomSerializerTest.java b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/CustomSerializerTest.java new file mode 100644 index 0000000..b787b5f --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/CustomSerializerTest.java @@ -0,0 +1,26 @@ +package io.github.finoid.snapshots.jackson2.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.time.Instant; + +public class CustomSerializerTest { + + @Test + public void test1(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier( + new PropertyResolvingSnapshotConfig(), testInfo.getTestClass().get(), false); + + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect + .serializer(HibernateSnapshotSerializer.class) + .toMatchSnapshot(new BaseEntity(1L, Instant.now(), Instant.now(), "This should render")); + + snapshotVerifier.validateSnapshots(); + } +} diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/HibernateSnapshotSerializer.java b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/HibernateSnapshotSerializer.java new file mode 100644 index 0000000..8e6f395 --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/HibernateSnapshotSerializer.java @@ -0,0 +1,40 @@ +package io.github.finoid.snapshots.jackson2.docs; + +import io.github.finoid.snapshots.jackson2.serializers.DeterministicJacksonSnapshotSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreType; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.time.Instant; +import java.util.List; +import java.util.Set; + +public class HibernateSnapshotSerializer extends DeterministicJacksonSnapshotSerializer { + + @Override + public void configure(ObjectMapper objectMapper) { + super.configure(objectMapper); + + // Ignore Hibernate Lists to prevent infinite recursion + objectMapper.addMixIn(List.class, IgnoreTypeMixin.class); + objectMapper.addMixIn(Set.class, IgnoreTypeMixin.class); + + // Ignore Fields that Hibernate generates for us automatically + objectMapper.addMixIn(BaseEntity.class, IgnoreHibernateEntityFields.class); + } + + @JsonIgnoreType + class IgnoreTypeMixin { + } + + abstract class IgnoreHibernateEntityFields { + @JsonIgnore + abstract Long getId(); + + @JsonIgnore + abstract Instant getCreatedDate(); + + @JsonIgnore + abstract Instant getLastModifiedDate(); + } +} diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/JsonAssertReporter.java b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/JsonAssertReporter.java new file mode 100644 index 0000000..a3b6396 --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/JsonAssertReporter.java @@ -0,0 +1,21 @@ +package io.github.finoid.snapshots.jackson2.docs; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.reporters.SnapshotReporter; +import io.github.finoid.snapshots.serializers.SerializerType; +import lombok.SneakyThrows; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; + +public class JsonAssertReporter implements SnapshotReporter { + @Override + public boolean supportsFormat(String outputFormat) { + return SerializerType.JSON.name().equalsIgnoreCase(outputFormat); + } + + @Override + @SneakyThrows + public void report(Snapshot previous, Snapshot current) { + JSONAssert.assertEquals(previous.getBody(), current.getBody(), JSONCompareMode.STRICT); + } +} diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/JsonObjectComparator.java b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/JsonObjectComparator.java new file mode 100644 index 0000000..33ae5a7 --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/JsonObjectComparator.java @@ -0,0 +1,19 @@ +package io.github.finoid.snapshots.jackson2.docs; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.comparators.SnapshotComparator; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; + +public class JsonObjectComparator implements SnapshotComparator { + @SneakyThrows + private static Object asObject(String snapshotName, String json) { + return new ObjectMapper().readValue(json.replaceFirst(snapshotName + "=", ""), Object.class); + } + + @Override + public boolean matches(Snapshot previous, Snapshot current) { + return asObject(previous.getName(), previous.getBody()) + .equals(asObject(current.getName(), current.getBody())); + } +} diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/__snapshots__/CustomSerializerTest.snap b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/__snapshots__/CustomSerializerTest.snap new file mode 100644 index 0000000..dc185fc --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/__snapshots__/CustomSerializerTest.snap @@ -0,0 +1,12 @@ +docs.jackson.io.github.finoid.snapshots.CustomSerializerTest.test1=[ + { + "somethingElse": "This should render" + } +] + + +io.github.finoid.snapshots.jackson2.docs.CustomSerializerTest.test1=[ + { + "somethingElse": "This should render" + } +] \ No newline at end of file diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/__snapshots__/CustomSerializerTest.snap.debug b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/__snapshots__/CustomSerializerTest.snap.debug new file mode 100644 index 0000000..fd6b3e1 --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/docs/__snapshots__/CustomSerializerTest.snap.debug @@ -0,0 +1,5 @@ +io.github.finoid.snapshots.jackson2.docs.CustomSerializerTest.test1=[ + { + "somethingElse": "This should render" + } +] \ No newline at end of file diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/DeterministicJacksonSnapshotSerializerTest.java b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/DeterministicJacksonSnapshotSerializerTest.java new file mode 100644 index 0000000..6b5d71b --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/DeterministicJacksonSnapshotSerializerTest.java @@ -0,0 +1,157 @@ +package io.github.finoid.snapshots.jackson2.serializers; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import io.github.finoid.snapshots.serializers.SerializerType; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.TreeMap; +import java.util.TreeSet; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class DeterministicJacksonSnapshotSerializerTest { + + @Test + public void shouldSerializeDifferentTypes(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier( + new PropertyResolvingSnapshotConfig(), testInfo.getTestClass().get(), false); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.serializer("orderedJson").toMatchSnapshot(new TypeDummy()); + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldSupportJsonFormat() { + Assertions.assertThat(new DeterministicJacksonSnapshotSerializer().getOutputFormat()) + .isEqualTo(SerializerType.JSON.name()); + } + + private Map nonDeterministicMap(Map target) { + final List items = + new ArrayList() { + { + add("f"); + add("a"); + add("d"); + add("e"); + add("g"); + add("b"); + add("c"); + } + }; + + int size = items.size(); + for (int i = 0; i < size; i++) { + String random = pluckRandom(items); + target.put(random, (int) random.charAt(0)); + } + return target; + } + + private Collection nonDeterministicCollection(Collection target) { + final List items = + new ArrayList() { + { + add("f"); + add("a"); + add("d"); + add("e"); + add("g"); + add("b"); + add("c"); + } + }; + + int size = items.size(); + for (int i = 0; i < size; i++) { + target.add(pluckRandom(items)); + } + + return target; + } + + private String pluckRandom(List array) { + int rnd = new Random().nextInt(array.size()); + return array.remove(rnd); + } + + private enum AnEnum { + F, + A, + D, + E, + G, + B, + C + } + + private final class TypeDummy { + private final Void aNull = null; + private final Object anObject = new Object(); + private final byte aByte = "A".getBytes()[0]; + private final short aShort = 32767; + private final int anInt = 2147483647; + private final long aLong = 9223372036854775807L; + private final float aFloat = 0.1234567F; + private final double aDouble = 1.123456789123456D; + private final boolean aBoolean = true; + private final char aChar = 'A'; + private final String string = "Hello World"; + private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); + private final LocalDate localDate = LocalDate.parse("2020-10-19"); + private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); + private final ZonedDateTime zonedDateTime = + ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); + private final AnEnum anEnum = AnEnum.A; + private final Optional presentOptional = Optional.of("Hello World"); + private final Optional emptyOptional = Optional.empty(); + private final String[] stringArray = {"f", "a", "d", "e", "g", "b", "c"}; + private final Object[] anEnumArray = Arrays.stream(AnEnum.values()).toArray(); + + // Maps + private final Map hashMap = nonDeterministicMap(new HashMap<>()); + private final Map treeMap = nonDeterministicMap(new TreeMap<>()); + private final Map linkedHashMap = nonDeterministicMap(new LinkedHashMap<>()); + + // Sets + private final Collection linkedHashSet = + nonDeterministicCollection(new LinkedHashSet<>()); + private final Collection hashSet = nonDeterministicCollection(new HashSet<>()); + private final Collection treeSet = nonDeterministicCollection(new TreeSet<>()); + + // Lists + private final Collection arrayList = nonDeterministicCollection(new ArrayList<>()); + private final Collection linkedList = nonDeterministicCollection(new LinkedList<>()); + + // Mixed Maps, Sets, Lists + private final Collection listOfCollections = + new ArrayList() { + { + add(nonDeterministicMap(new LinkedHashMap<>())); + add(nonDeterministicCollection(new LinkedHashSet<>())); + add(nonDeterministicCollection(new LinkedList<>())); + } + }; + } +} diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/JacksonSnapshotSerializerTest.java b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/JacksonSnapshotSerializerTest.java new file mode 100644 index 0000000..d01429b --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/JacksonSnapshotSerializerTest.java @@ -0,0 +1,174 @@ +package io.github.finoid.snapshots.jackson2.serializers; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotHeader; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.TreeSet; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class JacksonSnapshotSerializerTest { + + private static final SnapshotConfig DEFAULT_CONFIG = + new PropertyResolvingSnapshotConfig() { + @Override + public SnapshotSerializer getSerializer() { + return new JacksonSnapshotSerializer(); + } + }; + + private final SnapshotSerializerContext gen = + new SnapshotSerializerContext( + "test", null, new SnapshotHeader(), JacksonSnapshotSerializerTest.class, null); + + @Test + public void shouldSerializeMap() { + Map map = new HashMap<>(); + map.put("name", "John Doe"); + map.put("age", 40); + + SnapshotSerializer serializer = new JacksonSnapshotSerializer(); + Snapshot result = serializer.apply(map, gen); + Assertions.assertThat(result.getBody()) + .isEqualTo( + "[\n" + + " {\n" + + " \"age\": 40,\n" + + " \"name\": \"John Doe\"\n" + + " }\n" + + "]"); + } + + @Test + public void shouldSerializeDifferentTypes(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(new TypeDummy()); + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldSupportJsonFormat() { + Assertions.assertThat(new JacksonSnapshotSerializer().getOutputFormat()) + .isEqualTo(SerializerType.JSON.name()); + } + + private Map deterministicMap(Map target) { + final List items = + new ArrayList() { + { + add("f"); + add("a"); + add("d"); + add("e"); + add("g"); + add("b"); + add("c"); + } + }; + items.forEach(it -> target.put(it, (int) it.charAt(0))); + return target; + } + + private Collection deterministicCollection(Collection target) { + final List items = + new ArrayList() { + { + add("f"); + add("a"); + add("d"); + add("e"); + add("g"); + add("b"); + add("c"); + } + }; + target.addAll(items); + return target; + } + + private enum AnEnum { + F, + A, + D, + E, + G, + B, + C + } + + private final class TypeDummy { + private final Void aNull = null; + private final Object anObject = new Object(); + private final byte aByte = "A".getBytes()[0]; + private final short aShort = 32767; + private final int anInt = 2147483647; + private final long aLong = 9223372036854775807L; + private final float aFloat = 0.1234567F; + private final double aDouble = 1.123456789123456D; + private final boolean aBoolean = true; + private final char aChar = 'A'; + private final String string = "Hello World"; + private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); + private final LocalDate localDate = LocalDate.parse("2020-10-19"); + private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); + private final ZonedDateTime zonedDateTime = + ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); + private final AnEnum anEnum = AnEnum.A; + private final Optional presentOptional = Optional.of("Hello World"); + private final Optional emptyOptional = Optional.empty(); + private final String[] stringArray = {"f", "a", "d", "e", "g", "b", "c"}; + private final Object[] anEnumArray = Arrays.stream(AnEnum.values()).toArray(); + + // Maps + private final Map hashMap = deterministicMap(new HashMap<>()); + private final Map treeMap = deterministicMap(new TreeMap<>()); + private final Map linkedHashMap = deterministicMap(new LinkedHashMap<>()); + + // Sets + private final Collection linkedHashSet = deterministicCollection(new LinkedHashSet<>()); + private final Collection hashSet = deterministicCollection(new HashSet<>()); + private final Collection treeSet = deterministicCollection(new TreeSet<>()); + + // Lists + private final Collection arrayList = deterministicCollection(new ArrayList<>()); + private final Collection linkedList = deterministicCollection(new LinkedList<>()); + + // Mixed Maps, Sets, Lists + private final Collection listOfCollections = + new ArrayList() { + { + add(deterministicMap(new LinkedHashMap<>())); + add(deterministicCollection(new LinkedHashSet<>())); + add(deterministicCollection(new LinkedList<>())); + } + }; + } +} diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap new file mode 100644 index 0000000..dd625f8 --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap @@ -0,0 +1,424 @@ +io.github.finoid.snapshots.jackson.serializers.DeterministicJacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "aBoolean": true, + "aByte": 65, + "aChar": "A", + "aDouble": 1.123456789123456, + "aFloat": 0.1234567, + "aLong": 9223372036854775807, + "aShort": 32767, + "anEnum": "A", + "anEnumArray": [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "anInt": 2147483647, + "anObject": { }, + "arrayList": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "date": "2020-10-19T22:21:07.103+00:00", + "emptyOptional": null, + "hashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "hashSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedHashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "linkedHashSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedList": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "listOfCollections": [ + { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ] + ], + "localDate": "2020-10-19", + "localDateTime": "2020-10-19T22:21:07.103", + "presentOptional": "Hello World", + "string": "Hello World", + "stringArray": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "treeMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "treeSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" + } +] + + +io.github.finoid.snapshots.jackson2.serializers.DeterministicJacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "aBoolean": true, + "aByte": 65, + "aChar": "A", + "aDouble": 1.123456789123456, + "aFloat": 0.1234567, + "aLong": 9223372036854775807, + "aShort": 32767, + "anEnum": "A", + "anEnumArray": [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "anInt": 2147483647, + "anObject": { }, + "arrayList": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "date": "2020-10-19T22:21:07.103+00:00", + "emptyOptional": null, + "hashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "hashSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedHashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "linkedHashSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedList": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "listOfCollections": [ + { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ] + ], + "localDate": "2020-10-19", + "localDateTime": "2020-10-19T22:21:07.103", + "presentOptional": "Hello World", + "string": "Hello World", + "stringArray": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "treeMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "treeSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" + } +] + + +serializers.jackson.io.github.finoid.snapshots.DeterministicJacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "aBoolean": true, + "aByte": 65, + "aChar": "A", + "aDouble": 1.123456789123456, + "aFloat": 0.1234567, + "aLong": 9223372036854775807, + "aShort": 32767, + "anEnum": "A", + "anEnumArray": [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "anInt": 2147483647, + "anObject": { }, + "arrayList": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "date": "2020-10-19T22:21:07.103+00:00", + "emptyOptional": null, + "hashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "hashSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedHashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "linkedHashSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedList": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "listOfCollections": [ + { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ] + ], + "localDate": "2020-10-19", + "localDateTime": "2020-10-19T22:21:07.103", + "presentOptional": "Hello World", + "string": "Hello World", + "stringArray": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "treeMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "treeSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" + } +] \ No newline at end of file diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap.debug similarity index 94% rename from java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap rename to snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap.debug index 8f171f3..f3b512a 100644 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap.debug @@ -1,4 +1,4 @@ -au.com.origin.snapshots.jackson.serializers.DeterministicJacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ +io.github.finoid.snapshots.jackson2.serializers.DeterministicJacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ { "aBoolean": true, "aByte": 65, diff --git a/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap new file mode 100644 index 0000000..97d087f --- /dev/null +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap @@ -0,0 +1,424 @@ +io.github.finoid.snapshots.jackson.serializers.JacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "anObject": { }, + "aByte": 65, + "aShort": 32767, + "anInt": 2147483647, + "aLong": 9223372036854775807, + "aFloat": 0.1234567, + "aDouble": 1.123456789123456, + "aBoolean": true, + "aChar": "A", + "string": "Hello World", + "date": "2020-10-19T22:21:07.103+00:00", + "localDate": "2020-10-19", + "localDateTime": "2020-10-19T22:21:07.103", + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]", + "anEnum": "A", + "presentOptional": "Hello World", + "emptyOptional": null, + "stringArray": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "anEnumArray": [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "hashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "treeMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "linkedHashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "linkedHashSet": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "hashSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "treeSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "arrayList": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "linkedList": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "listOfCollections": [ + { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ] + ] + } +] + + +io.github.finoid.snapshots.jackson2.serializers.JacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "anObject": { }, + "aByte": 65, + "aShort": 32767, + "anInt": 2147483647, + "aLong": 9223372036854775807, + "aFloat": 0.1234567, + "aDouble": 1.123456789123456, + "aBoolean": true, + "aChar": "A", + "string": "Hello World", + "date": "2020-10-19T22:21:07.103+00:00", + "localDate": "2020-10-19", + "localDateTime": "2020-10-19T22:21:07.103", + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]", + "anEnum": "A", + "presentOptional": "Hello World", + "emptyOptional": null, + "stringArray": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "anEnumArray": [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "hashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "treeMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "linkedHashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "linkedHashSet": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "hashSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "treeSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "arrayList": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "linkedList": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "listOfCollections": [ + { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ] + ] + } +] + + +serializers.jackson.io.github.finoid.snapshots.JacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "anObject": { }, + "aByte": 65, + "aShort": 32767, + "anInt": 2147483647, + "aLong": 9223372036854775807, + "aFloat": 0.1234567, + "aDouble": 1.123456789123456, + "aBoolean": true, + "aChar": "A", + "string": "Hello World", + "date": "2020-10-19T22:21:07.103+00:00", + "localDate": "2020-10-19", + "localDateTime": "2020-10-19T22:21:07.103", + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]", + "anEnum": "A", + "presentOptional": "Hello World", + "emptyOptional": null, + "stringArray": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "anEnumArray": [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "hashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "treeMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "linkedHashMap": { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + "linkedHashSet": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "hashSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "treeSet": [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "arrayList": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "linkedList": [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "listOfCollections": [ + { + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103 + }, + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ] + ] + } +] \ No newline at end of file diff --git a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap.debug similarity index 94% rename from java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap rename to snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap.debug index 715e791..82260b9 100644 --- a/java-snapshot-testing-plugin-jackson/src/test/java/au/com/origin/snapshots/jackson/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap +++ b/snapshot-testing-jackson2/src/test/java/io/github/finoid/snapshots/jackson2/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap.debug @@ -1,4 +1,4 @@ -au.com.origin.snapshots.jackson.serializers.JacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ +io.github.finoid.snapshots.jackson2.serializers.JacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ { "anObject": { }, "aByte": 65, diff --git a/snapshot-testing-jackson2/src/test/resources/snapshot.properties b/snapshot-testing-jackson2/src/test/resources/snapshot.properties new file mode 100644 index 0000000..8fee0cb --- /dev/null +++ b/snapshot-testing-jackson2/src/test/resources/snapshot.properties @@ -0,0 +1,8 @@ +serializer=io.github.finoid.snapshots.jackson2.serializers.JacksonSnapshotSerializer +serializer.orderedJson=io.github.finoid.snapshots.jackson2.serializers.DeterministicJacksonSnapshotSerializer +comparator=io.github.finoid.snapshots.comparators.PlainTextEqualsComparator +reporters=io.github.finoid.snapshots.reporters.PlainTextSnapshotReporter +snapshot-dir=__snapshots__ +output-dir=src/test/java +ci-env-var=CI +update-snapshot=none \ No newline at end of file diff --git a/snapshot-testing-jackson3/pom.xml b/snapshot-testing-jackson3/pom.xml new file mode 100644 index 0000000..2363ffe --- /dev/null +++ b/snapshot-testing-jackson3/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + io.github.finoid + snapshot-testing-parent + ${revision} + ../pom.xml + + + snapshot-testing-jackson3 + finoid-snapshot-testing-jackson3 + Jackson3 library for snapshot-testing + + + 21 + 21 + 21 + 21 + UTF-8 + + 3.11.1 + 6.0.2 + 1.5.3 + + + + + org.checkerframework + checker-qual + + + io.github.finoid + snapshot-testing-core + + + org.slf4j + slf4j-api + ${slf4j.version} + + + tools.jackson.core + jackson-core + ${jackson3.version} + provided + + + tools.jackson.core + jackson-databind + ${jackson3.version} + provided + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.junit.jupiter + junit-jupiter-api + ${junit6.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit6.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit6.version} + test + + + org.skyscreamer + jsonassert + ${jsonassert.version} + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + \ No newline at end of file diff --git a/snapshot-testing-jackson3/src/main/java/io/github/finoid/snapshots/jackson3/serializers/v1/DeterministicCollectionModule.java b/snapshot-testing-jackson3/src/main/java/io/github/finoid/snapshots/jackson3/serializers/v1/DeterministicCollectionModule.java new file mode 100644 index 0000000..3136d93 --- /dev/null +++ b/snapshot-testing-jackson3/src/main/java/io/github/finoid/snapshots/jackson3/serializers/v1/DeterministicCollectionModule.java @@ -0,0 +1,63 @@ +package io.github.finoid.snapshots.jackson3.serializers.v1; + +import lombok.extern.slf4j.Slf4j; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.module.SimpleModule; + +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Inspired by: https://www.stubbornjava.com/posts/creating-a-somewhat-deterministic-jackson-objectmapper. + */ +@Slf4j +public class DeterministicCollectionModule extends SimpleModule { + + public DeterministicCollectionModule() { + addSerializer(Collection.class, new CollectionSerializer()); + } + + /** + * Collections gets converted into a sorted Object[]. This then gets serialized using the default + * Array serializer. + */ + private static class CollectionSerializer extends ValueSerializer> { + + @Override + public void serialize(Collection value, JsonGenerator gen, SerializationContext ctxt) + throws JacksonException { + Object[] sorted = convert(value); + + if (value == null) { + ctxt.getDefaultNullValueSerializer().serialize(null, gen, ctxt); + } else { + ctxt.findTypedValueSerializer(Object[].class, true).serialize(sorted, gen, ctxt); + } + } + + private Object[] convert(Collection value) { + if (value == null || value.isEmpty()) { + return Collections.emptyList().toArray(); + } + + try { + return value.stream() + .filter(Objects::nonNull) + .sorted() + .collect(Collectors.toList()) + .toArray(); + } catch (ClassCastException ex) { + log.warn( + "Unable to sort() collection - this may result in a non deterministic snapshot.\n" + + "Consider adding a custom serializer for this type via the JacksonSnapshotSerializer#configure() method.\n" + + ex.getMessage()); + return value.toArray(); + } + } + } +} diff --git a/snapshot-testing-jackson3/src/main/java/io/github/finoid/snapshots/jackson3/serializers/v1/DeterministicJacksonSnapshotSerializer.java b/snapshot-testing-jackson3/src/main/java/io/github/finoid/snapshots/jackson3/serializers/v1/DeterministicJacksonSnapshotSerializer.java new file mode 100644 index 0000000..7fb4566 --- /dev/null +++ b/snapshot-testing-jackson3/src/main/java/io/github/finoid/snapshots/jackson3/serializers/v1/DeterministicJacksonSnapshotSerializer.java @@ -0,0 +1,25 @@ +package io.github.finoid.snapshots.jackson3.serializers.v1; + +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.json.JsonMapper; + +/** + * Attempts to deterministically render a snapshot. + * + *

This can help in situations where collections are rendering in a different order on subsequent + * runs. + * + *

Note that collections will be ordered which mar or may not be desirable given your use case. + */ +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class DeterministicJacksonSnapshotSerializer extends JacksonSnapshotSerializer { + + /** + * @param builder the builder to be built + */ + @Override + public void configure(final JsonMapper.Builder builder) { + builder.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); + builder.addModule(new DeterministicCollectionModule()); + } +} diff --git a/snapshot-testing-jackson3/src/main/java/io/github/finoid/snapshots/jackson3/serializers/v1/JacksonSnapshotSerializer.java b/snapshot-testing-jackson3/src/main/java/io/github/finoid/snapshots/jackson3/serializers/v1/JacksonSnapshotSerializer.java new file mode 100644 index 0000000..7035308 --- /dev/null +++ b/snapshot-testing-jackson3/src/main/java/io/github/finoid/snapshots/jackson3/serializers/v1/JacksonSnapshotSerializer.java @@ -0,0 +1,99 @@ +package io.github.finoid.snapshots.jackson3.serializers.v1; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.exceptions.SnapshotExtensionException; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import tools.jackson.core.util.DefaultIndenter; +import tools.jackson.core.util.DefaultPrettyPrinter; +import tools.jackson.core.util.Separators; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.cfg.DateTimeFeature; +import tools.jackson.databind.json.JsonMapper; + +import java.util.Collections; +import java.util.List; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class JacksonSnapshotSerializer implements SnapshotSerializer { + static DefaultPrettyPrinter.Indenter lfOnlyIndenter = new DefaultIndenter(" ", "\n"); + private static final DefaultPrettyPrinter pp = new DefaultPrettyPrinter() { + { + this.indentArraysWith(lfOnlyIndenter); + this.indentObjectsWith(lfOnlyIndenter); + + Separators separators = Separators.createDefaultInstance() + .withRootSeparator(""); + this.withSeparators(separators); + } + + // It's a requirement + // @see https://github.com/FasterXML/jackson-databind/issues/2203 + public DefaultPrettyPrinter createInstance() { + return new DefaultPrettyPrinter(this); + } + }.withArrayIndenter(lfOnlyIndenter); + private final JsonMapper jsonMapper = createMapper(); + + private JsonMapper createMapper() { + JsonMapper.Builder builder = + JsonMapper.builder() + .defaultPrettyPrinter(pp) + .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) + .enable(DateTimeFeature.WRITE_DATES_WITH_ZONE_ID) + .disable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS) + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL)) + .changeDefaultPropertyInclusion(incl -> incl.withContentInclusion(JsonInclude.Include.NON_NULL)) + .changeDefaultVisibility(visibility -> + visibility.withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withSetterVisibility(JsonAutoDetect.Visibility.NONE) + .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)); + + if (shouldFindAndRegisterModules()) { + builder.findAndAddModules(); + } + + configure(builder); + + return builder.build(); + } + + /** + * Override to customize the Jackson jsonMapper + * + * @param builder the builder to be built + */ + public void configure(final JsonMapper.Builder builder) { + } + + /** + * Override to control the registration of all available jackson2 modules within the classpath + * which are locatable via JDK ServiceLoader facility, along with module-provided SPI. + */ + protected boolean shouldFindAndRegisterModules() { + return true; + } + + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + try { + List objects = Collections.singletonList(object); + + String body = jsonMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(objects); + return gen.toSnapshot(body); + } catch (Exception e) { + throw new SnapshotExtensionException("Jackson Serialization failed", e); + } + } + + @Override + public String getOutputFormat() { + return SerializerType.JSON.name(); + } +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/NoNameChangeTest.java b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/NoNameChangeTest.java new file mode 100644 index 0000000..0138c4c --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/NoNameChangeTest.java @@ -0,0 +1,24 @@ +package io.github.finoid.snapshots.jackson3; + +import io.github.finoid.snapshots.jackson3.serializers.v1.DeterministicJacksonSnapshotSerializer; +import io.github.finoid.snapshots.jackson3.serializers.v1.JacksonSnapshotSerializer; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * These classes are likely defined in snapshot.properties as a string. + * + *

The clients IDE will not complain if they change so ensure they don't + */ +public class NoNameChangeTest { + + @Test + public void serializersApiShouldNotChange() { + assertThat(JacksonSnapshotSerializer.class.getName()) + .isEqualTo("io.github.finoid.snapshots.jackson3.serializers.v1.JacksonSnapshotSerializer"); + assertThat(DeterministicJacksonSnapshotSerializer.class.getName()) + .isEqualTo( + "io.github.finoid.snapshots.jackson3.serializers.v1.DeterministicJacksonSnapshotSerializer"); + } +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/ReflectionUtilities.java b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/ReflectionUtilities.java new file mode 100644 index 0000000..42f66eb --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/ReflectionUtilities.java @@ -0,0 +1,32 @@ +package io.github.finoid.snapshots.jackson3; + +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; + +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.stream.Stream; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class ReflectionUtilities { + + // FIXME consider guava reflection instead + public static Method getMethod(Class clazz, String methodName) { + try { + return Stream.of(clazz.getDeclaredMethods()) + .filter(method -> method.getName().equals(methodName)) + .findFirst() + .orElseThrow(() -> new NoSuchMethodException("Not Found")); + } catch (NoSuchMethodException e) { + return Optional.ofNullable(clazz.getSuperclass()) + .map(superclass -> getMethod(superclass, methodName)) + .orElseThrow( + () -> + new SnapshotMatchException( + "Could not find method " + + methodName + + " on class " + + clazz + + "\nPlease annotate your test method with @Test and make it without any parameters!")); + } + } +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/BaseEntity.java b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/BaseEntity.java new file mode 100644 index 0000000..7ca974e --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/BaseEntity.java @@ -0,0 +1,16 @@ +package io.github.finoid.snapshots.jackson3.docs; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.time.Instant; + +// Example base class used by all hibernate entities +@Data +@AllArgsConstructor +public class BaseEntity { + private Long id; + private Instant createdDate; + private Instant lastModifiedDate; + private String somethingElse; +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/CustomSerializerTest.java b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/CustomSerializerTest.java new file mode 100644 index 0000000..bd0caee --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/CustomSerializerTest.java @@ -0,0 +1,26 @@ +package io.github.finoid.snapshots.jackson3.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.time.Instant; + +public class CustomSerializerTest { + + @Test + public void test1(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier( + new PropertyResolvingSnapshotConfig(), testInfo.getTestClass().get(), false); + + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect + .serializer(HibernateSnapshotSerializer.class) + .toMatchSnapshot(new BaseEntity(1L, Instant.now(), Instant.now(), "This should render")); + + snapshotVerifier.validateSnapshots(); + } +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/HibernateSnapshotSerializer.java b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/HibernateSnapshotSerializer.java new file mode 100644 index 0000000..407b453 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/HibernateSnapshotSerializer.java @@ -0,0 +1,40 @@ +package io.github.finoid.snapshots.jackson3.docs; + +import io.github.finoid.snapshots.jackson3.serializers.v1.DeterministicJacksonSnapshotSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreType; +import tools.jackson.databind.json.JsonMapper; + +import java.time.Instant; +import java.util.List; +import java.util.Set; + +public class HibernateSnapshotSerializer extends DeterministicJacksonSnapshotSerializer { + + @Override + public void configure(final JsonMapper.Builder builder) { + super.configure(builder); + + // Ignore Hibernate Lists to prevent infinite recursion + builder.addMixIn(List.class, IgnoreTypeMixin.class); + builder.addMixIn(Set.class, IgnoreTypeMixin.class); + + // Ignore Fields that Hibernate generates for us automatically + builder.addMixIn(BaseEntity.class, IgnoreHibernateEntityFields.class); + } + + @JsonIgnoreType + class IgnoreTypeMixin { + } + + abstract class IgnoreHibernateEntityFields { + @JsonIgnore + abstract Long getId(); + + @JsonIgnore + abstract Instant getCreatedDate(); + + @JsonIgnore + abstract Instant getLastModifiedDate(); + } +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/JsonAssertReporter.java b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/JsonAssertReporter.java new file mode 100644 index 0000000..8c333ff --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/JsonAssertReporter.java @@ -0,0 +1,21 @@ +package io.github.finoid.snapshots.jackson3.docs; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.reporters.SnapshotReporter; +import io.github.finoid.snapshots.serializers.SerializerType; +import lombok.SneakyThrows; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; + +public class JsonAssertReporter implements SnapshotReporter { + @Override + public boolean supportsFormat(String outputFormat) { + return SerializerType.JSON.name().equalsIgnoreCase(outputFormat); + } + + @Override + @SneakyThrows + public void report(Snapshot previous, Snapshot current) { + JSONAssert.assertEquals(previous.getBody(), current.getBody(), JSONCompareMode.STRICT); + } +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/JsonObjectComparator.java b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/JsonObjectComparator.java new file mode 100644 index 0000000..effe592 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/JsonObjectComparator.java @@ -0,0 +1,19 @@ +package io.github.finoid.snapshots.jackson3.docs; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.comparators.SnapshotComparator; +import lombok.SneakyThrows; +import tools.jackson.databind.json.JsonMapper; + +public class JsonObjectComparator implements SnapshotComparator { + @SneakyThrows + private static Object asObject(String snapshotName, String json) { + return new JsonMapper().readValue(json.replaceFirst(snapshotName + "=", ""), Object.class); + } + + @Override + public boolean matches(Snapshot previous, Snapshot current) { + return asObject(previous.getName(), previous.getBody()) + .equals(asObject(current.getName(), current.getBody())); + } +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/__snapshots__/CustomSerializerTest.snap b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/__snapshots__/CustomSerializerTest.snap new file mode 100644 index 0000000..bc6e0e4 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/__snapshots__/CustomSerializerTest.snap @@ -0,0 +1,12 @@ +docs.jackson3.io.github.finoid.snapshots.CustomSerializerTest.test1=[ + { + "somethingElse" : "This should render" + } +] + + +io.github.finoid.snapshots.jackson3.docs.CustomSerializerTest.test1=[ + { + "somethingElse" : "This should render" + } +] \ No newline at end of file diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/__snapshots__/CustomSerializerTest.snap.debug b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/__snapshots__/CustomSerializerTest.snap.debug new file mode 100644 index 0000000..e1f7119 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/docs/__snapshots__/CustomSerializerTest.snap.debug @@ -0,0 +1,5 @@ +io.github.finoid.snapshots.jackson3.docs.CustomSerializerTest.test1=[ + { + "somethingElse" : "This should render" + } +] \ No newline at end of file diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/DeterministicJacksonSnapshotSerializerTest.java b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/DeterministicJacksonSnapshotSerializerTest.java new file mode 100644 index 0000000..834d958 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/DeterministicJacksonSnapshotSerializerTest.java @@ -0,0 +1,157 @@ +package io.github.finoid.snapshots.jackson3.serializers.v1; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import io.github.finoid.snapshots.serializers.SerializerType; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.TreeMap; +import java.util.TreeSet; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class DeterministicJacksonSnapshotSerializerTest { + + @Test + public void shouldSerializeDifferentTypes(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier( + new PropertyResolvingSnapshotConfig(), testInfo.getTestClass().get(), false); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.serializer("orderedJson").toMatchSnapshot(new TypeDummy()); + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldSupportJsonFormat() { + Assertions.assertThat(new DeterministicJacksonSnapshotSerializer().getOutputFormat()) + .isEqualTo(SerializerType.JSON.name()); + } + + private Map nonDeterministicMap(Map target) { + final List items = + new ArrayList() { + { + add("f"); + add("a"); + add("d"); + add("e"); + add("g"); + add("b"); + add("c"); + } + }; + + int size = items.size(); + for (int i = 0; i < size; i++) { + String random = pluckRandom(items); + target.put(random, (int) random.charAt(0)); + } + return target; + } + + private Collection nonDeterministicCollection(Collection target) { + final List items = + new ArrayList() { + { + add("f"); + add("a"); + add("d"); + add("e"); + add("g"); + add("b"); + add("c"); + } + }; + + int size = items.size(); + for (int i = 0; i < size; i++) { + target.add(pluckRandom(items)); + } + + return target; + } + + private String pluckRandom(List array) { + int rnd = new Random().nextInt(array.size()); + return array.remove(rnd); + } + + private enum AnEnum { + F, + A, + D, + E, + G, + B, + C + } + + private final class TypeDummy { + private final Void aNull = null; + private final Object anObject = new Object(); + private final byte aByte = "A".getBytes()[0]; + private final short aShort = 32767; + private final int anInt = 2147483647; + private final long aLong = 9223372036854775807L; + private final float aFloat = 0.1234567F; + private final double aDouble = 1.123456789123456D; + private final boolean aBoolean = true; + private final char aChar = 'A'; + private final String string = "Hello World"; + private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); + private final LocalDate localDate = LocalDate.parse("2020-10-19"); + private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); + private final ZonedDateTime zonedDateTime = + ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); + private final AnEnum anEnum = AnEnum.A; + private final Optional presentOptional = Optional.of("Hello World"); + private final Optional emptyOptional = Optional.empty(); + private final String[] stringArray = {"f", "a", "d", "e", "g", "b", "c"}; + private final Object[] anEnumArray = Arrays.stream(AnEnum.values()).toArray(); + + // Maps + private final Map hashMap = nonDeterministicMap(new HashMap<>()); + private final Map treeMap = nonDeterministicMap(new TreeMap<>()); + private final Map linkedHashMap = nonDeterministicMap(new LinkedHashMap<>()); + + // Sets + private final Collection linkedHashSet = + nonDeterministicCollection(new LinkedHashSet<>()); + private final Collection hashSet = nonDeterministicCollection(new HashSet<>()); + private final Collection treeSet = nonDeterministicCollection(new TreeSet<>()); + + // Lists + private final Collection arrayList = nonDeterministicCollection(new ArrayList<>()); + private final Collection linkedList = nonDeterministicCollection(new LinkedList<>()); + + // Mixed Maps, Sets, Lists + private final Collection listOfCollections = + new ArrayList() { + { + add(nonDeterministicMap(new LinkedHashMap<>())); + add(nonDeterministicCollection(new LinkedHashSet<>())); + add(nonDeterministicCollection(new LinkedList<>())); + } + }; + } +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/JacksonSnapshotSerializerTest.java b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/JacksonSnapshotSerializerTest.java new file mode 100644 index 0000000..4319301 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/JacksonSnapshotSerializerTest.java @@ -0,0 +1,174 @@ +package io.github.finoid.snapshots.jackson3.serializers.v1; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotHeader; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.TreeSet; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class JacksonSnapshotSerializerTest { + + private static final SnapshotConfig DEFAULT_CONFIG = + new PropertyResolvingSnapshotConfig() { + @Override + public SnapshotSerializer getSerializer() { + return new JacksonSnapshotSerializer(); + } + }; + + private final SnapshotSerializerContext gen = + new SnapshotSerializerContext( + "test", null, new SnapshotHeader(), JacksonSnapshotSerializerTest.class, null); + + @Test + public void shouldSerializeMap() { + Map map = new HashMap<>(); + map.put("name", "John Doe"); + map.put("age", 40); + + SnapshotSerializer serializer = new JacksonSnapshotSerializer(); + Snapshot result = serializer.apply(map, gen); + Assertions.assertThat(result.getBody()) + .isEqualTo( + "[\n" + + " {\n" + + " \"age\" : 40,\n" + + " \"name\" : \"John Doe\"\n" + + " }\n" + + "]"); + } + + @Test + public void shouldSerializeDifferentTypes(TestInfo testInfo) { + SnapshotVerifier snapshotVerifier = + new SnapshotVerifier(DEFAULT_CONFIG, testInfo.getTestClass().get()); + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot(new TypeDummy()); + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldSupportJsonFormat() { + Assertions.assertThat(new JacksonSnapshotSerializer().getOutputFormat()) + .isEqualTo(SerializerType.JSON.name()); + } + + private Map deterministicMap(Map target) { + final List items = + new ArrayList() { + { + add("f"); + add("a"); + add("d"); + add("e"); + add("g"); + add("b"); + add("c"); + } + }; + items.forEach(it -> target.put(it, (int) it.charAt(0))); + return target; + } + + private Collection deterministicCollection(Collection target) { + final List items = + new ArrayList() { + { + add("f"); + add("a"); + add("d"); + add("e"); + add("g"); + add("b"); + add("c"); + } + }; + target.addAll(items); + return target; + } + + private enum AnEnum { + F, + A, + D, + E, + G, + B, + C + } + + private final class TypeDummy { + private final Void aNull = null; + private final Object anObject = new Object(); + private final byte aByte = "A".getBytes()[0]; + private final short aShort = 32767; + private final int anInt = 2147483647; + private final long aLong = 9223372036854775807L; + private final float aFloat = 0.1234567F; + private final double aDouble = 1.123456789123456D; + private final boolean aBoolean = true; + private final char aChar = 'A'; + private final String string = "Hello World"; + private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); + private final LocalDate localDate = LocalDate.parse("2020-10-19"); + private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); + private final ZonedDateTime zonedDateTime = + ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); + private final AnEnum anEnum = AnEnum.A; + private final Optional presentOptional = Optional.of("Hello World"); + private final Optional emptyOptional = Optional.empty(); + private final String[] stringArray = {"f", "a", "d", "e", "g", "b", "c"}; + private final Object[] anEnumArray = Arrays.stream(AnEnum.values()).toArray(); + + // Maps + private final Map hashMap = deterministicMap(new HashMap<>()); + private final Map treeMap = deterministicMap(new TreeMap<>()); + private final Map linkedHashMap = deterministicMap(new LinkedHashMap<>()); + + // Sets + private final Collection linkedHashSet = deterministicCollection(new LinkedHashSet<>()); + private final Collection hashSet = deterministicCollection(new HashSet<>()); + private final Collection treeSet = deterministicCollection(new TreeSet<>()); + + // Lists + private final Collection arrayList = deterministicCollection(new ArrayList<>()); + private final Collection linkedList = deterministicCollection(new LinkedList<>()); + + // Mixed Maps, Sets, Lists + private final Collection listOfCollections = + new ArrayList() { + { + add(deterministicMap(new LinkedHashMap<>())); + add(deterministicCollection(new LinkedHashSet<>())); + add(deterministicCollection(new LinkedList<>())); + } + }; + } +} diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap new file mode 100644 index 0000000..e35c382 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap @@ -0,0 +1,282 @@ +io.github.finoid.snapshots.jackson3.serializers.v1.DeterministicJacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "aBoolean" : true, + "aByte" : 65, + "aChar" : "A", + "aDouble" : 1.123456789123456, + "aFloat" : 0.1234567, + "aLong" : 9223372036854775807, + "aShort" : 32767, + "anEnum" : "A", + "anEnumArray" : [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "anInt" : 2147483647, + "anObject" : { }, + "arrayList" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "date" : "2020-10-19T22:21:07.103Z", + "emptyOptional" : null, + "hashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "hashSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedHashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "linkedHashSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedList" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "listOfCollections" : [ + { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ] + ], + "localDate" : "2020-10-19", + "localDateTime" : "2020-10-19T22:21:07.103", + "presentOptional" : "Hello World", + "string" : "Hello World", + "stringArray" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "treeMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "treeSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "zonedDateTime" : "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" + } +] + + +v1.serializers.jackson3.io.github.finoid.snapshots.DeterministicJacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "aBoolean" : true, + "aByte" : 65, + "aChar" : "A", + "aDouble" : 1.123456789123456, + "aFloat" : 0.1234567, + "aLong" : 9223372036854775807, + "aShort" : 32767, + "anEnum" : "A", + "anEnumArray" : [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "anInt" : 2147483647, + "anObject" : { }, + "arrayList" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "date" : "2020-10-19T22:21:07.103Z", + "emptyOptional" : null, + "hashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "hashSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedHashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "linkedHashSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedList" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "listOfCollections" : [ + { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ] + ], + "localDate" : "2020-10-19", + "localDateTime" : "2020-10-19T22:21:07.103", + "presentOptional" : "Hello World", + "string" : "Hello World", + "stringArray" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "treeMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "treeSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "zonedDateTime" : "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" + } +] \ No newline at end of file diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap.debug b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap.debug new file mode 100644 index 0000000..6d3f725 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap.debug @@ -0,0 +1,140 @@ +io.github.finoid.snapshots.jackson3.serializers.v1.DeterministicJacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "aBoolean" : true, + "aByte" : 65, + "aChar" : "A", + "aDouble" : 1.123456789123456, + "aFloat" : 0.1234567, + "aLong" : 9223372036854775807, + "aShort" : 32767, + "anEnum" : "A", + "anEnumArray" : [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "anInt" : 2147483647, + "anObject" : { }, + "arrayList" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "date" : "2020-10-19T22:21:07.103Z", + "emptyOptional" : null, + "hashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "hashSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedHashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "linkedHashSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedList" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "listOfCollections" : [ + { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ] + ], + "localDate" : "2020-10-19", + "localDateTime" : "2020-10-19T22:21:07.103", + "presentOptional" : "Hello World", + "string" : "Hello World", + "stringArray" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "treeMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "treeSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "zonedDateTime" : "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" + } +] \ No newline at end of file diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/JacksonSnapshotSerializerTest.snap b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/JacksonSnapshotSerializerTest.snap new file mode 100644 index 0000000..c75264a --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/JacksonSnapshotSerializerTest.snap @@ -0,0 +1,282 @@ +io.github.finoid.snapshots.jackson3.serializers.v1.JacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "aBoolean" : true, + "aByte" : 65, + "aChar" : "A", + "aDouble" : 1.123456789123456, + "aFloat" : 0.1234567, + "aLong" : 9223372036854775807, + "aShort" : 32767, + "anEnum" : "A", + "anEnumArray" : [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "anInt" : 2147483647, + "anObject" : { }, + "arrayList" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "date" : "2020-10-19T22:21:07.103Z", + "emptyOptional" : null, + "hashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "hashSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedHashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "linkedHashSet" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "linkedList" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "listOfCollections" : [ + { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ] + ], + "localDate" : "2020-10-19", + "localDateTime" : "2020-10-19T22:21:07.103", + "presentOptional" : "Hello World", + "string" : "Hello World", + "stringArray" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "treeMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "treeSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "zonedDateTime" : "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" + } +] + + +v1.serializers.jackson3.io.github.finoid.snapshots.JacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "aBoolean" : true, + "aByte" : 65, + "aChar" : "A", + "aDouble" : 1.123456789123456, + "aFloat" : 0.1234567, + "aLong" : 9223372036854775807, + "aShort" : 32767, + "anEnum" : "A", + "anEnumArray" : [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "anInt" : 2147483647, + "anObject" : { }, + "arrayList" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "date" : "2020-10-19T22:21:07.103Z", + "emptyOptional" : null, + "hashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "hashSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedHashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "linkedHashSet" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "linkedList" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "listOfCollections" : [ + { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ] + ], + "localDate" : "2020-10-19", + "localDateTime" : "2020-10-19T22:21:07.103", + "presentOptional" : "Hello World", + "string" : "Hello World", + "stringArray" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "treeMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "treeSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "zonedDateTime" : "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" + } +] \ No newline at end of file diff --git a/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/JacksonSnapshotSerializerTest.snap.debug b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/JacksonSnapshotSerializerTest.snap.debug new file mode 100644 index 0000000..0366607 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/java/io/github/finoid/snapshots/jackson3/serializers/v1/__snapshots__/JacksonSnapshotSerializerTest.snap.debug @@ -0,0 +1,140 @@ +io.github.finoid.snapshots.jackson3.serializers.v1.JacksonSnapshotSerializerTest.shouldSerializeDifferentTypes=[ + { + "aBoolean" : true, + "aByte" : 65, + "aChar" : "A", + "aDouble" : 1.123456789123456, + "aFloat" : 0.1234567, + "aLong" : 9223372036854775807, + "aShort" : 32767, + "anEnum" : "A", + "anEnumArray" : [ + "F", + "A", + "D", + "E", + "G", + "B", + "C" + ], + "anInt" : 2147483647, + "anObject" : { }, + "arrayList" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "date" : "2020-10-19T22:21:07.103Z", + "emptyOptional" : null, + "hashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "hashSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "linkedHashMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "linkedHashSet" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "linkedList" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "listOfCollections" : [ + { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ] + ], + "localDate" : "2020-10-19", + "localDateTime" : "2020-10-19T22:21:07.103", + "presentOptional" : "Hello World", + "string" : "Hello World", + "stringArray" : [ + "f", + "a", + "d", + "e", + "g", + "b", + "c" + ], + "treeMap" : { + "a" : 97, + "b" : 98, + "c" : 99, + "d" : 100, + "e" : 101, + "f" : 102, + "g" : 103 + }, + "treeSet" : [ + "a", + "b", + "c", + "d", + "e", + "f", + "g" + ], + "zonedDateTime" : "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" + } +] \ No newline at end of file diff --git a/snapshot-testing-jackson3/src/test/resources/snapshot.properties b/snapshot-testing-jackson3/src/test/resources/snapshot.properties new file mode 100644 index 0000000..74c96d2 --- /dev/null +++ b/snapshot-testing-jackson3/src/test/resources/snapshot.properties @@ -0,0 +1,8 @@ +serializer=io.github.finoid.snapshots.jackson3.serializers.v1.JacksonSnapshotSerializer +serializer.orderedJson=io.github.finoid.snapshots.jackson3.serializers.v1.DeterministicJacksonSnapshotSerializer +comparator=io.github.finoid.snapshots.comparators.v1.PlainTextEqualsComparator +reporters=io.github.finoid.snapshots.reporters.v1.PlainTextSnapshotReporter +snapshot-dir=__snapshots__ +output-dir=src/test/java +ci-env-var=CI +update-snapshot=none \ No newline at end of file diff --git a/snapshot-testing-junit5/pom.xml b/snapshot-testing-junit5/pom.xml new file mode 100644 index 0000000..2c8b343 --- /dev/null +++ b/snapshot-testing-junit5/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + io.github.finoid + snapshot-testing-parent + ${revision} + ../pom.xml + + + snapshot-testing-junit5 + finoid-snapshot-testing-junit5 + JUnit5 library for snapshot-testing + + + 21 + 21 + 21 + 21 + UTF-8 + + + + + io.github.finoid + snapshot-testing-core + + + io.github.finoid + snapshot-testing-jackson2 + + + org.checkerframework + checker-qual + + + org.junit.jupiter + junit-jupiter-api + ${junit5.version} + provided + + + org.junit.jupiter + junit-jupiter-engine + ${junit5.version} + provided + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + + com.fasterxml.jackson.core + jackson-core + ${jackson2.version} + test + + + com.fasterxml.jackson.core + jackson-databind + ${jackson2.version} + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit5.version} + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + \ No newline at end of file diff --git a/snapshot-testing-junit5/src/main/java/io/github/finoid/snapshots/junit5/SnapshotExtension.java b/snapshot-testing-junit5/src/main/java/io/github/finoid/snapshots/junit5/SnapshotExtension.java new file mode 100644 index 0000000..22a9179 --- /dev/null +++ b/snapshot-testing-junit5/src/main/java/io/github/finoid/snapshots/junit5/SnapshotExtension.java @@ -0,0 +1,127 @@ +package io.github.finoid.snapshots.junit5; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfigInjector; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import io.github.finoid.snapshots.logging.LoggingHelper; +import io.github.finoid.snapshots.utils.ReflectionUtils; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor; +import org.junit.jupiter.engine.descriptor.ClassTestDescriptor; + +import java.lang.reflect.Field; + +@Slf4j +@SuppressWarnings({"checkstyle:all", "EmptyBlockTag", "NullAway"}) // TODO (nw) rewrite +public class SnapshotExtension + implements AfterAllCallback, + BeforeAllCallback, + SnapshotConfigInjector, + ParameterResolver, + BeforeEachCallback { + + private SnapshotVerifier snapshotVerifier; + + @Override + public void beforeAll(ExtensionContext context) { + // don't fail if a test is run alone from the IDE for example + boolean failOnOrphans = shouldFailOnOrphans(context); + Class testClass = + context + .getTestClass() + .orElseThrow(() -> new SnapshotMatchException("Unable to locate Test class")); + this.snapshotVerifier = new SnapshotVerifier(getSnapshotConfig(), testClass, failOnOrphans); + } + + @Override + public void afterAll(ExtensionContext context) { + this.snapshotVerifier.validateSnapshots(); + } + + @Override + public SnapshotConfig getSnapshotConfig() { + return new PropertyResolvingSnapshotConfig(); + } + + /** + * FIXME This is a hack until I find the correct way to determine if a test run is individual or + * as part of a class + * + * @param context + * @return + */ + private boolean shouldFailOnOrphans(ExtensionContext context) { + try { + Field field = context.getClass().getSuperclass().getDeclaredField("testDescriptor"); + field.setAccessible(true); + Object testDescriptor = field.get(context); + if (testDescriptor instanceof ClassTestDescriptor classTestDescriptor) { // Junit 5.3.2 + return classTestDescriptor.getChildren().size() > 1; + } else if (testDescriptor instanceof ClassBasedTestDescriptor classTestDescriptor) { // Junit 5.7.2 + return classTestDescriptor.getChildren().size() > 1; + } + } catch (Exception e) { + log.error( + "FAILED: (Java Snapshot Testing) Unable to get JUnit5 ClassTestDescriptor or ClassBasedTestDescriptor!\n" + + "Ensure you are using Junit5 >= 5.3.2\n" + + "This may be due to JUnit5 changing their private api as we use reflection to access it\n" + + "Log a support ticket https://github.com/origin-energy/java-snapshot-testing/issues and supply your JUnit5 version\n" + + "Setting failOnOrphans=true as this is the safest option." + + "This means that running a test alone (say from the IDE) will fail the snapshot, you need to run the entire class.", + e); + } + return true; + } + + @Override + public boolean supportsParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + boolean supports = parameterContext.getParameter().getType() == Expect.class; + if (supports) { + LoggingHelper.deprecatedV5( + log, + "Injecting 'Expect' via method a argument is no longer recommended. Consider using instance variable injection instead."); + } + return supports; + } + + @Override + public Object resolveParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return new Expect( + snapshotVerifier, + extensionContext + .getTestMethod() + .orElseThrow(() -> new RuntimeException("getTestMethod() is missing"))); + } + + @Override + public void beforeEach(ExtensionContext context) { + if (context.getTestInstance().isPresent() && context.getTestMethod().isPresent()) { + ReflectionUtils.findFieldByPredicate( + context.getTestClass().get(), (field) -> field.getType() == Expect.class) + .ifPresent( + (field) -> { + Expect expect = Expect.of(snapshotVerifier, context.getTestMethod().get()); + ReflectionUtils.makeAccessible(field); + try { + field.set(context.getTestInstance().get(), expect); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/BaseClassTest.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/BaseClassTest.java new file mode 100644 index 0000000..210c637 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/BaseClassTest.java @@ -0,0 +1,24 @@ +package io.github.finoid.snapshots.junit5; + +import io.github.finoid.snapshots.Expect; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith({SnapshotExtension.class}) +public class BaseClassTest { + + class TestBase { + Expect expect; + } + + @Nested + @ExtendWith(SnapshotExtension.class) + class NestedClass extends TestBase { + + @Test + public void helloWorldTest() { + expect.toMatchSnapshot("Hello World"); + } + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/NestedClassTest.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/NestedClassTest.java new file mode 100644 index 0000000..514f855 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/NestedClassTest.java @@ -0,0 +1,56 @@ +package io.github.finoid.snapshots.junit5; + +import io.github.finoid.snapshots.Expect; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("checkstyle:HideUtilityClassConstructor") +@Disabled // TODO (nw) rewrite +@ExtendWith({SnapshotExtension.class}) +public class NestedClassTest { + + @AfterAll + public static void afterAll() { + Path path = + Paths.get("src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTest.snap"); + assertThat(Files.exists(path)).isFalse(); + } + + @Nested + class NestedClassWithExpectArgument { + + @Test + public void helloWorldTest(Expect expect) { + expect.toMatchSnapshot("Hello World"); + } + } + + @Nested + class NestedClassWithoutSnapshot { + + @Test + public void helloWorldTest() { + assertThat(true).isTrue(); + } + } + + @Nested + class NestedClassWithExpectInstance { + + Expect expect; + + @Test + public void helloWorldTest() { + expect.toMatchSnapshot("Hello World"); + } + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/NestedClassTestWithExtends.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/NestedClassTestWithExtends.java new file mode 100644 index 0000000..c53e643 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/NestedClassTestWithExtends.java @@ -0,0 +1,37 @@ +package io.github.finoid.snapshots.junit5; + +import io.github.finoid.snapshots.Expect; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class NestedClassTestWithExtends { + + @AfterAll + public static void afterAll() { + Path path = + Paths.get( + "src/test/java/io/github/finoid/snapshots/__snapshots__/NestedClassTestWithExtends.snap"); + assertThat(Files.exists(path)).isFalse(); + } + + @ExtendWith(SnapshotExtension.class) + @Nested + class NestedClass { + + Expect expect; + + @Test + public void helloWorldTest() { + expect.toMatchSnapshot("Hello World"); + } + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/SnapshotExtensionUsedTest.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/SnapshotExtensionUsedTest.java new file mode 100644 index 0000000..57adadd --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/SnapshotExtensionUsedTest.java @@ -0,0 +1,44 @@ +package io.github.finoid.snapshots.junit5; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.annotations.SnapshotName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(SnapshotExtension.class) +public class SnapshotExtensionUsedTest { + + private Expect expect; + + @Test + public void shouldUseExtension(Expect expect) { + expect.toMatchSnapshot("Hello World"); + } + + @Test + public void shouldUseExtensionAgain(Expect expect) { + expect.toMatchSnapshot("Hello World"); + } + + @Test + public void shouldUseExtensionViaInstanceVariable() { + expect.toMatchSnapshot("Hello World"); + } + + @Test + public void shouldUseExtensionAgainViaInstanceVariable() { + expect.toMatchSnapshot("Hello World"); + } + + @SnapshotName("hello_world") + @Test + public void snapshotWithName() { + expect.toMatchSnapshot("Hello World"); + } + + @SnapshotName("hello_world_2") + @Test + public void snapshotWithNameAgain() { + expect.toMatchSnapshot("Hello World"); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/SnapshotParameterTest.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/SnapshotParameterTest.java new file mode 100644 index 0000000..1ea9369 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/SnapshotParameterTest.java @@ -0,0 +1,52 @@ +package io.github.finoid.snapshots.junit5; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.jackson2.serializers.v1.JacksonSnapshotSerializer; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +@ExtendWith({SnapshotExtension.class}) +class SnapshotParameterTest { + + private Expect expect; + + static Stream testData() { + + return Stream.of( + Arguments.of("Scenario2", "test input 1"), + Arguments.of("Scenario2", "test input 1"), + Arguments.of("Scenario2", "test input 1"), + Arguments.of("Scenario3", "test input 2"), + Arguments.of("Scenario3", "test input 2")); + } + + @ParameterizedTest + @MethodSource("io.github.finoid.snapshots.junit5.SnapshotParameterTest#testData") + void shouldSupportParameterizedTest(String scenario, String testInput, Expect expect) { + expect.toMatchSnapshot("Duplicates are OK"); + expect.toMatchSnapshot("Duplicates are OK"); + expect.scenario("Scenario1").toMatchSnapshot("Additional snapshots need to include a scenario"); + expect + .serializer(JacksonSnapshotSerializer.class) + .scenario(scenario) + .toMatchSnapshot(testInput); + } + + @ParameterizedTest + @MethodSource("io.github.finoid.snapshots.junit5.SnapshotParameterTest#testData") + void shouldSupportParameterizedTestViaInstanceVariable(String scenario, String testInput) { + this.expect.toMatchSnapshot("Duplicates are OK"); + this.expect.toMatchSnapshot("Duplicates are OK"); + this.expect + .scenario("Scenario1") + .toMatchSnapshot("Additional snapshots need to include a scenario"); + this.expect + .serializer(JacksonSnapshotSerializer.class) + .scenario(scenario) + .toMatchSnapshot(testInput); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/BaseClassTest$NestedClass.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/BaseClassTest$NestedClass.snap new file mode 100644 index 0000000..40d3301 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/BaseClassTest$NestedClass.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.junit5.BaseClassTest$NestedClass.helloWorldTest=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTest$NestedClassWithExpectArgument.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTest$NestedClassWithExpectArgument.snap new file mode 100644 index 0000000..b86cedb --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTest$NestedClassWithExpectArgument.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.junit5.NestedClassTest$NestedClassWithExpectArgument.helloWorldTest=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTest$NestedClassWithExpectInstance.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTest$NestedClassWithExpectInstance.snap new file mode 100644 index 0000000..3604f45 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTest$NestedClassWithExpectInstance.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.junit5.NestedClassTest$NestedClassWithExpectInstance.helloWorldTest=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTestWithExtends$NestedClass.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTestWithExtends$NestedClass.snap new file mode 100644 index 0000000..101f30a --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/NestedClassTestWithExtends$NestedClass.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.junit5.NestedClassTestWithExtends$NestedClass.helloWorldTest=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/SnapshotExtensionUsedTest.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/SnapshotExtensionUsedTest.snap new file mode 100644 index 0000000..15b22f9 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/SnapshotExtensionUsedTest.snap @@ -0,0 +1,28 @@ +hello_world=[ +Hello World +] + + +hello_world_2=[ +Hello World +] + + +io.github.finoid.snapshots.junit5.SnapshotExtensionUsedTest.shouldUseExtension=[ +Hello World +] + + +io.github.finoid.snapshots.junit5.SnapshotExtensionUsedTest.shouldUseExtensionAgain=[ +Hello World +] + + +io.github.finoid.snapshots.junit5.SnapshotExtensionUsedTest.shouldUseExtensionAgainViaInstanceVariable=[ +Hello World +] + + +io.github.finoid.snapshots.junit5.SnapshotExtensionUsedTest.shouldUseExtensionViaInstanceVariable=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/SnapshotParameterTest.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/SnapshotParameterTest.snap new file mode 100644 index 0000000..c58a887 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/__snapshots__/SnapshotParameterTest.snap @@ -0,0 +1,38 @@ +io.github.finoid.snapshots.junit5.SnapshotParameterTest.shouldSupportParameterizedTest=[ +Duplicates are OK +] + + +io.github.finoid.snapshots.junit5.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable=[ +Duplicates are OK +] + + +io.github.finoid.snapshots.junit5.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable[Scenario1]=[ +Additional snapshots need to include a scenario +] + + +io.github.finoid.snapshots.junit5.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable[Scenario2]=[ + "test input 1" +] + + +io.github.finoid.snapshots.junit5.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable[Scenario3]=[ + "test input 2" +] + + +io.github.finoid.snapshots.junit5.SnapshotParameterTest.shouldSupportParameterizedTest[Scenario1]=[ +Additional snapshots need to include a scenario +] + + +io.github.finoid.snapshots.junit5.SnapshotParameterTest.shouldSupportParameterizedTest[Scenario2]=[ + "test input 1" +] + + +io.github.finoid.snapshots.junit5.SnapshotParameterTest.shouldSupportParameterizedTest[Scenario3]=[ + "test input 2" +] \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/CustomFrameworkExample.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/CustomFrameworkExample.java new file mode 100644 index 0000000..9de0119 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/CustomFrameworkExample.java @@ -0,0 +1,32 @@ +package io.github.finoid.snapshots.junit5.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +// Notice we aren't using any framework extensions +public class CustomFrameworkExample { + + private static SnapshotVerifier snapshotVerifier; + + @BeforeAll + static void beforeAll() { + snapshotVerifier = + new SnapshotVerifier(new PropertyResolvingSnapshotConfig(), CustomFrameworkExample.class); + } + + @AfterAll + static void afterAll() { + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldMatchSnapshotOne(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("Hello World"); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/CustomSnapshotConfigExample.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/CustomSnapshotConfigExample.java new file mode 100644 index 0000000..3ba40a0 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/CustomSnapshotConfigExample.java @@ -0,0 +1,18 @@ +package io.github.finoid.snapshots.junit5.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.annotations.UseSnapshotConfig; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(SnapshotExtension.class) +// apply your custom snapshot configuration to this test class +@UseSnapshotConfig(LowercaseToStringSnapshotConfig.class) +public class CustomSnapshotConfigExample { + + @Test + public void myTest(Expect expect) { + expect.toMatchSnapshot("hello world"); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/JUnit5Example.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/JUnit5Example.java new file mode 100644 index 0000000..0bfe7a4 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/JUnit5Example.java @@ -0,0 +1,26 @@ +package io.github.finoid.snapshots.junit5.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +// Ensure you extend your test class with the SnapshotExtension +@ExtendWith({SnapshotExtension.class}) +public class JUnit5Example { + + // Option 1: inject Expect as an instance variable + private Expect expect; + + @Test + public void myTest1() { + // Verify your snapshot + expect.toMatchSnapshot("Hello World"); + } + + // Option 2: inject Expect into the method signature + @Test + public void myTest2(Expect expect) { + expect.toMatchSnapshot("Hello World Again"); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/JUnit5ResolutionHierarchyExample.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/JUnit5ResolutionHierarchyExample.java new file mode 100644 index 0000000..cbfeb71 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/JUnit5ResolutionHierarchyExample.java @@ -0,0 +1,34 @@ +package io.github.finoid.snapshots.junit5.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.annotations.UseSnapshotConfig; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(SnapshotExtension.class) +@UseSnapshotConfig(LowercaseToStringSnapshotConfig.class) +public class JUnit5ResolutionHierarchyExample { + + private Expect expect; + + @Test + public void aliasMethodTest() { + expect + .serializer("json") // <------ Using snapshot.properties + .toMatchSnapshot(new TestObject()); + } + + @Test + public void customSerializerTest() { + expect + .serializer(UppercaseToStringSerializer.class) // <------ Using custom serializer + .toMatchSnapshot(new TestObject()); + } + + // Read from LowercaseToStringSnapshotConfig defined on the class + @Test + public void lowercaseTest() { + expect.toMatchSnapshot(new TestObject()); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/LowercaseToStringSerializer.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/LowercaseToStringSerializer.java new file mode 100644 index 0000000..74f2e05 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/LowercaseToStringSerializer.java @@ -0,0 +1,18 @@ +package io.github.finoid.snapshots.junit5.docs; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; + +public class LowercaseToStringSerializer implements SnapshotSerializer { + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + return gen.toSnapshot(object.toString().toLowerCase()); + } + + @Override + public String getOutputFormat() { + return SerializerType.TEXT.name(); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/LowercaseToStringSnapshotConfig.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/LowercaseToStringSnapshotConfig.java new file mode 100644 index 0000000..753515b --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/LowercaseToStringSnapshotConfig.java @@ -0,0 +1,12 @@ +package io.github.finoid.snapshots.junit5.docs; + +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; + +public class LowercaseToStringSnapshotConfig extends PropertyResolvingSnapshotConfig { + + @Override + public SnapshotSerializer getSerializer() { + return new LowercaseToStringSerializer(); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/MyFirstSnapshotTest.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/MyFirstSnapshotTest.java new file mode 100644 index 0000000..bc51e98 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/MyFirstSnapshotTest.java @@ -0,0 +1,31 @@ +package io.github.finoid.snapshots.junit5.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.annotations.SnapshotName; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.HashMap; +import java.util.Map; + +@ExtendWith({SnapshotExtension.class}) +public class MyFirstSnapshotTest { + + private Expect expect; + + @SnapshotName("i_can_give_custom_names_to_my_snapshots") + @Test + public void toStringSerializationTest() { + expect.toMatchSnapshot("Hello World"); + } + + @Test + public void jsonSerializationTest() { + Map map = new HashMap<>(); + map.put("name", "John Doe"); + map.put("age", 40); + + expect.serializer("json").toMatchSnapshot(map); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/TestObject.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/TestObject.java new file mode 100644 index 0000000..c33d3d1 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/TestObject.java @@ -0,0 +1,8 @@ +package io.github.finoid.snapshots.junit5.docs; + +public class TestObject { + @Override + public String toString() { + return "This is a snapshot of the toString() method"; + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/UppercaseToStringSerializer.java b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/UppercaseToStringSerializer.java new file mode 100644 index 0000000..7bdd3aa --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/UppercaseToStringSerializer.java @@ -0,0 +1,18 @@ +package io.github.finoid.snapshots.junit5.docs; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; + +public class UppercaseToStringSerializer implements SnapshotSerializer { + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + return gen.toSnapshot(object.toString().toUpperCase()); + } + + @Override + public String getOutputFormat() { + return SerializerType.TEXT.name(); + } +} diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/CustomFrameworkExample.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/CustomFrameworkExample.snap new file mode 100644 index 0000000..1a8a465 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/CustomFrameworkExample.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.junit5.docs.CustomFrameworkExample.shouldMatchSnapshotOne=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/CustomSnapshotConfigExample.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/CustomSnapshotConfigExample.snap new file mode 100644 index 0000000..29105c7 --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/CustomSnapshotConfigExample.snap @@ -0,0 +1 @@ +io.github.finoid.snapshots.junit5.docs.CustomSnapshotConfigExample.myTest=hello world \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/CustomSnapshotContextConfigExample.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/CustomSnapshotContextConfigExample.snap new file mode 100644 index 0000000..e69de29 diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/JUnit5Example.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/JUnit5Example.snap new file mode 100644 index 0000000..f139e2e --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/JUnit5Example.snap @@ -0,0 +1,8 @@ +io.github.finoid.snapshots.junit5.docs.JUnit5Example.myTest1=[ +Hello World +] + + +io.github.finoid.snapshots.junit5.docs.JUnit5Example.myTest2=[ +Hello World Again +] \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/JUnit5ResolutionHierarchyExample.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/JUnit5ResolutionHierarchyExample.snap new file mode 100644 index 0000000..e8103ac --- /dev/null +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/JUnit5ResolutionHierarchyExample.snap @@ -0,0 +1,9 @@ +io.github.finoid.snapshots.junit5.docs.JUnit5ResolutionHierarchyExample.aliasMethodTest=[ + { } +] + + +io.github.finoid.snapshots.junit5.docs.JUnit5ResolutionHierarchyExample.customSerializerTest=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD + + +io.github.finoid.snapshots.junit5.docs.JUnit5ResolutionHierarchyExample.lowercaseTest=this is a snapshot of the tostring() method \ No newline at end of file diff --git a/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/MyFirstSnapshotContextTest.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/MyFirstSnapshotContextTest.snap new file mode 100644 index 0000000..e69de29 diff --git a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/MyFirstSnapshotTest.snap b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/MyFirstSnapshotTest.snap similarity index 55% rename from java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/MyFirstSnapshotTest.snap rename to snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/MyFirstSnapshotTest.snap index 1987c84..7183ab7 100644 --- a/java-snapshot-testing-junit5/src/test/java/au/com/origin/snapshots/docs/__snapshots__/MyFirstSnapshotTest.snap +++ b/snapshot-testing-junit5/src/test/java/io/github/finoid/snapshots/junit5/docs/__snapshots__/MyFirstSnapshotTest.snap @@ -1,11 +1,11 @@ -au.com.origin.snapshots.docs.MyFirstSnapshotTest.jsonSerializationTest=[ +i_can_give_custom_names_to_my_snapshots=[ +Hello World +] + + +io.github.finoid.snapshots.junit5.docs.MyFirstSnapshotTest.jsonSerializationTest=[ { "age": 40, "name": "John Doe" } -] - - -i_can_give_custom_names_to_my_snapshots=[ -Hello World ] \ No newline at end of file diff --git a/java-snapshot-testing-junit5/src/test/resources/junit-platform.properties b/snapshot-testing-junit5/src/test/resources/junit-platform.properties similarity index 100% rename from java-snapshot-testing-junit5/src/test/resources/junit-platform.properties rename to snapshot-testing-junit5/src/test/resources/junit-platform.properties diff --git a/snapshot-testing-junit5/src/test/resources/snapshot.properties b/snapshot-testing-junit5/src/test/resources/snapshot.properties new file mode 100644 index 0000000..66e62ff --- /dev/null +++ b/snapshot-testing-junit5/src/test/resources/snapshot.properties @@ -0,0 +1,8 @@ +serializer=io.github.finoid.snapshots.serializers.v1.ToStringSnapshotSerializer +serializer.json=io.github.finoid.snapshots.jackson2.serializers.v1.DeterministicJacksonSnapshotSerializer +comparator=io.github.finoid.snapshots.comparators.v1.PlainTextEqualsComparator +reporters=io.github.finoid.snapshots.reporters.v1.PlainTextSnapshotReporter +snapshot-dir=__snapshots__ +output-dir=src/test/java +ci-env-var=CI +update-snapshot=none \ No newline at end of file diff --git a/snapshot-testing-junit6/pom.xml b/snapshot-testing-junit6/pom.xml new file mode 100644 index 0000000..1185d31 --- /dev/null +++ b/snapshot-testing-junit6/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + io.github.finoid + snapshot-testing-parent + ${revision} + ../pom.xml + + + snapshot-testing-junit6 + finoid-snapshot-testing-junit6 + JUnit5 library for snapshot-testing + + + 21 + 21 + 21 + 21 + UTF-8 + + + + + io.github.finoid + snapshot-testing-core + + + io.github.finoid + snapshot-testing-jackson3 + + + org.checkerframework + checker-qual + + + org.junit.jupiter + junit-jupiter-api + ${junit6.version} + provided + + + org.junit.jupiter + junit-jupiter-engine + ${junit6.version} + provided + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + + tools.jackson.core + jackson-core + ${jackson3.version} + provided + + + tools.jackson.core + jackson-databind + ${jackson3.version} + provided + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit6.version} + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + \ No newline at end of file diff --git a/snapshot-testing-junit6/src/main/java/io/github/finoid/snapshots/junit5/SnapshotExtension.java b/snapshot-testing-junit6/src/main/java/io/github/finoid/snapshots/junit5/SnapshotExtension.java new file mode 100644 index 0000000..22a9179 --- /dev/null +++ b/snapshot-testing-junit6/src/main/java/io/github/finoid/snapshots/junit5/SnapshotExtension.java @@ -0,0 +1,127 @@ +package io.github.finoid.snapshots.junit5; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfig; +import io.github.finoid.snapshots.config.SnapshotConfigInjector; +import io.github.finoid.snapshots.exceptions.SnapshotMatchException; +import io.github.finoid.snapshots.logging.LoggingHelper; +import io.github.finoid.snapshots.utils.ReflectionUtils; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor; +import org.junit.jupiter.engine.descriptor.ClassTestDescriptor; + +import java.lang.reflect.Field; + +@Slf4j +@SuppressWarnings({"checkstyle:all", "EmptyBlockTag", "NullAway"}) // TODO (nw) rewrite +public class SnapshotExtension + implements AfterAllCallback, + BeforeAllCallback, + SnapshotConfigInjector, + ParameterResolver, + BeforeEachCallback { + + private SnapshotVerifier snapshotVerifier; + + @Override + public void beforeAll(ExtensionContext context) { + // don't fail if a test is run alone from the IDE for example + boolean failOnOrphans = shouldFailOnOrphans(context); + Class testClass = + context + .getTestClass() + .orElseThrow(() -> new SnapshotMatchException("Unable to locate Test class")); + this.snapshotVerifier = new SnapshotVerifier(getSnapshotConfig(), testClass, failOnOrphans); + } + + @Override + public void afterAll(ExtensionContext context) { + this.snapshotVerifier.validateSnapshots(); + } + + @Override + public SnapshotConfig getSnapshotConfig() { + return new PropertyResolvingSnapshotConfig(); + } + + /** + * FIXME This is a hack until I find the correct way to determine if a test run is individual or + * as part of a class + * + * @param context + * @return + */ + private boolean shouldFailOnOrphans(ExtensionContext context) { + try { + Field field = context.getClass().getSuperclass().getDeclaredField("testDescriptor"); + field.setAccessible(true); + Object testDescriptor = field.get(context); + if (testDescriptor instanceof ClassTestDescriptor classTestDescriptor) { // Junit 5.3.2 + return classTestDescriptor.getChildren().size() > 1; + } else if (testDescriptor instanceof ClassBasedTestDescriptor classTestDescriptor) { // Junit 5.7.2 + return classTestDescriptor.getChildren().size() > 1; + } + } catch (Exception e) { + log.error( + "FAILED: (Java Snapshot Testing) Unable to get JUnit5 ClassTestDescriptor or ClassBasedTestDescriptor!\n" + + "Ensure you are using Junit5 >= 5.3.2\n" + + "This may be due to JUnit5 changing their private api as we use reflection to access it\n" + + "Log a support ticket https://github.com/origin-energy/java-snapshot-testing/issues and supply your JUnit5 version\n" + + "Setting failOnOrphans=true as this is the safest option." + + "This means that running a test alone (say from the IDE) will fail the snapshot, you need to run the entire class.", + e); + } + return true; + } + + @Override + public boolean supportsParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + boolean supports = parameterContext.getParameter().getType() == Expect.class; + if (supports) { + LoggingHelper.deprecatedV5( + log, + "Injecting 'Expect' via method a argument is no longer recommended. Consider using instance variable injection instead."); + } + return supports; + } + + @Override + public Object resolveParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return new Expect( + snapshotVerifier, + extensionContext + .getTestMethod() + .orElseThrow(() -> new RuntimeException("getTestMethod() is missing"))); + } + + @Override + public void beforeEach(ExtensionContext context) { + if (context.getTestInstance().isPresent() && context.getTestMethod().isPresent()) { + ReflectionUtils.findFieldByPredicate( + context.getTestClass().get(), (field) -> field.getType() == Expect.class) + .ifPresent( + (field) -> { + Expect expect = Expect.of(snapshotVerifier, context.getTestMethod().get()); + ReflectionUtils.makeAccessible(field); + try { + field.set(context.getTestInstance().get(), expect); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/BaseClassTest.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/BaseClassTest.java new file mode 100644 index 0000000..134b4ac --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/BaseClassTest.java @@ -0,0 +1,25 @@ +package io.github.finoid.snapshots.junit6; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith({SnapshotExtension.class}) +public class BaseClassTest { + + class TestBase { + Expect expect; + } + + @Nested + @ExtendWith(SnapshotExtension.class) + class NestedClass extends TestBase { + + @Test + public void helloWorldTest() { + expect.toMatchSnapshot("Hello World"); + } + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/NestedClassTest.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/NestedClassTest.java new file mode 100644 index 0000000..8b4260b --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/NestedClassTest.java @@ -0,0 +1,57 @@ +package io.github.finoid.snapshots.junit6; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +@ExtendWith({SnapshotExtension.class}) +@Disabled // TODO (nw) rewrite +public class NestedClassTest { + + @AfterAll + public static void afterAll() { + Path path = + Paths.get("src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/NestedClassTest.snap"); + assertThat(Files.exists(path)).isFalse(); + } + + @Nested + class NestedClassWithExpectArgument { + + @Test + public void helloWorldTest(Expect expect) { + expect.toMatchSnapshot("Hello World"); + } + } + + @Nested + class NestedClassWithoutSnapshot { + + @Test + public void helloWorldTest() { + assertThat(true).isTrue(); + } + } + + @Nested + class NestedClassWithExpectInstance { + + Expect expect; + + @Test + public void helloWorldTest() { + expect.toMatchSnapshot("Hello World"); + } + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/NestedClassTestWithExtends.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/NestedClassTestWithExtends.java new file mode 100644 index 0000000..708a2de --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/NestedClassTestWithExtends.java @@ -0,0 +1,38 @@ +package io.github.finoid.snapshots.junit6; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("checkstyle:all") // TODO (nw) rewrite +public class NestedClassTestWithExtends { + + @AfterAll + public static void afterAll() { + Path path = + Paths.get( + "src/test/java/io/github/finoid/snapshots/__snapshots__/NestedClassTestWithExtends.snap"); + assertThat(Files.exists(path)).isFalse(); + } + + @ExtendWith(SnapshotExtension.class) + @Nested + class NestedClass { + + Expect expect; + + @Test + public void helloWorldTest() { + expect.toMatchSnapshot("Hello World"); + } + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/SnapshotExtensionUsedTest.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/SnapshotExtensionUsedTest.java new file mode 100644 index 0000000..b9b9330 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/SnapshotExtensionUsedTest.java @@ -0,0 +1,45 @@ +package io.github.finoid.snapshots.junit6; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.annotations.SnapshotName; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(SnapshotExtension.class) +public class SnapshotExtensionUsedTest { + + private Expect expect; + + @Test + public void shouldUseExtension(Expect expect) { + expect.toMatchSnapshot("Hello World"); + } + + @Test + public void shouldUseExtensionAgain(Expect expect) { + expect.toMatchSnapshot("Hello World"); + } + + @Test + public void shouldUseExtensionViaInstanceVariable() { + expect.toMatchSnapshot("Hello World"); + } + + @Test + public void shouldUseExtensionAgainViaInstanceVariable() { + expect.toMatchSnapshot("Hello World"); + } + + @SnapshotName("hello_world") + @Test + public void snapshotWithName() { + expect.toMatchSnapshot("Hello World"); + } + + @SnapshotName("hello_world_2") + @Test + public void snapshotWithNameAgain() { + expect.toMatchSnapshot("Hello World"); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/SnapshotParameterTest.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/SnapshotParameterTest.java new file mode 100644 index 0000000..705f02a --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/SnapshotParameterTest.java @@ -0,0 +1,53 @@ +package io.github.finoid.snapshots.junit6; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.jackson3.serializers.v1.JacksonSnapshotSerializer; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +@ExtendWith({SnapshotExtension.class}) +class SnapshotParameterTest { + + private Expect expect; + + static Stream testData() { + + return Stream.of( + Arguments.of("Scenario2", "test input 1"), + Arguments.of("Scenario2", "test input 1"), + Arguments.of("Scenario2", "test input 1"), + Arguments.of("Scenario3", "test input 2"), + Arguments.of("Scenario3", "test input 2")); + } + + @ParameterizedTest + @MethodSource("io.github.finoid.snapshots.junit6.SnapshotParameterTest#testData") + void shouldSupportParameterizedTest(String scenario, String testInput, Expect expect) { + expect.toMatchSnapshot("Duplicates are OK"); + expect.toMatchSnapshot("Duplicates are OK"); + expect.scenario("Scenario1").toMatchSnapshot("Additional snapshots need to include a scenario"); + expect + .serializer(JacksonSnapshotSerializer.class) + .scenario(scenario) + .toMatchSnapshot(testInput); + } + + @ParameterizedTest + @MethodSource("io.github.finoid.snapshots.junit6.SnapshotParameterTest#testData") + void shouldSupportParameterizedTestViaInstanceVariable(String scenario, String testInput) { + this.expect.toMatchSnapshot("Duplicates are OK"); + this.expect.toMatchSnapshot("Duplicates are OK"); + this.expect + .scenario("Scenario1") + .toMatchSnapshot("Additional snapshots need to include a scenario"); + this.expect + .serializer(JacksonSnapshotSerializer.class) + .scenario(scenario) + .toMatchSnapshot(testInput); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/BaseClassTest$NestedClass.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/BaseClassTest$NestedClass.snap new file mode 100644 index 0000000..3c813c8 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/BaseClassTest$NestedClass.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.junit6.BaseClassTest$NestedClass.helloWorldTest=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/NestedClassTest$NestedClassWithExpectArgument.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/NestedClassTest$NestedClassWithExpectArgument.snap new file mode 100644 index 0000000..d0d9dd7 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/NestedClassTest$NestedClassWithExpectArgument.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.junit6.NestedClassTest$NestedClassWithExpectArgument.helloWorldTest=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/NestedClassTestWithExtends$NestedClass.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/NestedClassTestWithExtends$NestedClass.snap new file mode 100644 index 0000000..a6f9e28 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/NestedClassTestWithExtends$NestedClass.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.junit6.NestedClassTestWithExtends$NestedClass.helloWorldTest=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/SnapshotExtensionUsedTest.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/SnapshotExtensionUsedTest.snap new file mode 100644 index 0000000..e39cc31 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/SnapshotExtensionUsedTest.snap @@ -0,0 +1,28 @@ +hello_world=[ +Hello World +] + + +hello_world_2=[ +Hello World +] + + +io.github.finoid.snapshots.junit6.SnapshotExtensionUsedTest.shouldUseExtension=[ +Hello World +] + + +io.github.finoid.snapshots.junit6.SnapshotExtensionUsedTest.shouldUseExtensionAgain=[ +Hello World +] + + +io.github.finoid.snapshots.junit6.SnapshotExtensionUsedTest.shouldUseExtensionAgainViaInstanceVariable=[ +Hello World +] + + +io.github.finoid.snapshots.junit6.SnapshotExtensionUsedTest.shouldUseExtensionViaInstanceVariable=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/SnapshotParameterTest.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/SnapshotParameterTest.snap new file mode 100644 index 0000000..7edca69 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/__snapshots__/SnapshotParameterTest.snap @@ -0,0 +1,38 @@ +io.github.finoid.snapshots.junit6.SnapshotParameterTest.shouldSupportParameterizedTest=[ +Duplicates are OK +] + + +io.github.finoid.snapshots.junit6.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable=[ +Duplicates are OK +] + + +io.github.finoid.snapshots.junit6.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable[Scenario1]=[ +Additional snapshots need to include a scenario +] + + +io.github.finoid.snapshots.junit6.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable[Scenario2]=[ + "test input 1" +] + + +io.github.finoid.snapshots.junit6.SnapshotParameterTest.shouldSupportParameterizedTestViaInstanceVariable[Scenario3]=[ + "test input 2" +] + + +io.github.finoid.snapshots.junit6.SnapshotParameterTest.shouldSupportParameterizedTest[Scenario1]=[ +Additional snapshots need to include a scenario +] + + +io.github.finoid.snapshots.junit6.SnapshotParameterTest.shouldSupportParameterizedTest[Scenario2]=[ + "test input 1" +] + + +io.github.finoid.snapshots.junit6.SnapshotParameterTest.shouldSupportParameterizedTest[Scenario3]=[ + "test input 2" +] \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/CustomFrameworkExample.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/CustomFrameworkExample.java new file mode 100644 index 0000000..a26bfe6 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/CustomFrameworkExample.java @@ -0,0 +1,32 @@ +package io.github.finoid.snapshots.junit6.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.SnapshotVerifier; +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +// Notice we aren't using any framework extensions +public class CustomFrameworkExample { + + private static SnapshotVerifier snapshotVerifier; + + @BeforeAll + static void beforeAll() { + snapshotVerifier = + new SnapshotVerifier(new PropertyResolvingSnapshotConfig(), CustomFrameworkExample.class); + } + + @AfterAll + static void afterAll() { + snapshotVerifier.validateSnapshots(); + } + + @Test + void shouldMatchSnapshotOne(TestInfo testInfo) { + Expect expect = Expect.of(snapshotVerifier, testInfo.getTestMethod().get()); + expect.toMatchSnapshot("Hello World"); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/CustomSnapshotConfigExample.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/CustomSnapshotConfigExample.java new file mode 100644 index 0000000..2e64886 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/CustomSnapshotConfigExample.java @@ -0,0 +1,18 @@ +package io.github.finoid.snapshots.junit6.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.annotations.UseSnapshotConfig; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(SnapshotExtension.class) +// apply your custom snapshot configuration to this test class +@UseSnapshotConfig(LowercaseToStringSnapshotConfig.class) +public class CustomSnapshotConfigExample { + + @Test + public void myTest(Expect expect) { + expect.toMatchSnapshot("hello world"); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/JUnit5Example.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/JUnit5Example.java new file mode 100644 index 0000000..1598b84 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/JUnit5Example.java @@ -0,0 +1,26 @@ +package io.github.finoid.snapshots.junit6.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +// Ensure you extend your test class with the SnapshotExtension +@ExtendWith({SnapshotExtension.class}) +public class JUnit5Example { + + // Option 1: inject Expect as an instance variable + private Expect expect; + + @Test + public void myTest1() { + // Verify your snapshot + expect.toMatchSnapshot("Hello World"); + } + + // Option 2: inject Expect into the method signature + @Test + public void myTest2(Expect expect) { + expect.toMatchSnapshot("Hello World Again"); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/JUnit5ResolutionHierarchyExample.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/JUnit5ResolutionHierarchyExample.java new file mode 100644 index 0000000..797bb4b --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/JUnit5ResolutionHierarchyExample.java @@ -0,0 +1,34 @@ +package io.github.finoid.snapshots.junit6.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.annotations.UseSnapshotConfig; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(SnapshotExtension.class) +@UseSnapshotConfig(LowercaseToStringSnapshotConfig.class) +public class JUnit5ResolutionHierarchyExample { + + private Expect expect; + + @Test + public void aliasMethodTest() { + expect + .serializer("json") // <------ Using snapshot.properties + .toMatchSnapshot(new TestObject()); + } + + @Test + public void customSerializerTest() { + expect + .serializer(UppercaseToStringSerializer.class) // <------ Using custom serializer + .toMatchSnapshot(new TestObject()); + } + + // Read from LowercaseToStringSnapshotConfig defined on the class + @Test + public void lowercaseTest() { + expect.toMatchSnapshot(new TestObject()); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/LowercaseToStringSerializer.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/LowercaseToStringSerializer.java new file mode 100644 index 0000000..92d1f1b --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/LowercaseToStringSerializer.java @@ -0,0 +1,18 @@ +package io.github.finoid.snapshots.junit6.docs; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; + +public class LowercaseToStringSerializer implements SnapshotSerializer { + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + return gen.toSnapshot(object.toString().toLowerCase()); + } + + @Override + public String getOutputFormat() { + return SerializerType.TEXT.name(); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/LowercaseToStringSnapshotConfig.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/LowercaseToStringSnapshotConfig.java new file mode 100644 index 0000000..a956b4b --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/LowercaseToStringSnapshotConfig.java @@ -0,0 +1,12 @@ +package io.github.finoid.snapshots.junit6.docs; + +import io.github.finoid.snapshots.config.PropertyResolvingSnapshotConfig; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; + +public class LowercaseToStringSnapshotConfig extends PropertyResolvingSnapshotConfig { + + @Override + public SnapshotSerializer getSerializer() { + return new LowercaseToStringSerializer(); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/MyFirstSnapshotTest.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/MyFirstSnapshotTest.java new file mode 100644 index 0000000..709764d --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/MyFirstSnapshotTest.java @@ -0,0 +1,31 @@ +package io.github.finoid.snapshots.junit6.docs; + +import io.github.finoid.snapshots.Expect; +import io.github.finoid.snapshots.annotations.SnapshotName; +import io.github.finoid.snapshots.junit5.SnapshotExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.HashMap; +import java.util.Map; + +@ExtendWith({SnapshotExtension.class}) +public class MyFirstSnapshotTest { + + private Expect expect; + + @SnapshotName("i_can_give_custom_names_to_my_snapshots") + @Test + public void toStringSerializationTest() { + expect.toMatchSnapshot("Hello World"); + } + + @Test + public void jsonSerializationTest() { + Map map = new HashMap<>(); + map.put("name", "John Doe"); + map.put("age", 40); + + expect.serializer("json").toMatchSnapshot(map); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/TestObject.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/TestObject.java new file mode 100644 index 0000000..a69b2d3 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/TestObject.java @@ -0,0 +1,8 @@ +package io.github.finoid.snapshots.junit6.docs; + +public class TestObject { + @Override + public String toString() { + return "This is a snapshot of the toString() method"; + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/UppercaseToStringSerializer.java b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/UppercaseToStringSerializer.java new file mode 100644 index 0000000..9d64ee2 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/UppercaseToStringSerializer.java @@ -0,0 +1,18 @@ +package io.github.finoid.snapshots.junit6.docs; + +import io.github.finoid.snapshots.Snapshot; +import io.github.finoid.snapshots.SnapshotSerializerContext; +import io.github.finoid.snapshots.serializers.SerializerType; +import io.github.finoid.snapshots.serializers.SnapshotSerializer; + +public class UppercaseToStringSerializer implements SnapshotSerializer { + @Override + public Snapshot apply(Object object, SnapshotSerializerContext gen) { + return gen.toSnapshot(object.toString().toUpperCase()); + } + + @Override + public String getOutputFormat() { + return SerializerType.TEXT.name(); + } +} diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/CustomFrameworkExample.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/CustomFrameworkExample.snap new file mode 100644 index 0000000..f744371 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/CustomFrameworkExample.snap @@ -0,0 +1,3 @@ +io.github.finoid.snapshots.junit6.docs.CustomFrameworkExample.shouldMatchSnapshotOne=[ +Hello World +] \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/CustomSnapshotConfigExample.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/CustomSnapshotConfigExample.snap new file mode 100644 index 0000000..43b9767 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/CustomSnapshotConfigExample.snap @@ -0,0 +1 @@ +io.github.finoid.snapshots.junit6.docs.CustomSnapshotConfigExample.myTest=hello world \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/CustomSnapshotContextConfigExample.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/CustomSnapshotContextConfigExample.snap new file mode 100644 index 0000000..e69de29 diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/JUnit5Example.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/JUnit5Example.snap new file mode 100644 index 0000000..466d9d6 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/JUnit5Example.snap @@ -0,0 +1,8 @@ +io.github.finoid.snapshots.junit6.docs.JUnit5Example.myTest1=[ +Hello World +] + + +io.github.finoid.snapshots.junit6.docs.JUnit5Example.myTest2=[ +Hello World Again +] \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/JUnit5ResolutionHierarchyExample.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/JUnit5ResolutionHierarchyExample.snap new file mode 100644 index 0000000..231919e --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/JUnit5ResolutionHierarchyExample.snap @@ -0,0 +1,9 @@ +io.github.finoid.snapshots.junit6.docs.JUnit5ResolutionHierarchyExample.aliasMethodTest=[ + { } +] + + +io.github.finoid.snapshots.junit6.docs.JUnit5ResolutionHierarchyExample.customSerializerTest=THIS IS A SNAPSHOT OF THE TOSTRING() METHOD + + +io.github.finoid.snapshots.junit6.docs.JUnit5ResolutionHierarchyExample.lowercaseTest=this is a snapshot of the tostring() method \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/MyFirstSnapshotContextTest.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/MyFirstSnapshotContextTest.snap new file mode 100644 index 0000000..e69de29 diff --git a/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/MyFirstSnapshotTest.snap b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/MyFirstSnapshotTest.snap new file mode 100644 index 0000000..31920a7 --- /dev/null +++ b/snapshot-testing-junit6/src/test/java/io/github/finoid/snapshots/junit6/docs/__snapshots__/MyFirstSnapshotTest.snap @@ -0,0 +1,11 @@ +i_can_give_custom_names_to_my_snapshots=[ +Hello World +] + + +io.github.finoid.snapshots.junit6.docs.MyFirstSnapshotTest.jsonSerializationTest=[ + { + "age" : 40, + "name" : "John Doe" + } +] \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/resources/junit-platform.properties b/snapshot-testing-junit6/src/test/resources/junit-platform.properties new file mode 100644 index 0000000..07bf940 --- /dev/null +++ b/snapshot-testing-junit6/src/test/resources/junit-platform.properties @@ -0,0 +1,2 @@ +junit.jupiter.execution.parallel.enabled=true +junit.jupiter.execution.parallel.mode.default=concurrent \ No newline at end of file diff --git a/snapshot-testing-junit6/src/test/resources/snapshot.properties b/snapshot-testing-junit6/src/test/resources/snapshot.properties new file mode 100644 index 0000000..ee68d50 --- /dev/null +++ b/snapshot-testing-junit6/src/test/resources/snapshot.properties @@ -0,0 +1,8 @@ +serializer=io.github.finoid.snapshots.serializers.v1.ToStringSnapshotSerializer +serializer.json=io.github.finoid.snapshots.jackson3.serializers.v1.DeterministicJacksonSnapshotSerializer +comparator=io.github.finoid.snapshots.comparators.v1.PlainTextEqualsComparator +reporters=io.github.finoid.snapshots.reporters.v1.PlainTextSnapshotReporter +snapshot-dir=__snapshots__ +output-dir=src/test/java +ci-env-var=CI +update-snapshot=none \ No newline at end of file