diff --git a/README.md b/README.md index f03ba6b3..af6dc620 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,22 @@ -# spotless-snapshot +# Selfie -- Precise snapshot testing for the JVM and Javascript (Native TODO) +- Precise snapshot testing for the JVM and Javascript (others TBD) - Supports text and binary data. - Friendly to humans and version control. - In-place [literal snapshots](#literal-snapshots). - Allows [lenses](#lenses) to verify multiple aspects of an object under test. - e.g. pretty-printed JSON, or only the rendered plaintext of an HTML document, etc. -The example below uses the JUnit 5 runner, but we also support TODO. - -Here is a very simple test which snapshots the HTML served at various URLs. +ere is a very simple test which snapshots the HTML served at various URLs. ```java -@Test public void gzipFavicon(Expect expect) { - expect.toMatchBinarySnapshot(get("localhost:8080/favicon.ico", ContentEncoding.GZIP)); +@Test public void gzipFavicon() { + expectSelfie(get("/favicon.ico", ContentEncoding.GZIP)).toMatchDisk(); } -@Test public void orderFlow(Expect expect) { - expect.scenario("initial").toMatchSnapshot(get("localhost:8080/orders")); +@Test public void orderFlow() { + expectSelfie(get("/orders")).toMatchDisk("initial"); postOrder(); - expect.scenario("ordered").toMatchSnapshot(get("localhost:8080/orders")); + expectSelfie(get("/orders")).toMatchDisk("ordered"); } ``` @@ -43,12 +41,12 @@ H4sIAAAAAAAA/8pIzcnJVyjPL8pJUQQAlQYXAAAA ### Literal snapshots -A great thing about snapshots is that they are fast to write and update. A downside is that the asserted value is opaque. But it doesn't have to be! Just swap `toMatchSnapshot` for `toMatchLiteral`. +A great thing about snapshots is that they are fast to write and update. A downside is that the asserted value is opaque. But it doesn't have to be! Just swap `toMatchDisk` for `toBe`. ```java -@Test public void preventCssBloat(Expect expect) { - // spotless-snapshot can update this literal value for you ▼ - int size = expect.toMatchLiteral(get("/index.css").length, 5_236); +@Test public void preventCssBloat() { + // selfie can update this literal value for you ▼ + int size = expectSelfie(get("/index.css").length).toBe(5_236); if (size > 100_000) { Assert.fail("CSS has gotten too big!"); } @@ -62,7 +60,7 @@ Now we can see at a glance how a PR has affected the bundled size of our CSS. We When snapshotting HTML, you might want to look at only the rendered text, ignoring tags, classes, and all that. ```java -public SnapshotConfig extends SpotlessSnapshotConfig { +public SelfieConfig extends com.diffplug.selfie.SelfieConfig { @Override public @Nullable Snapshot intercept(Class className, String testName, Snapshot snapshot) { if (!snapshot.getValue().isBinary()) { String content = snapshot.getValue().valueString(); @@ -92,9 +90,9 @@ Order information Tracking #ABC123 ``` -Lenses can make PRs easier to review, by putting the focus on various aspects of the snapshot that are relevant to the change. +Lenses can make PRs easier to review, by putting the focus on whichever aspect of the snapshot is relevant to the change. ### Acknowledgements -- JUnit test runner heavily inspired by [origin-energy's java-snapshot-testing](https://github.com/origin-energy/java-snapshot-testing). +- Heavily inspired by [origin-energy's java-snapshot-testing](https://github.com/origin-energy/java-snapshot-testing). - Which in turn is heavily inspired by [Facebook's jest-snapshot](https://jestjs.io/docs/snapshot-testing). diff --git a/gradle/spotless.gradle b/gradle/spotless.gradle index 664e8544..17135d5e 100644 --- a/gradle/spotless.gradle +++ b/gradle/spotless.gradle @@ -16,7 +16,7 @@ spotless { target 'src/**/*.kt' licenseHeaderFile 干.file("spotless/license-${license}.java") ktfmt() - for (modifier in ['', 'override ', 'public ', 'protected ', 'private ', 'internal ', 'expected ', 'actual ']) { + for (modifier in ['', 'override ', 'public ', 'protected ', 'private ', 'internal ', 'infix ', 'expected ', 'actual ']) { for (key in ['inline', 'fun', 'val', 'override']) { String toCheck = "$modifier$key" replaceRegex("dense $toCheck", "\n\n(\\s*)$toCheck ", "\n\$1$toCheck ") diff --git a/snapshot-lib/build.gradle b/selfie-lib/build.gradle similarity index 100% rename from snapshot-lib/build.gradle rename to selfie-lib/build.gradle diff --git a/snapshot-lib/src/commonMain/kotlin/com/diffplug/snapshot/ArrayMap.kt b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/ArrayMap.kt similarity index 99% rename from snapshot-lib/src/commonMain/kotlin/com/diffplug/snapshot/ArrayMap.kt rename to selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/ArrayMap.kt index 6b8421e5..176a3934 100644 --- a/snapshot-lib/src/commonMain/kotlin/com/diffplug/snapshot/ArrayMap.kt +++ b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/ArrayMap.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie import kotlin.collections.binarySearch diff --git a/snapshot-lib/src/commonMain/kotlin/com/diffplug/snapshot/PerCharacterEscaper.kt b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/PerCharacterEscaper.kt similarity index 97% rename from snapshot-lib/src/commonMain/kotlin/com/diffplug/snapshot/PerCharacterEscaper.kt rename to selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/PerCharacterEscaper.kt index f06b1755..d3b09233 100644 --- a/snapshot-lib/src/commonMain/kotlin/com/diffplug/snapshot/PerCharacterEscaper.kt +++ b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/PerCharacterEscaper.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie expect class PerCharacterEscaper { fun escape(input: String): String diff --git a/snapshot-lib/src/commonMain/kotlin/com/diffplug/snapshot/SpotlessFile.kt b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/SnapshotFile.kt similarity index 82% rename from snapshot-lib/src/commonMain/kotlin/com/diffplug/snapshot/SpotlessFile.kt rename to selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/SnapshotFile.kt index 7a1bcece..dc9134da 100644 --- a/snapshot-lib/src/commonMain/kotlin/com/diffplug/snapshot/SpotlessFile.kt +++ b/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/SnapshotFile.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie class ParseException(val line: Int, message: String) : IllegalArgumentException(message) { constructor(lineReader: LineReader, message: String) : this(lineReader.getLineNumber(), message) @@ -47,11 +47,6 @@ internal data class SnapshotValueString(val value: String) : SnapshotValue { override fun valueString(): String = value } -internal data class SnapshotValueEmptyString(val value: String) : SnapshotValue { - override fun valueBinary() = throw UnsupportedOperationException("This is an empty string value.") - override fun valueString() = value -} - data class Snapshot( val value: SnapshotValue, private val lensData: ArrayMap @@ -73,8 +68,31 @@ data class Snapshot( interface Snapshotter { fun snapshot(value: T): Snapshot } -fun parseSS(valueReader: SnapshotValueReader): ArrayMap = TODO() -fun serializeSS(valueWriter: StringWriter, snapshots: ArrayMap): Unit = TODO() + +class SnapshotFile { + // this will probably become `` we'll cross that bridge when we get to it + var metadata: Map.Entry? = null + var snapshots = ArrayMap.empty() + fun serialize(valueWriter: StringWriter): Unit = TODO() + + companion object { + private val HEADER_PREFIX = """📷 """ + fun parse(valueReader: SnapshotValueReader): SnapshotFile { + val result = SnapshotFile() + val reader = SnapshotReader(valueReader) + // only if the first value starts with 📷 + if (reader.peekKey()?.startsWith(HEADER_PREFIX) == true) { + val metadataName = reader.peekKey()!!.substring(HEADER_PREFIX.length) + val metadataValue = reader.valueReader.nextValue().valueString() + result.metadata = entry(metadataName, metadataValue) + } + while (reader.peekKey() != null) { + result.snapshots = result.snapshots.plus(reader.peekKey()!!, reader.nextSnapshot()) + } + return result + } + } +} class SnapshotReader(val valueReader: SnapshotValueReader) { fun peekKey(): String? = TODO() @@ -176,11 +194,11 @@ class SnapshotValueReader(val lineReader: LineReader) { private val headerEnd = " ═╗" /** - * https://github.com/diffplug/spotless-snapshot/blob/f63192a84390901a3d3543066d095ea23bf81d21/snapshot-lib/src/commonTest/resources/com/diffplug/snapshot/scenarios_and_lenses.ss#L11-L29 + * https://github.com/diffplug/selfie/blob/f63192a84390901a3d3543066d095ea23bf81d21/snapshot-lib/src/commonTest/resources/com/diffplug/snapshot/scenarios_and_lenses.ss#L11-L29 */ private val nameEsc = PerCharacterEscaper.specifiedEscape("\\\\/∕[(])\nn\tt╔┌╗┐═─") - /** https://github.com/diffplug/spotless-snapshot/issues/2 */ + /** https://github.com/diffplug/selfie/issues/2 */ private val bodyEsc = PerCharacterEscaper.selfEscape("\uD801\uDF43\uD801\uDF41") } } diff --git a/snapshot-lib/src/commonTest/kotlin/com/diffplug/snapshot/ArrayMapTest.kt b/selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/ArrayMapTest.kt similarity index 99% rename from snapshot-lib/src/commonTest/kotlin/com/diffplug/snapshot/ArrayMapTest.kt rename to selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/ArrayMapTest.kt index 59c14769..1d319909 100644 --- a/snapshot-lib/src/commonTest/kotlin/com/diffplug/snapshot/ArrayMapTest.kt +++ b/selfie-lib/src/commonTest/kotlin/com/diffplug/selfie/ArrayMapTest.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe diff --git a/snapshot-lib/src/commonTest/resources/com/diffplug/snapshot/example.ss b/selfie-lib/src/commonTest/resources/com/diffplug/selfie/example.ss similarity index 100% rename from snapshot-lib/src/commonTest/resources/com/diffplug/snapshot/example.ss rename to selfie-lib/src/commonTest/resources/com/diffplug/selfie/example.ss diff --git a/snapshot-lib/src/commonTest/resources/com/diffplug/snapshot/scenarios_and_lenses.ss b/selfie-lib/src/commonTest/resources/com/diffplug/selfie/scenarios_and_lenses.ss similarity index 100% rename from snapshot-lib/src/commonTest/resources/com/diffplug/snapshot/scenarios_and_lenses.ss rename to selfie-lib/src/commonTest/resources/com/diffplug/selfie/scenarios_and_lenses.ss diff --git a/snapshot-lib/src/jsMain/kotlin/com/diffplug/snapshot/ArrayMap.js.kt b/selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/ArrayMap.js.kt similarity index 97% rename from snapshot-lib/src/jsMain/kotlin/com/diffplug/snapshot/ArrayMap.js.kt rename to selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/ArrayMap.js.kt index 6b855785..19ce8257 100644 --- a/snapshot-lib/src/jsMain/kotlin/com/diffplug/snapshot/ArrayMap.js.kt +++ b/selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/ArrayMap.js.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie internal actual fun entry(key: K, value: V): Map.Entry = E(key, value) diff --git a/snapshot-lib/src/jsMain/kotlin/com/diffplug/snapshot/PerCharacterEscaper.js.kt b/selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/PerCharacterEscaper.js.kt similarity index 96% rename from snapshot-lib/src/jsMain/kotlin/com/diffplug/snapshot/PerCharacterEscaper.js.kt rename to selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/PerCharacterEscaper.js.kt index 94c60bd0..e7aa74c2 100644 --- a/snapshot-lib/src/jsMain/kotlin/com/diffplug/snapshot/PerCharacterEscaper.js.kt +++ b/selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/PerCharacterEscaper.js.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie actual class PerCharacterEscaper { actual fun escape(input: String): String = TODO() diff --git a/snapshot-lib/src/jsMain/kotlin/com/diffplug/snapshot/SpotlessFile.js.kt b/selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/SnapshotFile.js.kt similarity index 96% rename from snapshot-lib/src/jsMain/kotlin/com/diffplug/snapshot/SpotlessFile.js.kt rename to selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/SnapshotFile.js.kt index f7c4babf..aea33987 100644 --- a/snapshot-lib/src/jsMain/kotlin/com/diffplug/snapshot/SpotlessFile.js.kt +++ b/selfie-lib/src/jsMain/kotlin/com/diffplug/selfie/SnapshotFile.js.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie actual class LineReader { actual companion object { diff --git a/snapshot-lib/src/jvmMain/kotlin/com/diffplug/snapshot/ArrayMap.jvm.kt b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/ArrayMap.jvm.kt similarity index 95% rename from snapshot-lib/src/jvmMain/kotlin/com/diffplug/snapshot/ArrayMap.jvm.kt rename to selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/ArrayMap.jvm.kt index 18fc57ec..f9a22fa7 100644 --- a/snapshot-lib/src/jvmMain/kotlin/com/diffplug/snapshot/ArrayMap.jvm.kt +++ b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/ArrayMap.jvm.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie internal actual fun entry(key: K, value: V): Map.Entry = java.util.Map.entry(key, value) diff --git a/snapshot-lib/src/jvmMain/kotlin/com/diffplug/snapshot/PerCharacterEscaper.jvm.kt b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/PerCharacterEscaper.jvm.kt similarity index 99% rename from snapshot-lib/src/jvmMain/kotlin/com/diffplug/snapshot/PerCharacterEscaper.jvm.kt rename to selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/PerCharacterEscaper.jvm.kt index 77e79012..7e30fbb1 100644 --- a/snapshot-lib/src/jvmMain/kotlin/com/diffplug/snapshot/PerCharacterEscaper.jvm.kt +++ b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/PerCharacterEscaper.jvm.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie /** * If your escape policy is "'123", it means this: diff --git a/snapshot-lib/src/jvmMain/kotlin/com/diffplug/snapshot/SpotlessFile.jvm.kt b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/SnapshotFile.jvm.kt similarity index 97% rename from snapshot-lib/src/jvmMain/kotlin/com/diffplug/snapshot/SpotlessFile.jvm.kt rename to selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/SnapshotFile.jvm.kt index bb57414b..1c0b4210 100644 --- a/snapshot-lib/src/jvmMain/kotlin/com/diffplug/snapshot/SpotlessFile.jvm.kt +++ b/selfie-lib/src/jvmMain/kotlin/com/diffplug/selfie/SnapshotFile.jvm.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie import java.io.InputStreamReader import java.io.LineNumberReader @@ -24,7 +24,6 @@ import java.nio.charset.StandardCharsets actual class LineReader(reader: Reader) : LineNumberReader(reader) { actual companion object { actual fun forString(content: String) = LineReader(StringReader(content)) - actual fun forBinary(content: ByteArray) = LineReader(InputStreamReader(content.inputStream(), StandardCharsets.UTF_8)) } diff --git a/snapshot-lib/src/jvmTest/kotlin/com/diffplug/snapshot/PerCharacterEscaperTest.kt b/selfie-lib/src/jvmTest/kotlin/com/diffplug/selfie/PerCharacterEscaperTest.kt similarity index 95% rename from snapshot-lib/src/jvmTest/kotlin/com/diffplug/snapshot/PerCharacterEscaperTest.kt rename to selfie-lib/src/jvmTest/kotlin/com/diffplug/selfie/PerCharacterEscaperTest.kt index 4147aa59..ff2f39db 100644 --- a/snapshot-lib/src/jvmTest/kotlin/com/diffplug/snapshot/PerCharacterEscaperTest.kt +++ b/selfie-lib/src/jvmTest/kotlin/com/diffplug/selfie/PerCharacterEscaperTest.kt @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie -import com.diffplug.snapshot.PerCharacterEscaper.Companion.selfEscape -import com.diffplug.snapshot.PerCharacterEscaper.Companion.specifiedEscape +import com.diffplug.selfie.PerCharacterEscaper.Companion.selfEscape +import com.diffplug.selfie.PerCharacterEscaper.Companion.specifiedEscape import java.util.function.Consumer import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test diff --git a/snapshot-lib/src/jvmTest/kotlin/com/diffplug/snapshot/SnapshotValueReaderTest.kt b/selfie-lib/src/jvmTest/kotlin/com/diffplug/selfie/SnapshotValueReaderTest.kt similarity index 99% rename from snapshot-lib/src/jvmTest/kotlin/com/diffplug/snapshot/SnapshotValueReaderTest.kt rename to selfie-lib/src/jvmTest/kotlin/com/diffplug/selfie/SnapshotValueReaderTest.kt index d17a8a62..63cc7b4e 100644 --- a/snapshot-lib/src/jvmTest/kotlin/com/diffplug/snapshot/SnapshotValueReaderTest.kt +++ b/selfie-lib/src/jvmTest/kotlin/com/diffplug/selfie/SnapshotValueReaderTest.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot +package com.diffplug.selfie import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe diff --git a/selfie-runner-junit5/build.gradle b/selfie-runner-junit5/build.gradle new file mode 100644 index 00000000..b4dbcd59 --- /dev/null +++ b/selfie-runner-junit5/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' +} +repositories { + mavenCentral() +} +apply from: rootProject.file('gradle/spotless.gradle') +apply plugin: 'java-library' +dependencies { + implementation project(':selfie-lib') + // User supplied Junit5 version + String JUNIT_API = '5.0.0' + compileOnly "org.junit.jupiter:junit-jupiter-api:$JUNIT_API" + compileOnly "org.junit.jupiter:junit-jupiter-engine:$JUNIT_API" + + testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5:1.9.0' + testImplementation 'io.kotest:kotest-assertions-core:5.6.2' + + String JUNIT_IMPL = '5.10.0' + testImplementation "org.junit.jupiter:junit-jupiter-api:$JUNIT_IMPL" + testImplementation "org.junit.jupiter:junit-jupiter-engine:$JUNIT_IMPL" +} +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/Selfie.kt b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/Selfie.kt new file mode 100644 index 00000000..c14d021f --- /dev/null +++ b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/Selfie.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 DiffPlug + * + * 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. + */ +package com.diffplug.selfie + +object SelfieRouting { + var isWriting: Boolean = true + var currentFile: SnapshotFile? = null + var currentDiskPrefix: String? = null + private fun assertInitializedProperly() { + if (currentFile == null || currentDiskPrefix == null) { + throw AssertionError("You called `toMatchDisk` without setting up snapshots.") + } + } + fun onDiskRightNow(scenario: String?): Snapshot? { + assertInitializedProperly() + val snapshotSuffix = scenario?.let { "/$scenario" } ?: "" + val snapshotName = "${currentDiskPrefix!!}${snapshotSuffix}" + return currentFile!!.snapshots.get(snapshotName) + } +} + +open class DiskSelfie internal constructor(private val actual: Snapshot) { + fun toMatchDisk(scenario: String? = null): Snapshot { + val snapshot = SelfieRouting.onDiskRightNow(scenario) + if (actual != snapshot) { + if (SelfieRouting.isWriting) { + TODO("write snapshot") + } else { + throw AssertionError() + } + } + return actual + } +} +fun expectSelfie(actual: T, snapshotter: Snapshotter) = + DiskSelfie(snapshotter.snapshot(actual)) + +class StringSelfie(private val actual: String) : DiskSelfie(Snapshot.of(actual)) { + fun toBe(expected: String): String = TODO() + fun toBe_TODO(): String = TODO() +} +fun expectSelfie(actual: String) = StringSelfie(actual) + +class BinarySelfie(private val actual: ByteArray) : DiskSelfie(Snapshot.of(actual)) { + fun toBeBase64(expected: String): ByteArray = TODO() + fun toBeBase64_TODO(): ByteArray = TODO() +} +fun expectSelfie(actual: ByteArray) = BinarySelfie(actual) + +class IntSelfie(private val actual: Int) { + fun toBe(expected: Int): Int = TODO() + fun toBe_TODO(): Int = TODO() +} +fun expectSelfie(actual: Int) = IntSelfie(actual) + +class LongSelfie(private val actual: Long) { + fun toBe(expected: Long): Long = TODO() + fun toBe_TODO(): Long = TODO() +} +fun expectSelfie(actual: Long) = LongSelfie(actual) + +class BooleanSelfie(private val actual: Boolean) { + fun toBe(expected: Boolean): Boolean = TODO() + fun toBe_TODO(): Boolean = TODO() +} +fun expectSelfie(actual: Boolean) = BooleanSelfie(actual) + +/** Sometimes a selfie doesn't get used. */ +fun preserveDiskSelfies(vararg names: String): Unit = TODO() + +// infix versions for the inline methods, consistent with Kotest's API +infix fun String.shouldBeSelfie(expected: String): String = expectSelfie(this).toBe(expected) +infix fun ByteArray.shouldBeSelfieBase64(expected: String): ByteArray = + expectSelfie(this).toBeBase64(expected) +infix fun Int.shouldBeSelfie(expected: Int): Int = expectSelfie(this).toBe(expected) +infix fun Long.shouldBeSelfie(expected: Long): Long = expectSelfie(this).toBe(expected) +infix fun Boolean.shouldBeSelfie(expected: Boolean): Boolean = expectSelfie(this).toBe(expected) diff --git a/snapshot-runner-junit5/src/main/kotlin/com/diffplug/snapshot/jest/Jest.kt b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/alternative_api/Jest.kt similarity index 93% rename from snapshot-runner-junit5/src/main/kotlin/com/diffplug/snapshot/jest/Jest.kt rename to selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/alternative_api/Jest.kt index 809c1848..25f2f236 100644 --- a/snapshot-runner-junit5/src/main/kotlin/com/diffplug/snapshot/jest/Jest.kt +++ b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/alternative_api/Jest.kt @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot.jest +package com.diffplug.selfie.alternative_api -import com.diffplug.snapshot.Snapshot -import com.diffplug.snapshot.Snapshotter +import com.diffplug.selfie.Snapshot +import com.diffplug.selfie.Snapshotter fun preserveScenarios(vararg names: String): Unit = TODO() class Expect(private val actual: Snapshot) { diff --git a/snapshot-runner-junit5/src/main/kotlin/com/diffplug/snapshot/junit5/Expect.kt b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/alternative_api/OriginEnergy.kt similarity index 77% rename from snapshot-runner-junit5/src/main/kotlin/com/diffplug/snapshot/junit5/Expect.kt rename to selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/alternative_api/OriginEnergy.kt index 33ff98f9..b495717c 100644 --- a/snapshot-runner-junit5/src/main/kotlin/com/diffplug/snapshot/junit5/Expect.kt +++ b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/alternative_api/OriginEnergy.kt @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.diffplug.snapshot.junit5 +package com.diffplug.selfie.alternative_api -import com.diffplug.snapshot.Snapshot -import com.diffplug.snapshot.Snapshotter +import com.diffplug.selfie.Snapshot +import com.diffplug.selfie.Snapshotter interface ExpectSnapshot { /** Creates a sub-snapshot, which can be used to group related snapshots together. */ fun scenario(name: String): ExpectSnapshot /** - * Spotless prunes snapshots which are no longer used. If there are some that you want to - * preserve, perhaps because a single test has different behavior in different environments, then - * this method will prevent a snapshot from being removed. + * Selfie prunes snapshots which are no longer used. If there are some that you want to preserve, + * perhaps because a single test has different behavior in different environments, then this + * method will prevent a snapshot from being removed. */ fun preserveScenarios(vararg names: String) fun toMatchSnapshotBinary(content: ByteArray): ByteArray diff --git a/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieExtension.kt b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieExtension.kt new file mode 100644 index 00000000..e79dbae4 --- /dev/null +++ b/selfie-runner-junit5/src/main/kotlin/com/diffplug/selfie/junit5/SelfieExtension.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 DiffPlug + * + * 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. + */ +package com.diffplug.selfie.junit5 + +import com.diffplug.selfie.SelfieRouting +import com.diffplug.selfie.SnapshotFile +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 + +class SelfieExtension : BeforeAllCallback, AfterAllCallback, BeforeEachCallback { + override fun beforeAll(context: ExtensionContext) { + // TOOD: load the selfie file if it exists + // TODO: create the selfie metadata if necessary + SelfieRouting.currentFile = SnapshotFile() + } + override fun beforeEach(context: ExtensionContext) { + SelfieRouting.currentDiskPrefix = context.testMethod.get().name + } + override fun afterAll(context: ExtensionContext) { + // TODO: test/prune orphan snapshots + SelfieRouting.currentDiskPrefix = null + SelfieRouting.currentFile = null + } +} diff --git a/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/NoEscapingNeeded.kt b/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/NoEscapingNeeded.kt new file mode 100644 index 00000000..018d57de --- /dev/null +++ b/selfie-runner-junit5/src/test/kotlin/com/diffplug/selfie/junit5/NoEscapingNeeded.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 DiffPlug + * + * 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. + */ +package com.diffplug.selfie.junit5 + +import com.diffplug.selfie.expectSelfie +import kotlin.test.Ignore +import kotlin.test.Test +import org.junit.jupiter.api.extension.ExtendWith + +@Ignore +@ExtendWith(SelfieExtension::class) +class NoEscapingNeeded { + @Test + fun _00_empty() { + expectSelfie("").toMatchDisk() + } + + @Test + fun _01_singleLineString() { + expectSelfie("this is one line").toMatchDisk() + } + + @Test + fun _01a_singleLineLeadingSpace() { + expectSelfie(" the leading space is significant").toMatchDisk() + } + + @Test + fun _01b_singleLineTrailingSpace() { + expectSelfie("the trailing space is significant ").toMatchDisk() + } + + @Test + fun _02_multiLineStringTrimmed() { + expectSelfie("Line 1\nLine 2").toMatchDisk() + } +} diff --git a/settings.gradle b/settings.gradle index 5c6fd53d..e64027c4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -40,6 +40,6 @@ blowdryerSetup { } } -include 'snapshot-lib' -include 'snapshot-runner-junit5' -rootProject.name = 'snapshot' +include 'selfie-lib' +include 'selfie-runner-junit5' +rootProject.name = 'selfie' diff --git a/snapshot-runner-junit5/build.gradle b/snapshot-runner-junit5/build.gradle deleted file mode 100644 index be16665b..00000000 --- a/snapshot-runner-junit5/build.gradle +++ /dev/null @@ -1,14 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' -} -repositories { - mavenCentral() -} -apply from: rootProject.file('gradle/spotless.gradle') -apply plugin: 'java-library' -dependencies { - implementation project(':snapshot-lib') - api 'org.junit.jupiter:junit-jupiter-api:5.0.0' - testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5:1.9.0' - testImplementation 'io.kotest:kotest-assertions-core:5.6.2' -} diff --git a/snapshot-runner-junit5/src/test/kotlin/com/diffplug/snapshot/junit5/CreateSnapshots.kt b/snapshot-runner-junit5/src/test/kotlin/com/diffplug/snapshot/junit5/CreateSnapshots.kt deleted file mode 100644 index 571064c5..00000000 --- a/snapshot-runner-junit5/src/test/kotlin/com/diffplug/snapshot/junit5/CreateSnapshots.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2023 DiffPlug - * - * 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. - */ -package com.diffplug.snapshot.junit5 - -import kotlin.test.Test -import kotlin.test.assertFails - -class CreateSnapshots { - @Test - fun test() { - assertFails { TODO() } - } -}