Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mock protocols and view mocks #19

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: objective-c

osx_image: xcode8.3
osx_image: xcode9.2

env:
global:
Expand Down
30 changes: 30 additions & 0 deletions Pod/Classes/Categories/UITraitCollection+ViewMockable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// UITraitCollection+ViewMockable.swift
// AGSnapshotHelper
//
// Created by Adam Grzegorowski on 05/05/2018.
// Copyright © 2018 Allegro Group. All rights reserved.
//

import Foundation
import UIKit

extension UITraitCollection {

convenience init(mockParameters: ViewMockable) {
var newTraitCollection: [UITraitCollection] = [
UITraitCollection(verticalSizeClass: mockParameters.verticalSizeClass),
UITraitCollection(horizontalSizeClass: mockParameters.horizontalSizeClass)
]

if #available(iOS 10.0, *) {
newTraitCollection.append(UITraitCollection(layoutDirection: mockParameters.layoutDirection))

if let mockedPreferredContentSizeCategory = mockParameters.preferredContentSizeCategory {
newTraitCollection.append(UITraitCollection(preferredContentSizeCategory: mockedPreferredContentSizeCategory))
}
}

self.init(traitsFrom: newTraitCollection)
}
}
25 changes: 25 additions & 0 deletions Pod/Classes/Mocking/DeviceMockable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// DeviceMockable.swift
// AGSnapshotHelper
//
// Created by Adam Grzegorowski on 06/03/2018.
// Copyright © 2018 Allegro Group. All rights reserved.
//

import Foundation
import UIKit

/// A set of properties that provide information about device.
@objc(AGDeviceMockable)
public protocol DeviceMockable {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DeviceMockable > MockableDevice?


/// Device name.
var name: String { get set }

/// Interface orientation to stub.
var orientation: UIInterfaceOrientation { get set }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why UIInterfaceOrientation and not UIUserInterfaceSizeClass? I don't know if combination of UIUserInterfaceIdiom and UIInterfaceOrientation makes sense. UIUserInterfaceIdiom can be UIUserInterfaceIdiomCarPlay, what orientation has this class then?

If we want to support only iOS (iPhone and iPad) we should be more specific than UIUserInterfaceIdiom, maybe create own type.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why UIInterfaceOrientation and not UIUserInterfaceSizeClass? I don't know if combination of UIUserInterfaceIdiom and UIInterfaceOrientation makes sense.

UIUserInterfaceSizeClasses are defined in ViewMockable protocol.

UIUserInterfaceIdiom can be UIUserInterfaceIdiomCarPlay, what orientation has this class then?

I have never checked interface orientation on CarPlay, but UIInterfaceOrientation has value .unknown so in worst case we could use that.

If we want to support only iOS (iPhone and iPad) we should be more specific than UIUserInterfaceIdiom, maybe create own type.

I don't think we have to limit only to iOS. Car play and tvOS probably can be supported out of box, watchKit requires some work.


/// User interface idiom to stub. Part of trait collection.
var userInterfaceIdiom: UIUserInterfaceIdiom { get set }
}

89 changes: 89 additions & 0 deletions Pod/Classes/Mocking/MockingDeviceView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// MockingDeviceView.swift
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MockingDeviceView or MockedDeviceView? 🧐

Copy link
Owner Author

@Grubas7 Grubas7 May 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably you are right.
Mocking and Mockable is good for protocol (https://swift.org/documentation/api-design-guidelines/#protocols-describing-what-is-should-read-as-nouns) and for class probably MockedDeviceView would be better.

Please check also remark: #19 (comment)

// AGSnapshotHelper
//
// Created by Adam Grzegorowski on 10/03/2018.
// Copyright © 2018 Allegro Group. All rights reserved.
//

import Foundation
import UIKit

public typealias DeviceViewMockable = ViewMockable & DeviceMockable
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DeviceViewMockable > MockableDeviceView?


/// View stubbing layout defining properties and information about device.
/// Some of layout properties (e.g. `safeAreaInsets`, or `traitCollection`) are read-only,
/// so this view can be used as container which helps to force custom layout.
/// Additionally `mockParameters` contains information about mocked device,
/// which can be use to distinguish interface orientation or device model.
@objc(AGMockingDeviceView)
public class MockingDeviceView: UIView {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe DeviceMockView? I find those classes names and protocols pretty confusing.

Copy link
Owner Author

@Grubas7 Grubas7 May 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if Mock/Mocking/Mockable word is correctly used.

mock objects are simulated objects that mimic the behavior of real objects in controlled ways*

And this is a UIView subclass, so it's not a abstract object that try to fulfil our expectation, but it's normal class. The behaviour that I'm trying to achieve is basically inject some read-only data to UIView instance. Maybe more precise name would be StubbedView/StubbedDeviceView?

*https://en.wikipedia.org/wiki/Mock_object


// MARK: - Public methods

/// Object storing properties to stub.
public var mockParameters: DeviceViewMockable? {
didSet {
guard let mockParameters = mockParameters else {
return
}

frame.size = mockParameters.size
setUpLayoutMargins()
}
}

/// Initializes and returns a newly allocated view object with rectangle frame equal to 0,0 origin and `mockParameters.size` size.
///
/// - Parameter mockParameters: Object with properties to stub.
public init(mockParameters: DeviceViewMockable) {
let newFrame = CGRect(origin: .zero, size: mockParameters.size)
super.init(frame: newFrame)
}

// MARK: - Overriden methods

override public init(frame: CGRect) {
super.init(frame: frame)

setUpLayoutMargins()
}

required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

setUpLayoutMargins()
}

@available(iOS 11.0, *)
override public var safeAreaInsets: UIEdgeInsets {
return mockParameters?.safeAreaInsets ?? super.safeAreaInsets
}

override public var traitCollection: UITraitCollection {
guard let mockParameters = mockParameters else {
return super.traitCollection
}

return UITraitCollection(traitsFrom: [
super.traitCollection,
.init(mockParameters: mockParameters),
.init(userInterfaceIdiom: mockParameters.userInterfaceIdiom)
])
}

// MARK: - Private methods

private func setUpLayoutMargins() {

if #available(iOS 11.0, *) {
if let mockedDirectionalLayoutMargins = mockParameters?.directionalLayoutMargins {
directionalLayoutMargins = mockedDirectionalLayoutMargins
}
}

if let mockedLayoutMargins = mockParameters?.layoutMargins {
layoutMargins = mockedLayoutMargins
}
}
}
85 changes: 85 additions & 0 deletions Pod/Classes/Mocking/MockingView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// MockingView.swift
// AGSnapshotHelper
//
// Created by Adam Grzegorowski on 22/02/2018.
// Copyright © 2018 Allegro Group. All rights reserved.
//

import Foundation
import UIKit

/// View stubbing layout defining properties.
/// Some of layout properties (e.g. `safeAreaInsets`, or `traitCollection`) are read-only,
/// so this view can be used as container which helps to force custom layout.
@objc(AGMockingView)
public class MockingView: UIView {

// MARK: - Public methods

/// Object storing properties to stub.
public var mockParameters: ViewMockable? {
didSet {
guard let mockParameters = mockParameters else {
return
}

frame.size = mockParameters.size
setUpLayoutMargins()
}
}

/// Initializes and returns a newly allocated view object with rectangle frame equal to 0,0 origin and `mockParameters.size` size.
///
/// - Parameter mockParameters: Object with properties to stub.
public init(mockParameters: ViewMockable) {
let newFrame = CGRect(origin: .zero, size: mockParameters.size)
super.init(frame: newFrame)
}

// MARK: - Overriden methods

override public init(frame: CGRect) {
super.init(frame: frame)

setUpLayoutMargins()
}

required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

setUpLayoutMargins()
}

@available(iOS 11.0, *)
override public var safeAreaInsets: UIEdgeInsets {
return mockParameters?.safeAreaInsets ?? super.safeAreaInsets
}

override public var traitCollection: UITraitCollection {
guard let mockParameters = mockParameters else {
return super.traitCollection
}

return UITraitCollection(traitsFrom: [
super.traitCollection,
.init(mockParameters: mockParameters),
])

}

// MARK: - Private methods

private func setUpLayoutMargins() {

if let mockedLayoutMargins = mockParameters?.layoutMargins {
layoutMargins = mockedLayoutMargins
}

if #available(iOS 11.0, *) {
if let mockedDirectionalLayoutMargins = mockParameters?.directionalLayoutMargins {
directionalLayoutMargins = mockedDirectionalLayoutMargins
}
}
}
}
43 changes: 43 additions & 0 deletions Pod/Classes/Mocking/ViewMockable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// ViewMockable.swift
// AGSnapshotHelper
//
// Created by Adam Grzegorowski on 22/02/2018.
// Copyright © 2018 Allegro Group. All rights reserved.
//

import Foundation
import UIKit

/// A set of properties that provide information about layout of view.
@objc(AGViewMockable)
public protocol ViewMockable {

/// Size to stub
var size: CGSize { get set }

/// Safe area insets to stub.
var safeAreaInsets: UIEdgeInsets { get set }

/// Layout margins insets to stub.
/// In iOS 11 and later use the `directionalLayoutMargins`.
var layoutMargins: UIEdgeInsets { get set }

/// Directional layout margins insets to stub.
@available(iOS 11.0, *)
var directionalLayoutMargins: NSDirectionalEdgeInsets { get set }

/// Horizontal size class to stub. Part of trait collection.
var horizontalSizeClass: UIUserInterfaceSizeClass { get set }

/// Vertical size class to stub. Part of trait collection.
var verticalSizeClass: UIUserInterfaceSizeClass { get set }

/// Preferred content size category to stub. Part of trait collection.
@available(iOS 10.0, *)
var preferredContentSizeCategory: UIContentSizeCategory? { get set }

/// Layout direction to stub. Part of trait collection.
@available(iOS 10.0, *)
var layoutDirection: UITraitEnvironmentLayoutDirection { get set }
}
32 changes: 32 additions & 0 deletions Tests/AGSnapshotHelper.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@

/* Begin PBXBuildFile section */
001744F1D98E90B4451755FF /* Pods_AGSnapshotHelperTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C5CE19F12645FEA3BD48CB44 /* Pods_AGSnapshotHelperTests.framework */; };
4123DEC3205453C100C04589 /* AGViewMockParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 4123DEC1205453C000C04589 /* AGViewMockParameters.m */; };
4144A6141D00CC4D009B134D /* AGSampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4144A6131D00CC4D009B134D /* AGSampleView.m */; };
414FC4382014E749006BE631 /* NotificationCenter+PostContentSizeCategoryChangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 414FC4372014E749006BE631 /* NotificationCenter+PostContentSizeCategoryChangeTests.swift */; };
415093CA1C498BD900F17144 /* FBSnapshotTestCase+AGSnapshotHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 41DDC1771C4985250098190D /* FBSnapshotTestCase+AGSnapshotHelperTests.m */; };
41D5CE3C204F29AD00CDFD67 /* MockingViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41D5CE3B204F29AD00CDFD67 /* MockingViewTests.swift */; };
41D5CE3E204F319100CDFD67 /* NSDirectionalEdgeInsets+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41D5CE3D204F319100CDFD67 /* NSDirectionalEdgeInsets+Equatable.swift */; };
41D6DE781FD8987000EAED6E /* UIApplication+PreferredContentSizeCategoryMockTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41D6DE771FD8987000EAED6E /* UIApplication+PreferredContentSizeCategoryMockTests.swift */; };
41E5497220545FC800D85E05 /* AGDeviceViewMockParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 41E5497120545FC800D85E05 /* AGDeviceViewMockParameters.m */; };
41E549742054668C00D85E05 /* MockingDeviceViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E549732054668C00D85E05 /* MockingDeviceViewTests.swift */; };
660525BA1C09112000B5BF97 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 660525B91C09112000B5BF97 /* main.m */; };
660525BD1C09112000B5BF97 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 660525BC1C09112000B5BF97 /* AppDelegate.m */; };
660525C01C09112000B5BF97 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 660525BF1C09112000B5BF97 /* ViewController.m */; };
Expand All @@ -32,12 +37,19 @@

/* Begin PBXFileReference section */
10CDCDF718D6988DFDCD3997 /* Pods-AGSnapshotHelperTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AGSnapshotHelperTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AGSnapshotHelperTests/Pods-AGSnapshotHelperTests.release.xcconfig"; sourceTree = "<group>"; };
4123DEC1205453C000C04589 /* AGViewMockParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AGViewMockParameters.m; sourceTree = "<group>"; };
4123DEC2205453C100C04589 /* AGViewMockParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AGViewMockParameters.h; sourceTree = "<group>"; };
4144A6121D00CC4D009B134D /* AGSampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AGSampleView.h; sourceTree = "<group>"; };
4144A6131D00CC4D009B134D /* AGSampleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AGSampleView.m; sourceTree = "<group>"; };
414FC4372014E749006BE631 /* NotificationCenter+PostContentSizeCategoryChangeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NotificationCenter+PostContentSizeCategoryChangeTests.swift"; sourceTree = "<group>"; };
41D5CE3B204F29AD00CDFD67 /* MockingViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockingViewTests.swift; sourceTree = "<group>"; };
41D5CE3D204F319100CDFD67 /* NSDirectionalEdgeInsets+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDirectionalEdgeInsets+Equatable.swift"; sourceTree = "<group>"; };
41D6DE771FD8987000EAED6E /* UIApplication+PreferredContentSizeCategoryMockTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+PreferredContentSizeCategoryMockTests.swift"; sourceTree = "<group>"; };
41D6DE791FD8989500EAED6E /* AGSnapshotHelperTests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AGSnapshotHelperTests-Bridging-Header.h"; sourceTree = "<group>"; };
41DDC1771C4985250098190D /* FBSnapshotTestCase+AGSnapshotHelperTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "FBSnapshotTestCase+AGSnapshotHelperTests.m"; sourceTree = "<group>"; };
41E5497020545FC800D85E05 /* AGDeviceViewMockParameters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AGDeviceViewMockParameters.h; sourceTree = "<group>"; };
41E5497120545FC800D85E05 /* AGDeviceViewMockParameters.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AGDeviceViewMockParameters.m; sourceTree = "<group>"; };
41E549732054668C00D85E05 /* MockingDeviceViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockingDeviceViewTests.swift; sourceTree = "<group>"; };
660525B51C09112000B5BF97 /* AGSnapshotHelperTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AGSnapshotHelperTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
660525B81C09112000B5BF97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
660525B91C09112000B5BF97 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -82,6 +94,19 @@
name = Pods;
sourceTree = "<group>";
};
41D5CE38204F298200CDFD67 /* Mocking */ = {
isa = PBXGroup;
children = (
41D5CE3B204F29AD00CDFD67 /* MockingViewTests.swift */,
41E549732054668C00D85E05 /* MockingDeviceViewTests.swift */,
41E5497020545FC800D85E05 /* AGDeviceViewMockParameters.h */,
41E5497120545FC800D85E05 /* AGDeviceViewMockParameters.m */,
4123DEC2205453C100C04589 /* AGViewMockParameters.h */,
4123DEC1205453C000C04589 /* AGViewMockParameters.m */,
);
path = Mocking;
sourceTree = "<group>";
};
4F61E30CF0A630D2526319F1 /* Frameworks */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -147,10 +172,12 @@
isa = PBXGroup;
children = (
66C7D4CC1C0226F4001C6546 /* Categories */,
41D5CE38204F298200CDFD67 /* Mocking */,
41DDC1771C4985250098190D /* FBSnapshotTestCase+AGSnapshotHelperTests.m */,
4144A6121D00CC4D009B134D /* AGSampleView.h */,
4144A6131D00CC4D009B134D /* AGSampleView.m */,
41D6DE791FD8989500EAED6E /* AGSnapshotHelperTests-Bridging-Header.h */,
41D5CE3D204F319100CDFD67 /* NSDirectionalEdgeInsets+Equatable.swift */,
730869FF1BF8E1E20033F34A /* Info.plist */,
);
path = AGSnapshotHelperTests;
Expand Down Expand Up @@ -327,8 +354,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
41E549742054668C00D85E05 /* MockingDeviceViewTests.swift in Sources */,
414FC4382014E749006BE631 /* NotificationCenter+PostContentSizeCategoryChangeTests.swift in Sources */,
41D5CE3E204F319100CDFD67 /* NSDirectionalEdgeInsets+Equatable.swift in Sources */,
41D5CE3C204F29AD00CDFD67 /* MockingViewTests.swift in Sources */,
41D6DE781FD8987000EAED6E /* UIApplication+PreferredContentSizeCategoryMockTests.swift in Sources */,
4123DEC3205453C100C04589 /* AGViewMockParameters.m in Sources */,
41E5497220545FC800D85E05 /* AGDeviceViewMockParameters.m in Sources */,
415093CA1C498BD900F17144 /* FBSnapshotTestCase+AGSnapshotHelperTests.m in Sources */,
4144A6141D00CC4D009B134D /* AGSampleView.m in Sources */,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
// Use this file to import your target's public headers that you would like to expose to Swift.
//

#import <AGSnapshotHelper/UIApplication+PreferredContentSizeCategoryMock.h>
#import "AGDeviceViewMockParameters.h"
#import "AGViewMockParameters.h"
Loading