fastlane-old / snapshot

Automate taking localized screenshots of your iOS app on every device
https://fastlane.tools
1.96k stars 141 forks source link

[UI Testing] Snapshot helper clicks item on tabBar #215

Closed davidrothera closed 8 years ago

davidrothera commented 8 years ago

When using the snapshot helper on an application that has a tabBar on it the lower right object is clicked erroneously.

I have created a simple example application showing this, https://github.com/davidrothera/snapshot_test run snapshot on that application and you will see it clicks into the second view controller.

zineer commented 8 years ago

+1 I just upgraded snapshot and I'm noticing the same thing. I have a Cordova app which has a toolbar along the bottom and the app recognizes the "snapshot swipe" as a press & hold on the bottom right button and it pops up the Safari "open in new tab" interface buttons.

plaetzchen commented 8 years ago

+1 I can't use snapshot to do a screenshot because it will always tap on the settings of my app when snapshot is run. Update: I fixed this by disabling (self.tabbarItem.enabled = NO) the last UITTabbarItem but that will make that item greyed out.

KrauseFx commented 8 years ago

I'm really sorry about this, I couldn't find a better solution. Check out this for more information about why. Also, please duplicate the radar and let me know if you have an idea on how to implement this the right way.

plaetzchen commented 8 years ago

One (probably bad) solution could be to send cmd + s to the simulator and collect the shots from the desktop by the time stamp?

KrauseFx commented 8 years ago

@plaetzchen I tried that, it's only possible using Apple Script, I don't think we should go that road.

KrauseFx commented 8 years ago

It didn't really work

plaetzchen commented 8 years ago

I agree that using AppleScript is the worst solution. Is there any official documentation how one uses the tap methods you use? Maybe the upper right corner would lead to less problems.

swizzlr commented 8 years ago

@KrauseFx would it at least be possible to retrieve the screenshot from before the event was synthesized, instead of after? that way we don't have a weird mid-transition screenshot, and then we just have to write code to restore the state we need to restore.

zineer commented 8 years ago

Additional documentation on the "swipe" being done in func snapshot would be handy as well. Then we could customize where the action is being done so it doesn't interfere with our interface. I tried playing around with different numbers, but it either doesn't make too much of a difference or it causes the screenshot to not be found at the end of snapshot

swizzlr commented 8 years ago

32.1 is a magic number that snapshot looks for. You'd need to inject that dependency if you want to workaround that.

zineer commented 8 years ago

I suspected that might be the case. Maybe we can have an option in the snapfile for magic swipe number so those of us who need to can override that. For example 0.1111, 0 and 0.1, 0 seem to not hit anything in my app, so I could do 0.111 as my magic number to look for.

Edit: Working to keep 32.1 in my code, I've found 32.1, 0 and 32.11, 0 in SnapshotHelper.swift don't trigger the bottom right toolbar button in my app but still generate a screenshot. I'm not sure where the swipe is happening haha, but its not hitting any of the buttons in the corners of my app :)

KrauseFx commented 8 years ago

@swizzlr

@KrauseFx would it at least be possible to retrieve the screenshot from before the event was synthesized, instead of after? that way we don't have a weird mid-transition screenshot, and then we just have to write code to restore the state we need to restore.

interesting, that sounds like a good idea

olavgm commented 8 years ago

@zineer solution works for me (it doesn't tap on the last item of the UITabBar), but I don't get the right screenshots.

Maybe there could be a way to get all the screenshots on a first pass and then we can select the ones we want... snapshot([1, 2, 6, 7, 9, 14]). Obviously not the best solution, but would help for the time being.

antoniocasero commented 8 years ago

Same problem here... I end up disabling the last item...

pedro380085 commented 8 years ago

self.tabBar.items.lastObject.enabled = NO is a working solution for now!

bassrock commented 8 years ago

Disabling the last tab bar item works, but it also causes it to be greyed out in the screenshots.

safx commented 8 years ago

I've found another tricky workaround: add a 1x1 sized view to bottom right corner of the screen:

if let rootView = window?.rootViewController?.view {
    let s = rootView.frame.size
    let view = UIView(frame: CGRectMake(s.width - 1, s.height - 1, 1, 1))
    //view.backgroundColor = UIColor.redColor()
    rootView.addSubview(view)
}
bencollier commented 8 years ago

@safx that's gross but that's for that!-Working for now :game_die:

plaetzchen commented 8 years ago

@safx This is a really good solution. I use this code in my UITabbarController subclass's viewDidLoad :

    if ([[[NSProcessInfo processInfo] arguments] containsObject:@"-ui_testing"]) {
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetWidth(self.view.frame)-1, CGRectGetHeight(self.view.frame)-1, 1, 1)];
        [view setBackgroundColor:[UIColor clearColor]];
        [self.view addSubview:view];
    }
EricKuck commented 8 years ago

@plaetzchen Thanks for the tip. To make it a bit easier to reuse, I put this in my AppDelegate:

- (void)addSnapshotSwipeGuardView {
#ifdef DEBUG
    if ([[[NSProcessInfo processInfo] arguments] containsObject:@"-ui_testing"]) {
        UIView *swipeBlockingView = [UIView new];

        [self.window addSubview:swipeBlockingView];

        // PureLayout code to make building constraints easier. Replace this with normal NSLayoutConstraint code if you don't already use PureLayout in your project.
        [swipeBlockingView autoPinEdgeToSuperviewEdge:ALEdgeRight];
        [swipeBlockingView autoPinEdgeToSuperviewEdge:ALEdgeBottom];
        [swipeBlockingView autoSetDimensionsToSize:CGSizeMake(1, 1)];
    }
#endif
}

Then I just call addSnapshotSwipeGuardView in application:didFinishLaunchingWithOptions:. While I generally try to keep my AppDelegates clean, putting it here ensures that no views will ever be messed with by snapshot, regardless of the type of view or the view controller they're in (I was having issues with UITextViews getting their text highlighted by snapshot).

plaetzchen commented 8 years ago

@EricKuck You are totally right this seems like a better solution but since I already use a subclass of UITabbarController it was the obvious choice for me but AppDelegate is a good place as well.

BTW: Did someone already write an article called "Massive App Delegate" on Medium ;)

andreamazz commented 8 years ago

I hade the same issue, I changed the sign on the Y coordinate here:

        let start = view.coordinateWithNormalizedOffset(CGVectorMake(32.10, -30000))
        let finish = view.coordinateWithNormalizedOffset(CGVectorMake(31, -30000))

Ugly, but it works :grimacing:

cyupa commented 8 years ago

:+1:

jeryRazakarison commented 8 years ago

Sadly, changing the sign make a tap on the top bar right button item.

EricKuck commented 8 years ago

Anything that doesn't involve intercepting the touch is just going to change what the problem is, not fix it. Every other solution just makes the gesture happen somewhere else so that it avoids one specific view, but touches another. The cleanest solution is going to be what I posted above until Apple creates a way to take screenshots without gestures.

MartinRogalla commented 8 years ago

Argh, I would really love to use snapshot so much. I really like it. However right now sadly I can't. The 'snapshot swipe' is affecting my UI. I've tried giving the snapshot function extra parameters so I can change the direction and position of the 'snapshot swipe' for a given screen, but on some views it will always bring me into a state I don't want to be in.

I duplicated the radar for now.

@KrauseFx Do you think it will be possible to combine a screenshot feature from within the App(http://stackoverflow.com/a/29702270)? This of course would then have to be activated somehow from the screenshot test suite.

I really love the work you've done with Fastlane. Thank you for all your hard work!

robertoferraz commented 8 years ago

I tried the workaround from @EricKuck and @andreamazz but didn't work for me. Someone has another idea?

davidrothera commented 8 years ago

I think ultimately unless someone finds an awesome undocumented method to trigger a screenshot without actually interacting with the app we are going to have to wait and see if Apple ever responds to the Radar :-1:

mihahribar commented 8 years ago

How about using the screenshots Xcode already makes while running UI Tests? (In Xcode 7.1 right click on test and select "Jump to report"). The eye icon displays the screenshot made by xcode at that point.

screen shot 2015-11-17 at 21 56 45

ffittschen commented 8 years ago

@mihahribar If I understand this correctly, that is exactly what snapshot does. The gesture is used to "remember" the timestamp and later map the remembered timestamps to the existing screenshots and copy the ones which correlate to the timestamps

mihahribar commented 8 years ago

:+1:

ffittschen commented 8 years ago

In case anyone of you is using Swift, the "translated" version of this answer works for me:

class AppDelegate: UIResponder, UIApplicationDelegate {
    //...
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        #if DEBUG
            addSnapshotSwipeGuardView()
        #endif

        return true
    }

    //...

    private func addSnapshotSwipeGuardView() {
        #if DEBUG
            if NSProcessInfo.processInfo().arguments.contains("-ui_testing") {
                let swipeBlockView = UIView()
                swipeBlockView.translatesAutoresizingMaskIntoConstraints = false

                window?.rootViewController?.view.addSubview(swipeBlockView)

                let horizontalConstraint = NSLayoutConstraint(item: swipeBlockView, attribute: NSLayoutAttribute.Right, relatedBy: NSLayoutRelation.Equal, toItem: window?.rootViewController?.view, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 0)
                window?.rootViewController?.view.addConstraint(horizontalConstraint)

                let verticalConstraint = NSLayoutConstraint(item: swipeBlockView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: window?.rootViewController?.view, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
                window?.rootViewController?.view.addConstraint(verticalConstraint)

                let widthConstraint = NSLayoutConstraint(item: swipeBlockView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 1)
                window?.rootViewController?.view.addConstraint(widthConstraint)

                let heightConstraint = NSLayoutConstraint(item: swipeBlockView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 1)
                window?.rootViewController?.view.addConstraint(heightConstraint)

            }
        #endif
    }
}

Don't forget to set the DEBUG flag! screen shot 2015-11-17 at 22 46 09

swizzlr commented 8 years ago

This is what I ended up doing. No changes to test target necessary: http://codehardstare.swizzlr.co/post/132335036254/a-new-approach-to-automating-screenshots

ConfusedVorlon commented 8 years ago

I haven't got anywhere with this - but thought I'd post something to see if anyone can take it further.

XCUIElement has a private method

_dispatchEvent:block: of encoding 'v32@0:8@16@?24'

I have tried sending events to that, and it seems to require that events

1) implement copyWithZone: 2) implement utf8String

Which my events don't. Perhaps someone else can make something useful of this.

KrauseFx commented 8 years ago

I just pushed a new release https://github.com/fastlane/snapshot/releases/tag/1.2.0 Please update to the latest version, make sure to read the release notes and let me know if it works for you :+1:

fastlanebot commented 8 years ago

This issue was migrated to https://github.com/fastlane/fastlane/issues/2597. Please post all further comments there.

fastlane is now a mono repo, you can read more about the change in our blog post. All tools are now available in the fastlane main repo :rocket: