Skip to content

Commit ab2dc3d

Browse files
amordesquare-tomb
andauthored
Add support for adding the view hierarchy description as an attachment (#1330)
* Add support for adding the view hierarchy description as an attachment to the test results * Fix copy-paste error in Sources/KIF/Additions/UIView-Debugging.m Co-authored-by: Tom Brow <106167956+square-tomb@users.noreply.github.com> * Fix copy-paste error in Sources/KIF/Additions/UIView-Debugging.m Co-authored-by: Tom Brow <106167956+square-tomb@users.noreply.github.com> --------- Co-authored-by: Tom Brow <106167956+square-tomb@users.noreply.github.com>
1 parent 1227d0d commit ab2dc3d

3 files changed

Lines changed: 135 additions & 121 deletions

File tree

Sources/KIF/Additions/UIView-Debugging.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,18 @@
1010

1111
@interface UIView (Debugging)
1212
/*!
13-
@abstract Prints the view hiererchy, starting from the top window(s), along with accessibility information, which is more related to KIF than the usual information given by the 'description' method.
13+
@abstract Prints a string representation of the view hierarchy via @code+[UIView viewHierarchyDescription]@endcode
1414
*/
1515
+(void)printViewHierarchy;
1616

1717
/*!
18-
@abstract Prints the view hiererchy, starting from this view, along with accessibility information, which is more related to KIF than the usual information given by the 'description' method.
18+
@abstract Prints a string representation of the view hierarchy via @code+[UIView viewHierarchyDescription]@endcode
1919
*/
2020
-(void)printViewHierarchy;
2121

22+
/*!
23+
@abstract Returns a string representation of the view hierarchy, starting from the top window(s), along with accessibility information, which is more related to KIF than the usual information given by the 'description' method.
24+
*/
25+
+(NSString *)viewHierarchyDescription;
26+
2227
@end

Sources/KIF/Additions/UIView-Debugging.m

Lines changed: 98 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -8,254 +8,237 @@
88
#import "UIView-Debugging.h"
99
#import "KIFEventVisualizer.h"
1010
#import "KIFTouchVisualizerView.h"
11+
#import <Foundation/Foundation.h>
1112

1213
@implementation UIView (Debugging)
1314

1415
+(void)printViewHierarchy {
16+
printf("%s", [self viewHierarchyDescription].UTF8String);
17+
}
18+
19+
+ (NSString *)viewHierarchyDescription {
1520
NSArray* windows = [UIApplication sharedApplication].windows;
21+
NSMutableString *result = [[NSMutableString alloc] init];
1622
if(windows.count == 1) {
17-
[windows[0] printViewHierarchy];
23+
[windows[0] _KIF_appendViewHierarchy:result];
1824
} else {
1925
//more than one window, also print some information about each window
2026
for (UIWindow* window in windows) {
21-
printf("Window level %f", window.windowLevel);
22-
if(window.isKeyWindow) printf(" (key window)");
23-
printf("\n");
24-
[window printViewHierarchy];
25-
printf("\n");
27+
[result appendFormat:@"Window level %@", @(window.windowLevel)];
28+
if(window.isKeyWindow) [result appendString:@" (key window)"];
29+
[result appendString:@"\n"];
30+
[window _KIF_appendViewHierarchy:result];
31+
[result appendString:@"\n"];
2632
}
2733
}
34+
return result;
2835
}
2936

37+
3038
- (void)printViewHierarchy {
31-
[self printViewHierarchyWithIndentation:0];
39+
NSMutableString *result = [NSMutableString new];
40+
[self _KIF_appendViewHierarchy:result];
41+
printf("%s", result.UTF8String);
3242
}
3343

34-
- (void)printViewHierarchyWithIndentation:(int)indent {
35-
44+
- (void)_KIF_appendViewHierarchy:(NSMutableString *)result {
45+
[self _KIF_viewHierarchyWithIndentation:0 result:result];
46+
}
47+
48+
- (void)_KIF_viewHierarchyWithIndentation:(int)indent result:(NSMutableString *)result {
49+
3650
// Don't print the touch visualizer view or it's subviews.
3751
if([self isKindOfClass:[KIFTouchVisualizerView class]]) {
3852
return;
3953
}
4054

41-
[self printIndentation:indent];
42-
[self printClassName];
55+
[self _KIF_appendIndentation:indent result:result];
56+
[self _KIF_appendClassName:result];
4357

44-
[self printAccessibilityInfo];
58+
[self _KIF_appendAccessibilityInfo:result];
4559

4660
if(self.hidden) {
47-
printf(" (invisible)");
61+
[result appendString:@" (invisible)"];
4862
}
4963

5064
if([self isKindOfClass:[UIImageView class]]) {
51-
[self printImageHighlightedState];
65+
[self _KIF_appendImageHighlightedState:result];
5266
}
5367

5468
if([self isKindOfClass:[UIControl class]]) {
55-
[self printControlState];
69+
[self _KIF_appendControlState:result];
5670
}
5771

5872
if([self isKindOfClass:[UIDatePicker class]]) {
59-
[self printDatePickerState];
73+
[self _KIF_appendDatePickerState:result];
6074
}
75+
76+
[result appendString:@"\n"];
77+
78+
[self _KIF_appendAccessibilityElementsWithIndentation:indent result:result];
6179

62-
printf("\n");
63-
64-
[self printAccessibilityElementsWithIndentation:indent];
65-
66-
// We do not want to print the view heirarchy under this class as it is too large and not helpful.
80+
// We do not want to print the view hierarchy under this class as it is too large and not helpful.
6781
if([self isKindOfClass:[NSClassFromString(@"_UIDatePickerView") class]]) {
6882
return;
6983
}
7084

7185
for (UIView *subview in self.subviews) {
72-
[subview printViewHierarchyWithIndentation:indent+1];
86+
[subview _KIF_viewHierarchyWithIndentation:indent+1 result:result];
7387
}
7488
}
7589

76-
- (void)printIndentation:(int)indent {
90+
- (void)_KIF_appendIndentation:(int)indent result:(NSMutableString *)result{
7791
for(int i = 0; i < indent; ++i) {
78-
printf("|\t");
92+
[result appendString:@"|\t"];
7993
}
8094
}
8195

82-
- (void)printClassName {
83-
NSString* name = NSStringFromClass([self class]);
84-
printf("%s", name.UTF8String);
96+
- (void)_KIF_appendClassName:(NSMutableString *)result {
97+
[result appendString:NSStringFromClass([self class])];
8598
}
8699

87-
- (void)printAccessibilityInfo {
100+
- (void)_KIF_appendAccessibilityInfo:(NSMutableString *)result {
88101
NSString* label = self.accessibilityLabel;
89102
NSString* identifier = self.accessibilityIdentifier;
90103
if(label != nil) {
91-
printf(", label: %s", label.UTF8String);
104+
[result appendFormat:@", label: %@", label];
92105
}
93106

94107
if(identifier != nil) {
95-
printf(", identifier: %s", identifier.UTF8String);
108+
[result appendFormat:@", identifier: %@", identifier];
96109
}
97110
}
98111

99-
- (void)printImageHighlightedState {
112+
- (void)_KIF_appendImageHighlightedState:(NSMutableString *)result {
100113
if(((UIImageView*)self).highlighted) {
101-
printf(" (highlighted)");
114+
[result appendString:@" (highlighted)"];
102115
} else {
103-
printf(" (not highlighted)");
116+
[result appendString:@" (not highlighted)"];
104117
}
105118
}
106119

107-
- (void)printControlState {
120+
- (void)_KIF_appendControlState:(NSMutableString *)result {
108121
UIControl* ctrl = (UIControl*)self;
109-
ctrl.enabled ? printf(" (enabled)") : printf(" (not enabled)");
110-
ctrl.selected ? printf(" (selected)") : printf(" (not selected)");
111-
ctrl.highlighted ? printf(" (highlighted)") : printf(" (not highlighted)");
122+
[result appendString:ctrl.enabled ? @" (enabled)" : @" (not enabled)"];
123+
[result appendString:ctrl.selected ? @" (selected)" : @" (not selected)"];
124+
[result appendString:ctrl.highlighted ? @" (highlighted)" : @" (not highlighted)" ];
112125
}
113126

114-
- (void)printDatePickerState {
127+
- (void)_KIF_appendDatePickerState:(NSMutableString *)result {
115128
UIDatePicker *datePicker = (UIDatePicker *)self;
116-
printf(" (date range:");
117-
datePicker.minimumDate ? printf(" %s", datePicker.minimumDate.description.UTF8String) : printf(" no minimum");
118-
printf(" -");
119-
datePicker.maximumDate ? printf(" %s", datePicker.minimumDate.description.UTF8String) : printf(" no maximum");
120-
printf(")");
121-
printf(" (mode:");
129+
[result appendFormat:@" (date range: %@ - %@)",
130+
datePicker.minimumDate ? datePicker.minimumDate.description : @"no minimum",
131+
datePicker.maximumDate ? datePicker. maximumDate.description : @"no maximum"];
132+
133+
134+
[result appendString:@" (mode:"];
122135

123136
switch (datePicker.datePickerMode) {
124137
case UIDatePickerModeTime:
125-
printf(" UIDatePickerModeTime");
138+
[result appendString:@" UIDatePickerModeTime"];
126139
break;
127140

128141
case UIDatePickerModeDate:
129-
printf(" UIDatePickerModeDate");
142+
[result appendString:@" UIDatePickerModeDate"];
130143
break;
131144

132145
case UIDatePickerModeDateAndTime:
133-
printf(" UIDatePickerModeDateAndTime");
146+
[result appendString:@" UIDatePickerModeDateAndTime"];
134147
break;
135148

136149
case UIDatePickerModeCountDownTimer:
137-
printf(" UIDatePickerModeCountDownTimer");
150+
[result appendString:@" UIDatePickerModeCountDownTimer"];
138151
break;
139152
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 170400 //__IPHONE_17_4
140153
case UIDatePickerModeYearAndMonth:
141-
printf(" UIDatePickerModeYearAndMonth");
154+
[result appendString:@" UIDatePickerModeYearAndMonth"];
142155
break;
143156
#endif
144157
}
145-
printf(")");
146-
printf(" (minute interval: %s)", @(datePicker.minuteInterval).stringValue.UTF8String);
158+
[result appendString:@")"];
159+
[result appendFormat:@" (minute interval: %@)", @(datePicker.minuteInterval)];
147160
}
148161

149-
- (void)printAccessibilityElementsWithIndentation:(int)indent {
162+
- (void)_KIF_appendAccessibilityElementsWithIndentation:(int)indent result:(NSMutableString *)result {
150163
NSInteger numOfAccElements = self.accessibilityElementCount;
151164
if(numOfAccElements != NSNotFound) {
152165
for (NSInteger i = 0; i < numOfAccElements; ++i) {
153-
[self printIndentation:indent];
166+
[self _KIF_appendIndentation:indent result:result];
154167
UIAccessibilityElement *e = [(UIAccessibilityElement*)self accessibilityElementAtIndex:i];
155-
printf("%s, label: %s", NSStringFromClass([e class]).UTF8String, e.accessibilityLabel.UTF8String);
168+
[result appendFormat:@"%@, label: %@", NSStringFromClass([e class]), e.accessibilityLabel];
156169
if(e.accessibilityValue && e.accessibilityValue.length > 0) {
157-
printf(", value: %s", e.accessibilityValue.UTF8String);
170+
[result appendFormat:@", value: %@", e.accessibilityValue];
158171
}
159172
if(e.accessibilityHint && e.accessibilityHint.length > 0) {
160-
printf(", hint: %s", e.accessibilityHint.UTF8String);
173+
[result appendFormat:@", hint: %@", e.accessibilityHint];
161174
}
162-
printf(", ");
163-
[self printAccessibilityTraits:e.accessibilityTraits];
164-
printf("\n");
175+
[result appendString:@", "];
176+
[self _KIF_appendAccessibilityTraits:e.accessibilityTraits result:result];
177+
[result appendString:@"\n"];
165178
}
166179
}
167180
}
168181

169-
- (void)printAccessibilityTraits:(UIAccessibilityTraits)traits {
170-
171-
printf("traits: ");
172-
bool didPrintOne = false;
182+
- (void)_KIF_appendAccessibilityTraits:(UIAccessibilityTraits)traits result:(NSMutableString *)result {
183+
184+
[result appendString:@"traits: "];
185+
NSMutableArray<NSString *> *components = [NSMutableArray new];
173186
if(traits == UIAccessibilityTraitNone) {
174-
printf("none");
175-
didPrintOne = true;
187+
[components addObject:@"none"];
176188
}
177189
if(traits & UIAccessibilityTraitButton) {
178-
if(didPrintOne) printf(", ");
179-
printf("button");
180-
didPrintOne = true;
190+
[components addObject:@"button"];
181191
}
182192
if(traits & UIAccessibilityTraitLink) {
183-
if(didPrintOne) printf(", ");
184-
printf("link");
185-
didPrintOne = true;
193+
[components addObject:@"link"];
186194
}
187195
if(traits & UIAccessibilityTraitHeader) {
188-
if(didPrintOne) printf(", ");
189-
printf("header");
190-
didPrintOne = true;
196+
[components addObject:@"header"];
191197
}
192198
if(traits & UIAccessibilityTraitSearchField) {
193-
if(didPrintOne) printf(", ");
194-
printf("search field");
195-
didPrintOne = true;
199+
[components addObject:@"search field"];
196200
}
197201
if(traits & UIAccessibilityTraitImage) {
198-
if(didPrintOne) printf(", ");
199-
printf("image");
200-
didPrintOne = true;
202+
[components addObject:@"image"];
201203
}
202204
if(traits & UIAccessibilityTraitSelected) {
203-
if(didPrintOne) printf(", ");
204-
printf("selected");
205-
didPrintOne = true;
205+
[components addObject:@"selected"];
206206
}
207207
if(traits & UIAccessibilityTraitPlaysSound) {
208-
if(didPrintOne) printf(", ");
209-
printf("plays sound");
210-
didPrintOne = true;
208+
[components addObject:@"plays sound"];
211209
}
212210
if(traits & UIAccessibilityTraitKeyboardKey) {
213-
if(didPrintOne) printf(", ");
214-
printf("keyboard key");
215-
didPrintOne = true;
211+
[components addObject:@"keyboard key"];
216212
}
217213
if(traits & UIAccessibilityTraitStaticText) {
218-
if(didPrintOne) printf(", ");
219-
printf("static text");
220-
didPrintOne = true;
214+
[components addObject:@"static text"];
221215
}
222216
if(traits & UIAccessibilityTraitSummaryElement) {
223-
if(didPrintOne) printf(", ");
224-
printf("summary element");
225-
didPrintOne = true;
217+
[components addObject:@"summary element"];
226218
}
227219
if(traits & UIAccessibilityTraitNotEnabled) {
228-
if(didPrintOne) printf(", ");
229-
printf("not enabled");
230-
didPrintOne = true;
220+
[components addObject:@"not enabled"];
231221
}
232222
if(traits & UIAccessibilityTraitUpdatesFrequently) {
233-
if(didPrintOne) printf(", ");
234-
printf("updates frequently");
235-
didPrintOne = true;
223+
[components addObject:@"updates frequently"];
236224
}
237225
if(traits & UIAccessibilityTraitStartsMediaSession) {
238-
if(didPrintOne) printf(", ");
239-
printf("starts media session");
240-
didPrintOne = true;
226+
[components addObject:@"starts media session"];
241227
}
242228
if(traits & UIAccessibilityTraitAdjustable) {
243-
if(didPrintOne) printf(", ");
244-
printf("adjustable");
245-
didPrintOne = true;
229+
[components addObject:@"adjustable"];
246230
}
247231
if(traits & UIAccessibilityTraitAllowsDirectInteraction) {
248-
if(didPrintOne) printf(", ");
249-
printf("allows direct interaction");
250-
didPrintOne = true;
232+
[components addObject:@"allows direct interaction"];
251233
}
252234
if(traits & UIAccessibilityTraitCausesPageTurn) {
253-
if(didPrintOne) printf(", ");
254-
printf("causes page turn");
255-
didPrintOne = true;
235+
[components addObject:@"causes page turn"];
256236
}
257-
if(!didPrintOne) {
258-
printf("unknown flags (0x%llx)", traits);
237+
238+
if(components.count == 0) {
239+
[result appendFormat:@"unknown flags (0x%llx)", traits];
240+
} else {
241+
[result appendString:[components componentsJoinedByString:@", "]];
259242
}
260243
}
261244

0 commit comments

Comments
 (0)