Skip to content

Commit 30645d8

Browse files
authored
Merge pull request #60 from leboncoin/feature/iconography
[Iconography] Add new global icons
2 parents 196bcf5 + 2da92d5 commit 30645d8

30 files changed

Lines changed: 418 additions & 11 deletions

.sourcery/template/SparkAutoImport.stencil

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Foundation
44
import UIKit
55
import SwiftUI
66
import XCTest
7+
import Testing
78

89
{% for import in argument.autoMockableImports %}
910
import {{ import }}

.sourcery/template/SparkAutoMockTest.stencil

Lines changed: 145 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
private init(){
1313
}
1414

15-
// MARK: - Tests
15+
// MARK: - XCTest
1616

1717
{% for method in protocol.allMethods|!definedInExtension %}
18-
18+
1919
public static func XCTCallsCount(
2020
_ mock: {% call mockName protocol %},
2121
{% call swiftifyMethodNameV2 method %}NumberOfCalls numberOfCalls: Int
@@ -26,7 +26,7 @@
2626
"Wrong {{ method.name }} number of called on {{ protocol.name }}"
2727
)
2828
}
29-
29+
3030
public static func XCTCalled(
3131
_ mock: {% call mockName protocol %},
3232
{% call swiftifyMethodNameV2 method %}Called isCalled: Bool
@@ -136,6 +136,148 @@
136136
}
137137

138138
{% endfor %}
139+
140+
// MARK: - Swift Testing
141+
142+
{% for method in protocol.allMethods|!definedInExtension %}
143+
144+
public static func expectCallsCount(
145+
_ mock: {% call mockName protocol %},
146+
{% call swiftifyMethodNameV2 method %}NumberOfCalls numberOfCalls: Int,
147+
sourceLocation: Testing.SourceLocation = #_sourceLocation
148+
) {
149+
#expect(
150+
mock.{% call swiftifyMethodNameV2 method %}CallsCount == numberOfCalls,
151+
"Wrong {{ method.name }} number of called on {{ protocol.name }}. Expected \(numberOfCalls), got \(mock.{% call swiftifyMethodNameV2 method %}CallsCount)",
152+
sourceLocation: sourceLocation
153+
)
154+
}
155+
156+
public static func expectCalled(
157+
_ mock: {% call mockName protocol %},
158+
{% call swiftifyMethodNameV2 method %}Called isCalled: Bool,
159+
sourceLocation: Testing.SourceLocation = #_sourceLocation
160+
) {
161+
#expect(
162+
mock.{% call swiftifyMethodNameV2 method %}Called == isCalled,
163+
"Wrong {{ method.name }} isCalled on {{ protocol.name }}. Expected \(isCalled), got \(mock.{% call swiftifyMethodNameV2 method %}Called)",
164+
sourceLocation: sourceLocation
165+
)
166+
}
167+
168+
public static func expect{% for key, value in method.annotations where value == "Identical" %}{% if forloop.first %}<
169+
{% for param in method.parameters where method.annotations[param.name] == "Identical" %}
170+
{% call dynamicTypeName param.name %}: AnyObject{% if not forloop.last or method.annotations["return"] == "Identical" %},{% endif %}
171+
{% endfor %}
172+
{% if method.annotations["return"] == "Identical" %}
173+
{% call dynamicTypeName "return" %}: AnyObject
174+
{% endif %}
175+
>{% endif %}{% endfor %}(
176+
_ mock: {% call mockName protocol %},
177+
expectedNumberOfCalls: Int,
178+
{% for param in method.parameters %}
179+
{% call givenParameter param %}: {% if method.annotations[param.name] == "Identical" %}{% call dynamicTypeName param.name %}{% elif protocol.associatedTypes[param.typeName] != nil %}{% call mockName protocol %}.{{ protocol.associatedTypes[param.typeName].name }}Mock{% else %}{{ '(' if param.isClosure or param.typeName|contains:"any" }}{{ param.typeName | replace:"?","" }}{{ ')' if param.isClosure or param.typeName|contains:"any" }}{% endif %}? = nil{% if not forloop.last or not method.returnTypeName.isVoid %}, {% endif %}
180+
{% endfor %}
181+
{% if not method.returnTypeName.isVoid %}expectedReturnValue: {% if method.annotations["return"] == "Identical" %}{% call dynamicTypeName "return" %}{% elif protocol.associatedTypes["Return"] != nil %}{% call mockName protocol %}.ReturnMock{% if method.isOptionalReturnType %}?{% endif %}{% else %}{{ method.returnTypeName }}{% endif %},{% endif %}
182+
sourceLocation: Testing.SourceLocation = #_sourceLocation
183+
) {
184+
// Count
185+
#expect(
186+
mock.{% call swiftifyMethodNameV2 method %}CallsCount == expectedNumberOfCalls,
187+
"Wrong {{ method.name }} number of called on {{ protocol.name }}. Expected \(expectedNumberOfCalls), got \(mock.{% call swiftifyMethodNameV2 method %}CallsCount)",
188+
sourceLocation: sourceLocation
189+
)
190+
191+
// Parameters
192+
if expectedNumberOfCalls > 0 {
193+
{% if method.parameters.count > 1 %}
194+
let args = mock.{% call swiftifyMethodNameV2 method %}ReceivedArguments
195+
196+
{% endif %}
197+
{% for param in method.parameters %}
198+
// {{ param.name|upperFirstLetter }}
199+
if let {% call givenParameter param %} {
200+
201+
{% if method.annotations[param.name] == "Identical" %}
202+
{% if method.parameters.count == 1 %}
203+
guard let receivedValue = mock.{% call swiftifyMethodNameV2 method %}Received{{ param.name|upperFirstLetter }} as? _{{ param.name|upperFirstLetter }} else {
204+
Issue.record("Wrong {{ method.name }} {{ param.name }} parameter type on {{ protocol.name }}", sourceLocation: sourceLocation)
205+
return
206+
}
207+
#expect(
208+
receivedValue === given{{ param.name | snakeToCamelCase }},
209+
"Wrong {{ method.name }} {{ param.name }} parameter on {{ protocol.name }}",
210+
sourceLocation: sourceLocation
211+
)
212+
{% else %}
213+
guard let receivedValue = args?.{{ param.name }} as? _{{ param.name|upperFirstLetter }} else {
214+
Issue.record("Wrong {{ method.name }} {{ param.name }} parameter type on {{ protocol.name }}", sourceLocation: sourceLocation)
215+
return
216+
}
217+
#expect(
218+
receivedValue === given{{ param.name | snakeToCamelCase }},
219+
"Wrong {{ method.name }} {{ param.name }} parameter on {{ protocol.name }}",
220+
sourceLocation: sourceLocation
221+
)
222+
{% endif %}
223+
{% else %}
224+
{% if method.parameters.count == 1 %}
225+
#expect(
226+
mock.{% call swiftifyMethodNameV2 method %}Received{{ param.name|upperFirstLetter }} == given{{ param.name | snakeToCamelCase }},
227+
"Wrong {{ method.name }} {{ param.name }} parameter on {{ protocol.name }}. Expected \(String(describing: given{{ param.name | snakeToCamelCase }})), got \(String(describing: mock.{% call swiftifyMethodNameV2 method %}Received{{ param.name|upperFirstLetter }}))",
228+
sourceLocation: sourceLocation
229+
)
230+
{% else %}
231+
#expect(
232+
args?.{{ param.name }} == given{{ param.name | snakeToCamelCase }},
233+
"Wrong {{ method.name }} {{ param.name }} parameter on {{ protocol.name }}. Expected \(String(describing: given{{ param.name | snakeToCamelCase }})), got \(String(describing: args?.{{ param.name }}))",
234+
sourceLocation: sourceLocation
235+
)
236+
{% endif %}
237+
{% endif %}
238+
} else {
239+
#expect(
240+
{% if method.parameters.count == 1 %}mock.{% call swiftifyMethodNameV2 method %}Received{{ param.name|upperFirstLetter }}{% else %}args?.{{ param.name }}{% endif %} == nil,
241+
"Wrong {{ method.name }} {{ param.name }} parameter value on {{ protocol.name }}. Should be nil but got \(String(describing: {% if method.parameters.count == 1 %}mock.{% call swiftifyMethodNameV2 method %}Received{{ param.name|upperFirstLetter }}{% else %}args?.{{ param.name }}{% endif %}))",
242+
sourceLocation: sourceLocation
243+
)
244+
}
245+
246+
{% endfor %}
247+
}
248+
249+
{% if not method.returnTypeName.isVoid %}
250+
// Return
251+
{% if method.isOptionalReturnType %}if let expectedReturnValue { {% endif %}
252+
{% if method.annotations["return"] == "Identical" %}
253+
guard let returnValue = mock.{% call swiftifyMethodNameV2 method %}ReturnValue as? _Return else {
254+
Issue.record("Wrong {{ method.name }} return value type on {{ protocol.name }}", sourceLocation: sourceLocation)
255+
return
256+
}
257+
#expect(
258+
returnValue === expectedReturnValue,
259+
"Wrong {{ method.name }} return value on {{ protocol.name }}",
260+
sourceLocation: sourceLocation
261+
)
262+
{% else %}
263+
#expect(
264+
mock.{% call swiftifyMethodNameV2 method %}ReturnValue == expectedReturnValue,
265+
"Wrong {{ method.name }} return value on {{ protocol.name }}. Expected \(String(describing: expectedReturnValue)), got \(String(describing: mock.{% call swiftifyMethodNameV2 method %}ReturnValue))",
266+
sourceLocation: sourceLocation
267+
)
268+
{% endif %}
269+
{% endif %}
270+
{% if method.isOptionalReturnType %} } else {
271+
#expect(
272+
mock.{% call swiftifyMethodNameV2 method %}ReturnValue == nil,
273+
"Wrong {{ method.name }} return value on {{ protocol.name }}. Should be nil but got \(String(describing: mock.{% call swiftifyMethodNameV2 method %}ReturnValue))",
274+
sourceLocation: sourceLocation
275+
)
276+
}
277+
{% endif %}
278+
}
279+
280+
{% endfor %}
139281
}
140282

141283
{% endif %}{% endfor %}

.sourcery/template/SparkAutoPublisherTest.stencil

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
private init(){
1616
}
1717

18-
// MARK: - Tests
18+
// MARK: - XCTest
1919

2020
{% for variable in class.allVariables|!definedInExtension where variable.readAccess != "private" and variable.definedInTypeName|contains: class.name and variable.attributes["Published"] != nil %}
21-
21+
2222
public static func XCTSinksCount(
2323
{{ variable.name }} mock: PublisherMock<Published<{% call existentialVariableTypeName variable.typeName %}>.Publisher>,
2424
expectedNumberOfSinks: Int
@@ -67,6 +67,71 @@
6767
}
6868

6969
{% endfor %}
70+
71+
// MARK: - Swift Testing
72+
73+
{% for variable in class.allVariables|!definedInExtension where variable.readAccess != "private" and variable.definedInTypeName|contains: class.name and variable.attributes["Published"] != nil %}
74+
75+
public static func expectSinksCount(
76+
{{ variable.name }} mock: PublisherMock<Published<{% call existentialVariableTypeName variable.typeName %}>.Publisher>,
77+
expectedNumberOfSinks: Int,
78+
sourceLocation: Testing.SourceLocation = #_sourceLocation
79+
) {
80+
#expect(
81+
mock.sinkCount == expectedNumberOfSinks,
82+
"Expected \(expectedNumberOfSinks) sinks, but got \(mock.sinkCount)",
83+
sourceLocation: sourceLocation
84+
)
85+
}
86+
87+
public static func expect{% if class.annotations[variable.name] == "Identical" %}<
88+
Value: AnyObject
89+
>{% endif %}(
90+
{{ variable.name }} mock: PublisherMock<Published<{% call existentialVariableTypeName variable.typeName %}>.Publisher>,
91+
expectedNumberOfSinks: Int,
92+
expectedValue: {% if class.annotations[variable.name] == "Identical" %}Value{% if variable.isOptional %}?{% endif %}{% else %}{{ variable.typeName }}{% endif %}{% if variable.isOptional %} = nil{% endif %},
93+
sourceLocation: Testing.SourceLocation = #_sourceLocation
94+
) {
95+
// Count
96+
#expect(
97+
mock.sinkCount == expectedNumberOfSinks,
98+
"Expected \(expectedNumberOfSinks) sinks, but got \(mock.sinkCount)",
99+
sourceLocation: sourceLocation
100+
)
101+
102+
// Value
103+
if expectedNumberOfSinks > 0 {
104+
{% if variable.isOptional %}if let expectedValue { {% endif %}
105+
{% if class.annotations[variable.name] == "Identical" %}
106+
guard let value = mock.sinkValue as? Value else {
107+
Issue.record("Expected value to be of type \(Value.self)", sourceLocation: sourceLocation)
108+
return
109+
}
110+
#expect(
111+
value === expectedValue,
112+
"Publisher values are not identical",
113+
sourceLocation: sourceLocation
114+
)
115+
{% else %}
116+
#expect(
117+
mock.sinkValue == expectedValue,
118+
"Expected value \(String(describing: expectedValue)), but got \(String(describing: mock.sinkValue))",
119+
sourceLocation: sourceLocation
120+
)
121+
{% endif %}
122+
{% if variable.isOptional %}
123+
} else {
124+
#expect(
125+
mock.sinkValue == nil,
126+
"Expected nil value, but got \(String(describing: mock.sinkValue))",
127+
sourceLocation: sourceLocation
128+
)
129+
}
130+
{% endif %}
131+
}
132+
}
133+
134+
{% endfor %}
70135
}
71136

72137
{% endif %}{% endfor %}

Sources/Core/Control/PropertyState/ControlPropertyState.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88

99
/// Contains the dynamic property for a ControlState.
10+
@available(*, deprecated, message: "Only used on deprecated button. Remove ASAP (13/03/2026")
1011
@_spi(SI_SPI) public final class ControlPropertyState<T: Equatable> {
1112

1213
// MARK: - Properties

Sources/Core/Control/PropertyStates/ControlPropertyStates.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import Foundation
1010

1111
/// Manage all the states for a dynamic property.
12+
@available(*, deprecated, message: "Only used on deprecated button. Remove ASAP (13/03/2026")
1213
@_spi(SI_SPI) public final class ControlPropertyStates<PropertyType: Equatable> {
1314

1415
// MARK: - Type Alias

Sources/Core/Control/State/ControlState.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import Foundation
1010

1111
/// Constants describing the state of a Spark control.
12+
@available(*, deprecated, message: "Only used on deprecated button. Remove ASAP (13/03/2026")
1213
@frozen public enum ControlState: CaseIterable, Equatable {
1314
/// The normal, or default, state of a control where the control is enabled but neither selected nor highlighted.
1415
case normal

Sources/Core/Control/Status/ControlStatus.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88

99
/// The current status of the control: highlighted or not, disabled or not and selected or not.
10+
@available(*, deprecated, message: "Only used on deprecated button. Remove ASAP (13/03/2026")
1011
@_spi(SI_SPI) public final class ControlStatus: Equatable {
1112

1213
// MARK: - Properties

Sources/Core/Control/SwiftUI/ControlStateImage.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import SwiftUI
1010

11+
@available(*, deprecated, message: "Only used on deprecated button. Remove ASAP (13/03/2026")
1112
@_spi(SI_SPI) public final class ControlStateImage: ObservableObject {
1213

1314
// MARK: - Public Published Properties

Sources/Core/Control/SwiftUI/ControlStateText.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import SwiftUI
1010

11+
@available(*, deprecated, message: "Only used on deprecated button. Remove ASAP (13/03/2026")
1112
@_spi(SI_SPI) public final class ControlStateText: ObservableObject {
1213

1314
// MARK: - Public Published Properties

Sources/Core/Control/UIView/UIControlStateImageView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import UIKit
1010

1111
/// The custom UIImageView which set the correct image from the state of the UIControl.
1212
/// Must be used only on UIControl.
13+
@available(*, deprecated, message: "Only used on deprecated button. Remove ASAP (13/03/2026")
1314
@_spi(SI_SPI) public final class UIControlStateImageView: UIImageView {
1415

1516
// MARK: - Properties

0 commit comments

Comments
 (0)