Skip to content

Commit

Permalink
Munit integration, fix plugin, add scripted test (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
keynmol committed Feb 11, 2024
1 parent ad8248a commit e45e2f9
Show file tree
Hide file tree
Showing 14 changed files with 144 additions and 14 deletions.
26 changes: 23 additions & 3 deletions README.md
@@ -1,7 +1,11 @@
<!--toc:start-->
- [snapshots-testing](#snapshots-testing)
- [Installation (SBT)](#installation-sbt)
- [Usage](#usage)
- [Scala.js](#scalajs)
<!--toc:end-->


[![sbt-snapshots Scala version support](https://index.scala-lang.org/indoorvivants/snapshot-testing/sbt-snapshots/latest.svg)](https://index.scala-lang.org/indoorvivants/snapshot-testing/sbt-snapshots)

## snapshots-testing
Expand All @@ -28,6 +32,7 @@ To add the plugin to your SBT build:
```scala
.settings(
snapshotsPackageName := "example",
snapshotsIntegrations += SnapshotIntegration.MUnit // if using MUnit
)
.enablePlugins(SnapshotsPlugin)
```
Expand All @@ -41,16 +46,22 @@ task to the test framework of choice.

## Usage

To interact with snapshots, the following build tasks are provided:
SBT tasks:

- `snapshotsCheck` - interactively accept modified snapshots (if there are any)
- `snapshotsAcceptAll` - accept all modified snapshots
- `snapshotsDiscardAll` - discard all snapshot changes

SBT settings:
- `snapshotsIntegrations` - list of test framework integrations to generate in test sources

Instead of providing a separate dependency for each test framework, the plugin generates
a single-file integration. This helps avoid the dependency hell of incompatible framework
versions. It might seem weird at first, because it is, but I believe it's the only way to
stay sane.

At this point there is no OOTB test framework integrations, but they will come in the future - even though they will be very small and short and are easier copied into your project.
- `snapshotsPackageName` - package name to use for generated file

[Sample MUnit integration](modules/example/src/test/scala/MunitSnapshotsIntegration.scala) |
[Sample MUnit tests](modules/example/src/test/scala/MunitExampleTests.scala)

You can see what the workflow looks like by
Expand All @@ -66,4 +77,13 @@ Here's the same workflow in video format:
https://github.com/indoorvivants/snapshot-testing/assets/1052965/eaef5f88-641b-4bec-85be-1e7458379a58


### Scala.js

The snapshots runtime will only run under Node.js (or any runtime where `require("node:fs")` import can succeed).

To make sure module import is enabled, you should ensure you're using the correct module kind -
for example commonJS:

```scala
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))
```
8 changes: 7 additions & 1 deletion build.sbt
Expand Up @@ -77,6 +77,7 @@ lazy val example = projectMatrix
nativeConfig ~= (_.withIncrementalCompilation(true)),
snapshotsPackageName := "example",
snapshotsAddRuntimeDependency := false,
snapshotsIntegrations += SnapshotIntegration.MUnit,
noPublish
)
.enablePlugins(SnapshotsPlugin)
Expand Down Expand Up @@ -108,7 +109,11 @@ lazy val snapshotsSbtPlugin = projectMatrix
scriptedBufferLog := false,
publishLocal := publishLocal
.dependsOn(
LocalRootProject / publishLocal
snapshotsBuildtime.jvm(Versions.Scala212) / publishLocal,
snapshotsRuntime.js(Versions.Scala3) / publishLocal,
snapshotsRuntime.native(Versions.Scala3) / publishLocal,
snapshotsRuntime.jvm(Versions.Scala3) / publishLocal,
snapshotsRuntime.jvm(Versions.Scala213) / publishLocal
)
.value
)
Expand Down Expand Up @@ -142,6 +147,7 @@ val CICommands = Seq(
"clean",
"compile",
"test",
"scripted",
"scalafmtCheckAll",
"scalafmtSbtCheck",
s"scalafix --check $scalafixRules",
Expand Down
2 changes: 1 addition & 1 deletion modules/example/src/test/scala/MunitExampleTests.scala
@@ -1,4 +1,4 @@
import com.indoorvivants.snapshots._
import com.indoorvivants.snapshots.munit_integration._
import munit._

class MunitExampleTests extends FunSuite with MunitSnapshotsIntegration {
Expand Down
@@ -1,13 +1,30 @@
/*
* Copyright 2024 Anton Sviridov
*
* 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
*
* 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.
*/

package com.indoorvivants.snapshots.munit_integration

import munit._
import com.indoorvivants.snapshots._
import munit.internal.difflib.Diffs
import munit.Assertions
import munit.FunSuite
import example.Snapshots

// This is a sample integration for Munit
trait MunitSnapshotsIntegration {
self: FunSuite =>

def assertSnapshot(name: String, contents: String) = {
Snapshots.read(name) match {
case None =>
Expand Down
34 changes: 32 additions & 2 deletions modules/snapshots-buildtime/src/main/scala/SnapshotsBuild.scala
Expand Up @@ -22,6 +22,8 @@ import java.nio.file.Files

import scala.io.StdIn

import com.indoorvivants.snapshots.build.SnapshotsBuild.SnapshotIntegration.MUnit

object SnapshotsBuild {

private object IO {
Expand Down Expand Up @@ -53,6 +55,11 @@ object SnapshotsBuild {
case object Interactive extends SnapshotAction
}

sealed trait SnapshotIntegration extends Product with Serializable
object SnapshotIntegration {
case object MUnit extends SnapshotIntegration
}

def checkSnapshots(
tmpLocation: File,
projectId: String,
Expand Down Expand Up @@ -154,8 +161,31 @@ object SnapshotsBuild {
).linesIterator.toList
)

// Files.createDirectories(snapshotsDestination.toPath)
// Files.createDirectories(tmpLocation.toPath)
Seq(sourceDestination)
}

def generateIntegrationSources(
sourceDestination: File,
integration: SnapshotIntegration
) = {
Files.createDirectories(sourceDestination.getParentFile().toPath)

val fileName = integration match {
case MUnit => "MunitSnapshotsIntegration.scala"

}

val contents = scala.io.Source
.fromInputStream(
getClass().getResourceAsStream("/" + fileName)
)
.getLines()
.toList

IO.writeLines(
sourceDestination,
contents
)

Seq(sourceDestination)
}
Expand Down
26 changes: 21 additions & 5 deletions modules/snapshots-sbt-plugin/src/main/scala/SnapshotsPlugin.scala
Expand Up @@ -23,7 +23,7 @@ import sbt.Keys.*
import sbt.*
import sbt.nio.Keys.*

import SnapshotsBuild.SnapshotAction
import SnapshotsBuild.{SnapshotAction, SnapshotIntegration}

object SnapshotsPlugin extends AutoPlugin {
object autoImport {
Expand All @@ -33,6 +33,9 @@ object SnapshotsPlugin extends AutoPlugin {
val snapshotsPackageName = settingKey[String](
"Package name under which the generated Snapshots object will be created"
)
val snapshotsIntegrations = settingKey[Seq[SnapshotIntegration]](
"Test framework integrations to generate in test sources"
)
val snapshotsAddRuntimeDependency = settingKey[Boolean](
"Whether to add snapshot runtime to the build - true by default, you shouldn't need to touch this"
)
Expand All @@ -43,6 +46,8 @@ object SnapshotsPlugin extends AutoPlugin {
val snapshotsAcceptAll = taskKey[Unit]("Accept all modified snapshots")
val snapshotsDiscardAll =
taskKey[Unit]("Discard all modifications to snapshots")

val SnapshotIntegration = SnapshotsBuild.SnapshotIntegration
}

val snapshotsTag = ConcurrentRestrictions.Tag("snapshots-check")
Expand All @@ -63,12 +68,13 @@ object SnapshotsPlugin extends AutoPlugin {
}

Seq(
"tech.neander" % s"snapshots-runtime_$cross" % BuildInfo.version
"com.indoorvivants.snapshots" % s"snapshots-runtime_$cross" % BuildInfo.version
)
} else Seq.empty
},
snapshotsProjectIdentifier := thisProject.value.id,
snapshotsAddRuntimeDependency := true,
snapshotsIntegrations := Seq.empty,
snapshotsTemporaryDirectory := (Test / managedResourceDirectories).value.head / "snapshots-tmp",
snapshotsCheck := Def
.task {
Expand Down Expand Up @@ -101,15 +107,25 @@ object SnapshotsPlugin extends AutoPlugin {
.tag(snapshotsTag)
.value,
Test / sourceGenerators += Def.task {
SnapshotsBuild.generateSources(
val dest = (Test / managedSourceDirectories).value.head

val sources = SnapshotsBuild.generateSources(
projectId = snapshotsProjectIdentifier.value,
packageName = snapshotsPackageName.value,
snapshotsDestination =
(Test / resourceDirectory).value / "snapshots" / snapshotsProjectIdentifier.value,
sourceDestination =
(Test / managedSourceDirectories).value.head / "Snapshots.scala",
sourceDestination = dest / "Snapshots.scala",
tmpLocation = snapshotsTemporaryDirectory.value
)

val integrations = snapshotsIntegrations.value.flatMap { integ =>
SnapshotsBuild.generateIntegrationSources(
dest / s"${integ}Integration.scala",
integ
)
}

sources ++ integrations
}
)
}
13 changes: 13 additions & 0 deletions modules/snapshots-sbt-plugin/src/sbt-test/basic/basic/build.sbt
@@ -0,0 +1,13 @@
lazy val root = projectMatrix
.in(file("."))
.jvmPlatform(Seq("3.3.1", "2.13.12"))
.nativePlatform(Seq("3.3.1"))
.jsPlatform(Seq("3.3.1"))
.settings(
snapshotsPackageName := "example",
snapshotsIntegrations += SnapshotIntegration.MUnit,
libraryDependencies +=
"org.scalameta" %%% "munit" % "1.0.0-M7" % Test,
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))
)
.enablePlugins(SnapshotsPlugin)
@@ -0,0 +1,6 @@
addSbtPlugin(
"com.indoorvivants.snapshots" % "sbt-snapshots" % sys.props("plugin.version")
)
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17")
addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.9.1")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.14.0")
@@ -0,0 +1,8 @@
import com.indoorvivants.snapshots.munit_integration._
import munit._

class MunitExampleTests extends FunSuite with MunitSnapshotsIntegration {
test("hello") {
assertSnapshot("my.snapshot", "hello - more stuff")
}
}
10 changes: 10 additions & 0 deletions modules/snapshots-sbt-plugin/src/sbt-test/basic/basic/test
@@ -0,0 +1,10 @@
> root/test
-> root3/test
-> rootNative3/test
-> rootJS3/test
> snapshotsAcceptAll
$ exists src/test/resources/snapshots/root/my.snapshot
> root3/test
> rootNative3/test
> rootJS3/test

4 changes: 4 additions & 0 deletions project/plugins.sbt
Expand Up @@ -26,6 +26,10 @@ Compile / unmanagedSourceDirectories +=
(ThisBuild / baseDirectory).value.getParentFile /
"modules" / "snapshots-buildtime" / "src" / "main" / "scala"

Compile / unmanagedResourceDirectories +=
(ThisBuild / baseDirectory).value.getParentFile /
"modules" / "snapshots-buildtime" / "src" / "main" / "resources"

Compile / sourceGenerators += Def.task {
val tmpDest =
(Compile / managedResourceDirectories).value.head / "BuildInfo.scala"
Expand Down

0 comments on commit e45e2f9

Please sign in to comment.