Giter VIP home page Giter VIP logo

nimble-snapshots's Introduction

GitHub Actions CI Cocoapods compatible SPM compatible Carthage compatible License

Nimble matchers for iOSSnapshotTestCase. Originally derived from Expecta Matchers for FBSnapshotTestCase.

Installing

CocoaPods

You need to be using CocoaPods 0.36 Beta 1 or higher. Your Podfile should look something like the following.

platform :ios, '10.0'

source 'https://github.com/CocoaPods/Specs.git'

# Whichever pods you need for your app go here.

target 'YOUR_APP_NAME_HERE_Tests', :exclusive => true do
  pod 'Nimble-Snapshots'
  pod 'Quick' # if you want to use it with Quick
end

Then run:

$ pod install

Carthage

You need to be using Carthage 0.18 or higher. Your Cartfile (or Cartfile.private) should look something like the following.

github "Quick/Quick" ~> 4.0
github "Quick/Nimble" ~> 9.2
github "uber/ios-snapshot-test-case" "8.0.0"
github "ashfurrow/Nimble-Snapshots"

Then run:

$ carthage bootstrap --use-xcframeworks --platform iOS

Swift Package Manager

To add Nimble-Snapshots as a dependency, you have to add it to the dependencies of your Package.swift file and refer to that dependency in your target.

import PackageDescription
let package = Package(
    name: "<Your Product Name>",
    dependencies: [
       .package(url: "https://github.com/ashfurrow/Nimble-Snapshots", .upToNextMajor(from: "9.0.0"))
    ],
    targets: [
        .target(
            name: "<Your Target Name>",
            dependencies: ["Nimble-Snapshots"]),
    ]
)

Use

Your tests will look something like the following.

import Quick
import Nimble
import Nimble_Snapshots
import UIKit

class MySpec: QuickSpec {
    override func spec() {
        describe("in some context") {
            it("has valid snapshot") {
                let view = ... // some view you want to test
                expect(view).to( haveValidSnapshot() )
            }
        }
    }
}

There are some options for testing the validity of snapshots. Snapshots can be given a name:

expect(view).to( haveValidSnapshot(named: "some custom name") )

We also have a prettier syntax for custom-named snapshots:

expect(view) == snapshot("some custom name")

To record snapshots, just replace haveValidSnapshot() with recordSnapshot() and haveValidSnapshot(named:) with recordSnapshot(named:). We also have a handy emoji operator.

📷(view)
📷(view, "some custom name")

By default, this pod will put the reference images inside a ReferenceImages directory; we try to put this in a place that makes sense (inside your unit tests directory). If we can't figure it out, or if you want to use your own directory instead, call setNimbleTestFolder() with the name of the directory in your unit test's path that we should use. For example, if the tests are in App/AppTesting/, you can call it with AppTesting.

If you have any questions or run into any trouble, feel free to open an issue on this repo.

Dynamic Type

Testing Dynamic Type manually is boring and no one seems to remember doing it when implementing a view/screen, so you can have snapshot tests according to content size categories.

In order to use Dynamic Type testing, make sure to provide a valid Host Application in your testing target.

Then you can use the haveValidDynamicTypeSnapshot and recordDynamicTypeSnapshot matchers:

// expect(view).to(recordDynamicTypeSnapshot()
expect(view).to(haveValidDynamicTypeSnapshot())

// You can also just test some sizes:
expect(view).to(haveValidDynamicTypeSnapshot(sizes: [UIContentSizeCategoryExtraLarge]))

// If you prefer the == syntax, we got you covered too:
expect(view) == dynamicTypeSnapshot()
expect(view) == dynamicTypeSnapshot(sizes: [UIContentSizeCategoryExtraLarge])

Note that this will post an UIContentSizeCategoryDidChangeNotification, so your views/view controllers need to observe that and update themselves.

For more info on usage, check out the dynamic type tests.

Dynamic Size

Testing the same view with many sizes is easy but error prone. It easy to fix one test on change and forget the others. For this we create a easy way to tests all sizes at same time.

You can use the new haveValidDynamicSizeSnapshot and recordDynamicSizeSnapshot matchers to test multiple sizes at once:

let sizes = ["SmallSize": CGSize(width: 44, height: 44),
"MediumSize": CGSize(width: 88, height: 88),
"LargeSize": CGSize(width: 132, height: 132)]

// expect(view).to(recordDynamicSizeSnapshot(sizes: sizes))
expect(view).to(haveValidDynamicSizeSnapshot(sizes: sizes))

// You can also just test some sizes:
expect(view).to(haveValidDynamicSizeSnapshot(sizes: sizes))

// If you prefer the == syntax, we got you covered too:
expect(view) == snapshot(sizes: sizes)
expect(view) == snapshot(sizes: sizes)

By default, the size will be set on the view using the frame property. To change this behavior you can use the ResizeMode enum:

public enum ResizeMode {
  case frame
  case constrains
  case block(resizeBlock: (UIView, CGSize) -> Void)
  case custom(viewResizer: ViewResizer)
}

To use the enum you can expect(view) == dynamicSizeSnapshot(sizes: sizes, resizeMode: newResizeMode). For custom behavior you can use ResizeMode.block. The block will be call on every resize. Or you can implement the ViewResizer protocol and resize yourself. The custom behavior can be used to record the views too.

For more info on usage, check the dynamic sizes tests.

nimble-snapshots's People

Contributors

alextakahashi avatar andreamazz avatar aniastrzezek avatar ashfurrow avatar brunomazzo avatar colinta avatar danielsaidi avatar dependabot[bot] avatar dogo avatar ejmartin504 avatar esttorhe avatar freak4pc avatar fsaragoca avatar joaolfp avatar joeydong avatar jwutke avatar karpelcevs avatar lascorbe avatar marcelofabri avatar maryamaffinityclick avatar mp0w avatar orta avatar paulz avatar robinbonin avatar thanasak-s avatar tibr avatar tomquist avatar vkt0r avatar yas375 avatar younata avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nimble-snapshots's Issues

Device or screen size based assertion

This framework is great, but for some view assertions they don't work across devices. I would like to have a haveValidScreenRatioDependentSnapshot(), that would generate a file name using the an aspect ratio, device type, or screensize. Not exactly sure on the naming or how best to implement this. This would make Xcode Continuous Integrations tests pass better when using a screen snapshot or full width or height snapshot, since it can test on all devices.

I haven't looked into what the facebook library provides yet, but wanted to bring this up as a potential feature in the meantime. Any thoughts?

Support for dynamic type

We (me and @fsaragoca) were thinking about having snapshot tests according to content size category as testing Dynamic Type manually is boring and no one seems to remember doing it when implementing a view/screen.

Initially I thought about making a separated component that used Nimble-Snapshots, but soon enough I've found myself needing internal methods, such as _sanitizedTestName.

I'd like to think what you folks think about this, if this would be a welcome addition, if it should be a subspec, etc.

The only downside for adding this to the library would be that I needed to swizzle (I actually used OCMock) -[UIApplication preferredContentSizeCategory] and -[UIFont preferredFontForTextStyle]. That's why I think a subspec would be better.

Also, the current implementation posts an UIContentSizeCategoryDidChangeNotification when the preferredContentSizeCategory is mocked, so the views/view controllers can update their content. This might cause trouble if whoever is writing the tests don't know about this implementation detail.

I also need names better than recordDynamicTypeSnapshot and haveValidDynamicTypeSnapshot. I thought about adding an extra parameter to the existing functions, but I think the default setting when someone wants to test Dynamic Type should be to test all the content size categories.

In short I think these are the options:

  1. Add this feature to Nimble-Snapshots
  2. Add this feature to Nimble-Snapshots in a subspec
  3. Make this another component that uses Nimble-Snapshots, but change the visibility of needed methods
  4. Make this another component that uses Nimble-Snapshots, but reimplement needed methods ¯_(ツ)_/¯

got expected to record a snapshot in Optional

I just installed Nimble_Snapshots and got the following error in my first line of code:

Code:
expect(view).to(recordSnapshot(named: "MainViewControllerAtLaunch"))

Test fails and I get this error:
.../MainViewControllerSpec.swift:39: error: -[Tests.MainViewControllerSpec MainViewController__Initialization__Storyboard__has_valid_snapshot] : , got expected to record a snapshot in Optional("MainViewControllerAtLaunch")

Any idea what's happening?
My setup: Swift 2.2 and Xcode 7.3.

Xcode 7.3 Support?

I can't get this to work with Xcode 7.3. No matter what I do, the tests aren't recognized and aren't run.

Is this a known issue or is this just a problem with the way I've set things up? I followed the instructions exactly

Without Quick

Could this also work without Quick? We are using Nimble in our project but not Quick.

Release new pod 🙏

@ashfurrow or @orta don't know if any of you guys could release a new version of this that contains my device agnostic code; right now i'm pointing at the git with branch: "master" which is not optimal (IMHO)

🙏

📷 emoji function doesn't detect correct folder for snapshots

The 📷 doesn't seem to pass through the correct file to search for the snapshot folder in. Not a huge problem cause I can just use expect(vc).to(recordSnapshot()) instead. Might try to fix if I take some time off reading about Swift OSS 🤗

Using expect(vc).to(recordSnapshot())
nimble snapshots right

Using 📷(vc)
nimble snapshots wrong

Fix CI

It's been broken for a while...

FailureDiffs directory along side the ReferenceImages

Currently the FailureDiffs isn't configured but it could easily be configured with the defaults suggested here.

If it's worth anything to you guys let me know and I'll try to make it happen. I use the env var and it works fine but I had to do a lot of digging to finally see what the difference is between this folder that we aren't getting by default and the other one which we are.

invalid context 0x0

Thank you for useful cocoapod!
When running tests we are getting:

Feb 23 01:55:29  Example[69449] <Error>: CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Feb 23 01:55:29  Example[69449] <Error>: CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Feb 23 01:55:29  Example[69449] <Error>: CGContextSetBlendMode: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Feb 23 01:55:29  Example[69449] <Error>: CGContextSetAlpha: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Feb 23 01:55:29  Example[69449] <Error>: CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Feb 23 01:55:29  Example[69449] <Error>: CGContextScaleCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Feb 23 01:55:29  Example[69449] <Error>: CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Feb 23 01:55:29  Example[69449] <Error>: CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
Feb 23 01:55:29  Example[69449] <Error>: CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.

setting symbolic breakpoint at CGPostError
shows the offending call to render view without context

Would it be possible to make it optional to pretender views without contexts?
Seems like it was added in this change: #39 1f3cf29 to fix #38

Failure installing with Carthage

carthage build Nimble-Snapshots --platform ios --toolchain com.apple.dt.toolchain.Swift_3_0_2

Fails with the error:

“Use Legacy Swift Language Version” (SWIFT_VERSION) is required to be configured correctly for targets which use Swift. Use the [Edit > Convert > To Current Swift Syntax…] menu to choose a Swift version or use the Build Settings editor to configure the build setting directly.

I understand this repo is trying to maintain compatibility with Swift 2.3 and Swift 3.0.x, but unfortunately it looks like Xcode now enforces SWIFT_VERSION being set in each target :/

Tests many sizes of view on one test

Hi, I usually tests the layout of my view controllers taking snapshots on all the available sizes. Because of this I did a little wrapper to pass a array of sizes and test all on one call. I did not find a contribution guide to follow, so I would like to ask what do you thing of this feature? I can do a PR for evaluation.

Nimble Snapshot Crashing in beforeSuite

I'm working with a bit of legacy code that is configured, seemingly identically, in multiple places.
Before refactoring each of these code paths to use a single method I thought Id create a reference snapshot with what should be the result and then compare each of the existing configure methods with the reference snapshot.

It made sense to me to put the reference code in beforeSuite
I'm encountering a crash here because of the force unwrapping of currentExampleMetadata which is set here. Quick has a beforeSuite configuration method but it doesn't have metaData to pass in.

I can think of a number of workarounds, I'm mostly raising the issue because not being able to use snapshots in beforeSuite was non-obvious and took a little digging to figure out why and wanted to see what other people thought.

Folder structure is inconsistent with test names

So, right now this can happen:

screen shot 2014-10-09 at 3 14 00 pm

in an ideal world, this spec:

import Quick
import Nimble

class ConfirmYourBidEnterYourEmailViewControllerTests: QuickSpec {
    override func spec() {

        it("looks right by default") {
            let sut = ConfirmYourBidEnterYourEmailViewController.instantiateFromStoryboard()
            expect(sut).to(recordSnapshot(named:"default"))
        }
    }
}

Puts the image in ReferenceImages/ConfirmYourBidEnterYourEmailViewControllerTests/default_2x.png

Switch checks with records

I was looking to add support for record mode and I saw switchChecksWithRecords, somehow it's undocumented and internal but apparently is always false inside the module.
I'm wondering if it is a mistake or you prefer it to be an hidden feature that also require to @testable import Nimble_Snapshots to use it.
If you are interested I can open a PR to make it public and document it.

Compiler Errors After Installation

We depend on FBSnapshotTestCase with a version specifier of ~> 1.5, but FBSnapshotTestCase 1.6 introduced breaking changes, so we need to update our use of it.

Improper rendering

On iOS 9, I'm getting views and buttons that aren't rendering properly.

screen shot 2015-11-04 at 2 17 46 pm

Specifically, customizations to views like text and images aren't being respected.

Calling something like

view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
expect(view).to(haveValidSnapshot())

Fixes the problem, but it's a hassle to do it every time. Using usesDrawViewHierarchyInRect fixes the issue but introduces many other problems (I don't trust UIKit's API for snapshots).

I think adding to the Snapshotable extensions to call this method ourselves would be an OK fix. Thoughts?

Can't set custom reference dir

While trying to set a custom reference dir I got the following error:

Could not infer reference image folder – You should provide a reference dir using FBSnapshotTest.setReferenceImagesDirectory(FB_REFERENCE_IMAGE_DIR)

This class is declared in HaveValidSnapshot.swift but is not declared public, so it seems you can't really use it. Is this intended behaviour? How can you set a custom reference directory?

Extension for turning on/off snapshots for entire classes

Hi!

So I've really been loving this for testing but I've been bummed when I had to change recordSnapshot to haveValidSnapshot and vice versa all across a subset of tests when I deleted the folder or updated wide reaching changes. So I came up with this extension:

enum NimbleSnapshotTestAction {
    case HaveValidSnapshot
    case RecordSnapshot

    func performSnapshotAction(
        named name: String?, usesDrawRect: Bool,
              tolerance: CGFloat?) -> MatcherFunc<Snapshotable>
    {
        switch self {
        case .HaveValidSnapshot:
            return haveValidSnapshot(named: name, usesDrawRect: usesDrawRect, tolerance: tolerance)
        case .RecordSnapshot:
            return recordSnapshot(named: name, usesDrawRect: usesDrawRect)

        }
    }
}

protocol NimbleSnapshotTest {
    var snapshotAction: NimbleSnapshotTestAction { get }
}

extension NimbleSnapshotTest where Self: XCTestCase {
    func performSnapshotAction(
        action: NimbleSnapshotTestAction? = nil,
        named name: String? = nil, usesDrawRect: Bool = false,
              tolerance: CGFloat? = nil) -> MatcherFunc<Snapshotable>
    {
        return (action ?? snapshotAction).performSnapshotAction(named: name, usesDrawRect: usesDrawRect, tolerance: tolerance)
    }
}

This allows me to to do things like:

class TestNimbleSnapshotSpec: QuickSpec, NimbleSnapshotTest {
    var snapshotAction: NimbleSnapshotTestAction = .HaveValidSnapshot

    override func spec() {
        let view = UIView()
        let snapshotAction = self.snapshotAction
        it("can snapshot or record depending on the top level") {
            expect(view).to(self.performSnapshotAction())
        }
        it("can also pass in the action explicitly (here we use local reference to get rid of the self)") {
            expect(view).to(self.performSnapshotAction(snapshotAction))
        }
        context("But if some other suite was created") {
            let snapshotAction: NimbleSnapshotTestAction = .RecordSnapshot
            it("could reference a local snapshotAction") {
                expect(view).to(self.performSnapshotAction(snapshotAction))
            }
            context("and that reference could be as deep as") {
                it("wanted to be") {
                    expect(view).to(self.performSnapshotAction(snapshotAction))
                }
                it("could even make another one nested deeper") {
                    let snapshotAction: NimbleSnapshotTestAction = .HaveValidSnapshot
                    expect(view).to(self.performSnapshotAction(snapshotAction))
                }
                context("or") {
                    it("could just reference the one scoped above") {
                        let snapshotAction: NimbleSnapshotTestAction = snapshotAction
                        expect(view).to(self.performSnapshotAction(snapshotAction))
                    }
                }
            }
        }
    }
}

I hate the fact that you have to use self in these, but for me the benefit of being able to change record/checks across multiple tests with one line rather than many is enough to make me willing to sacrifice.

If this is something you think is cool and want to include feel free, I can also write tests and conform to better/different styles, but otherwise just feel free to close this issue!

Thanks again for the great glue between Swift+Nimble+FBSnaphotTestCase!

Installing via Cocoapods doesn't compile

When I try to install this via cocoapods, the test project fails to compile due to the following error:

// FBSnapshotTestController(testName: _testFileName())
Pods/Nimble-Snapshots/HaveValidSnapshot.swift:40:84: Extra argument 'testName' in call  

I can't figure out why this is happening, maybe cocoapods is not installing the correct version of the FBSnapshotTestCase pod? I can get this to compile by just manually bringing in the HasValidSnapshot.swift into my test target.

Can't make Nimble-Snapshots to apply UIAppearance during tests

I've been trying to test that my views behave correctly using UIAppearance but I haven't been able to make Nimble-Snapshots apply my configured UIAppearance attributes during the test. After taking a look at both the code of ios-snapshot-test-case and Nimble-Snapshots it seems this should be supported according to this pull request and using the usesDrawRect parameter in recordSnapshot.

Here you have a failing test case:

import UIKit
import Quick
import Nimble
import Nimble_Snapshots


class SnapshotSpec: QuickSpec {
    override func spec() {
        describe("Appearance snapshots") {
            it("should work") {
                UILabel.appearance().textColor = UIColor.blue
                UILabel.appearance().font = UIFont.boldSystemFont(ofSize: 20)
                
                let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
                label.text = "This should be big & blue"
                
                expect(label).to(recordSnapshot(named: "label", usesDrawRect: true))
            }
        }
    }
}

The label in the recorded snapshot uses default attributes instead of the attributes configured using the UIAppearance proxy. Am I missing anything?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.