├── .gitignore ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── Snapshots.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Snapshots.xcworkspace └── contents.xcworkspacedata ├── Snapshots ├── ARLabel.h ├── ARLabel.m ├── App.swift ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── BridgingHeader.h ├── DeveloperDirWatcher.swift ├── Grepper.swift ├── Info.plist ├── Log.swift ├── LogSnapshotsPreviewViewController.swift ├── NSColor+ORSnapshotColours.h ├── NSColor+ORSnapshotColours.m ├── NSFileManager+RecursiveFind.h ├── NSFileManager+RecursiveFind.m ├── NSString+StringBetweenStrings.h ├── NSString+StringBetweenStrings.m ├── ORKaleidoscopeController.h ├── ORKaleidoscopeController.m ├── ORLogReader.h ├── ORLogReader.m ├── ORSlidingImageView.h ├── ORSlidingImageView.m ├── ORTestsSuiteModels.h ├── ORTestsSuiteModels.m ├── SnapshotsViewController.swift └── ViewController.swift ├── SnapshotsTests ├── DeveloperDirWatcherSpecs.swift ├── Info.plist ├── LogReaderSpecs.swift ├── MultipleSnapshotsErrors.log ├── NewSnapshotsRecorded.log ├── QuickJustNewSnapshot.log └── QuickNewSnapshot.log └── design ├── Browse.png ├── Run.png └── snapshots-app-design.sketch /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/ 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | <<<<<<< HEAD 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | DerivedData 17 | .idea/ 18 | *.xcscmblueprint 19 | Pods 20 | .pt 21 | .build 22 | tmtags 23 | tmtagsHistory 24 | config/releasenotes.txt 25 | .bundle 26 | Podfile.local 27 | chairs/ 28 | .github 29 | 30 | Snapshots.xcodeproj/project.xcworkspace/xcshareddata/ 31 | 32 | # Artsy Development APN certificate 33 | net.artsy.artsy.dev.pem 34 | .clang-format 35 | 36 | # Fastlane 37 | Preview.html 38 | fastlane/report.xml 39 | fastlane/Preview.html 40 | 41 | iOSInjectionProject/ 42 | 43 | xcuserdata 44 | *.xccheckout 45 | *.moved-aside 46 | DerivedData 47 | *.hmap 48 | *.ipa 49 | *.xcuserstate 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 56 | # 57 | # Pods/ 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Orta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.11' 2 | use_frameworks! 3 | 4 | target 'Snapshots' do 5 | pod 'Interstellar' 6 | pod 'FileKit' 7 | pod 'AsyncSwift' 8 | pod 'PXSourceList' 9 | 10 | target 'SnapshotsTests' do 11 | inherit! :search_paths 12 | 13 | pod 'Quick' 14 | pod 'Nimble' 15 | end 16 | end -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AsyncSwift (1.7.2) 3 | - FileKit (2.0.0) 4 | - Interstellar (1.4.0): 5 | - Interstellar/Core (= 1.4.0) 6 | - Interstellar/Warpdrive (= 1.4.0) 7 | - Interstellar/Core (1.4.0) 8 | - Interstellar/Warpdrive (1.4.0): 9 | - Interstellar/Core 10 | - Nimble (4.0.0) 11 | - PXSourceList (2.0.7) 12 | - Quick (0.9.1) 13 | 14 | DEPENDENCIES: 15 | - AsyncSwift 16 | - FileKit 17 | - Interstellar 18 | - Nimble 19 | - PXSourceList 20 | - Quick 21 | 22 | SPEC CHECKSUMS: 23 | AsyncSwift: 46a78bceb11f0e5bb52747f740d9692ec78d2d7c 24 | FileKit: fd360f82f62a1f70dd2ccdbe0950d3a6d1e25d1d 25 | Interstellar: 40fc666872436d6c1b3524c59437ebdb5dec1c6a 26 | Nimble: 72bcc3e2f02242e6bfaaf8d9412ca7bfe3d8b417 27 | PXSourceList: 8e37978a5db6c8d8d255f8913603bcea322c6445 28 | Quick: a5221fc21788b6aeda934805e68b061839bc3165 29 | 30 | PODFILE CHECKSUM: a9b50005461c9a49d1dca176a558f21fd8b28de5 31 | 32 | COCOAPODS: 1.0.0 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Snapshots App for Xcode 2 | 3 | Ignore this, see https://github.com/Antondomashnev/FBSnapshotsViewer 4 | 5 | --- 6 | 7 | ### An App to show the state of FBSnapshot Tests. 8 | 9 | I think Facebook have [view testing](https://github.com/facebook/ios-snapshot-test-case) right. So I build 10 | an Xcode plugin, then I decided maybe the plugin isn't the right approach, so I started an app. 11 | 12 | #### Main Features 13 | 14 | * Showing new images as they're created 15 | * Showing the differences between failed snapshot tests 16 | * Open specific diffs in Kaleidoscope 17 | * Turns red if you've created a view with a zero frame 18 | 19 | #### Installation 20 | 21 | Will provide a zip. 22 | 23 | #### Contributing 24 | 25 | It's all UI work, I'm taking a lot of the log parsing from the Xcode plugin. 26 | -------------------------------------------------------------------------------- /Snapshots.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Snapshots.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Snapshots/ARLabel.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | /// These labels don't get mangled by transparency 4 | 5 | @interface ARLabel : NSTextField 6 | @end 7 | -------------------------------------------------------------------------------- /Snapshots/ARLabel.m: -------------------------------------------------------------------------------- 1 | #import "ARLabel.h" 2 | 3 | @interface ARLabelCell : NSTextFieldCell 4 | 5 | @end 6 | 7 | 8 | @implementation ARLabelCell 9 | 10 | 11 | @end 12 | 13 | @implementation ARLabel 14 | 15 | - (void)awakeFromNib 16 | { 17 | [super awakeFromNib]; 18 | [self setup]; 19 | } 20 | 21 | - (instancetype)initWithFrame:(NSRect)frameRect 22 | { 23 | self = [super initWithFrame:frameRect]; 24 | if (!self) return nil; 25 | 26 | [self setup]; 27 | return self; 28 | } 29 | 30 | - (BOOL)allowsVibrancy 31 | { 32 | return NO; 33 | } 34 | 35 | - (void)setup 36 | { 37 | self.bezeled = NO; 38 | self.editable = NO; 39 | self.drawsBackground = NO; 40 | } 41 | 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Snapshots/App.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FileKit 3 | import Async 4 | 5 | class App: NSObject, NSCopying { 6 | let name: String 7 | let lastUpdated: NSDate 8 | 9 | let reader = ORLogReader() 10 | var logs = [Log]() 11 | 12 | var prettyName: String { 13 | return name.componentsSeparatedByString("-").first ?? "-" 14 | } 15 | 16 | init(name: String, date: NSDate) { 17 | self.name = name 18 | self.lastUpdated = date 19 | } 20 | 21 | func copyWithZone(zone: NSZone) -> AnyObject { 22 | let app = App(name: self.name, date: self.lastUpdated) 23 | app.logs = self.logs 24 | return app 25 | } 26 | 27 | func readLogs() { 28 | Async.background { 29 | for log in self.logs { 30 | // look into http://blog.krzyzanowskim.com/2015/01/10/nsscanner-for-raw-data-and-files/ ? 31 | 32 | guard let content = try? NSString(contentsOfURL: log.path.URL, encoding: NSUTF8StringEncoding) as String else { continue } 33 | if log.path.contains("SOKKWK") { 34 | print("OK") 35 | } 36 | 37 | self.reader.readLog(content) 38 | log.updateWithReader(self.reader) 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Snapshots/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Interstellar 2 | import Cocoa 3 | import FileKit 4 | 5 | @NSApplicationMain 6 | class AppDelegate: NSObject, NSApplicationDelegate { 7 | 8 | func applicationDidFinishLaunching(aNotification: NSNotification) { 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /Snapshots/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Snapshots/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | Default 518 | 519 | 520 | 521 | 522 | 523 | 524 | Left to Right 525 | 526 | 527 | 528 | 529 | 530 | 531 | Right to Left 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | Default 543 | 544 | 545 | 546 | 547 | 548 | 549 | Left to Right 550 | 551 | 552 | 553 | 554 | 555 | 556 | Right to Left 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 775 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | %{value1}@ 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 850 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 994 | 1001 | 1008 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | -------------------------------------------------------------------------------- /Snapshots/BridgingHeader.h: -------------------------------------------------------------------------------- 1 | #import "ORLogReader.h" 2 | 3 | //#import "libgrep.h" 4 | //#import "regex.h" -------------------------------------------------------------------------------- /Snapshots/DeveloperDirWatcher.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FileKit 3 | import Interstellar 4 | import Async 5 | 6 | class DeveloperDirWatcher: NSObject { 7 | var fileManager: NSFileManager = NSFileManager.defaultManager() 8 | var library = Path.UserLibrary 9 | var derivedDataDir = "Developer/Xcode/DerivedData" 10 | 11 | let appNamesUpdateSignal = Signal<[App]>() 12 | let appsWithMetadataUpdatedSignal = Signal<[App]>() 13 | 14 | let appUpdatedSignal = Signal() 15 | 16 | func startParsing() { 17 | Async.background { 18 | let apps = self.getAllAppNamesWithTests() 19 | let sorted = apps.sort { $0.lastUpdated.compare($1.lastUpdated) == .OrderedDescending } 20 | 21 | // Optional, note used though 22 | Async.main { self.appNamesUpdateSignal.update(sorted) } 23 | 24 | for app in apps { 25 | self.getLogsForApp(app.name).next { 26 | app.logs = $0.map { Log(path: $0) } 27 | Async.main { self.appsWithMetadataUpdatedSignal.update(sorted) } 28 | } 29 | } 30 | 31 | Async.main { self.appsWithMetadataUpdatedSignal.update(sorted) } 32 | } 33 | } 34 | 35 | func getAllAppNamesWithTests() -> [App] { 36 | Path.fileManager = fileManager 37 | 38 | let derived = library + derivedDataDir 39 | let paths = derived.find(searchDepth: 3) { path in 40 | return path.rawValue.hasSuffix("/Logs/Test") 41 | } 42 | 43 | // Looks like: 44 | // "/Users/orta/Library/Developer/Xcode/DerivedData/Aerodramus-elioeeoyxfebivbqkcrplnueiqkk/Logs/Test" 45 | 46 | return paths.flatMap { path in 47 | let date = path.modificationDate ?? NSDate.distantPast() 48 | return App(name: path.parent.parent.fileName, date: date) 49 | } 50 | } 51 | 52 | func getLogsForApp(name: String) -> Signal<[Path]> { 53 | let appLogs = self.library + self.derivedDataDir + name + "Logs" + "Test" 54 | let queries = ["replace recordSnapshot with a check", "expected a matching snapshot"] 55 | return Grepper.getPathsMatchingPattern(queries, fromPath: appLogs) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Snapshots/Grepper.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import Interstellar 3 | import FileKit 4 | import Async 5 | 6 | class Grepper: NSObject { 7 | 8 | class func getPathsMatchingPattern(patterns:[String], fromPath: Path) -> Signal<[Path]> { 9 | let signal = Signal<[Path]>() 10 | 11 | Async.background { 12 | let task:NSTask = NSTask() 13 | let pipe:NSPipe = NSPipe() 14 | 15 | task.launchPath = "/usr/bin/grep" 16 | var arguments = ["-lr"] 17 | for pattern in patterns { arguments.append("-e"); arguments.append(pattern) } 18 | arguments.append(fromPath.rawValue) 19 | 20 | // print("grep ", arguments.joinWithSeparator(" ")) 21 | 22 | task.arguments = arguments 23 | task.standardOutput = pipe 24 | task.launch() 25 | 26 | let handle = pipe.fileHandleForReading 27 | let data = handle.readDataToEndOfFile() 28 | 29 | guard let pathString = NSString(data: data, encoding: NSUTF8StringEncoding) else { 30 | Async.main { signal.update([]) } 31 | return 32 | } 33 | 34 | let pathStrings = pathString.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) 35 | 36 | guard pathString.length > 0 else { 37 | Async.main { signal.update([]) } 38 | return 39 | } 40 | 41 | print("Found useful test logs:", pathStrings) 42 | Async.main { signal.update(pathStrings.map { Path($0) }) } 43 | } 44 | 45 | return signal 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Snapshots/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2016 Orta. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Snapshots/Log.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FileKit 3 | 4 | class Log: NSObject, NSCopying { 5 | let path: Path 6 | var parsed: Bool = false 7 | 8 | var valid: Bool { 9 | return hasSnapshotErrors || hasNewSnapshots 10 | } 11 | 12 | var hasSnapshotErrors: Bool { 13 | return snapshotErrors.isEmpty == false 14 | } 15 | 16 | var hasNewSnapshots: Bool { 17 | return newSnapshots.isEmpty == false 18 | } 19 | 20 | var newSnapshots = [ORSnapshotCreationReference]() 21 | var snapshotErrors = [ORKaleidoscopeCommand]() 22 | 23 | var title: String { 24 | var message = "" 25 | if hasSnapshotErrors { 26 | message += "Recorded new Snapshots" 27 | } 28 | if hasNewSnapshots { 29 | if message.isEmpty == false { message += ", " } 30 | message += "Got Snapshots Errors" 31 | } 32 | return message 33 | } 34 | 35 | var name: String { 36 | return path.fileName 37 | } 38 | 39 | init(path: Path) { 40 | self.path = path 41 | } 42 | 43 | func copyWithZone(zone: NSZone) -> AnyObject { 44 | let log = Log(path: self.path) 45 | log.parsed = parsed 46 | log.newSnapshots = newSnapshots 47 | log.snapshotErrors = snapshotErrors 48 | return log 49 | } 50 | 51 | func updateWithReader(reader: ORLogReader) { 52 | parsed = true 53 | snapshotErrors = reader.uniqueDiffCommands() 54 | newSnapshots = reader.newSnapshots() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Snapshots/LogSnapshotsPreviewViewController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import Quartz 3 | import Interstellar 4 | 5 | class LogSnapshotsPreviewViewController: NSViewController { 6 | 7 | @IBOutlet weak var imageBrowser: IKImageBrowserView! 8 | @IBOutlet weak var titleTextField: NSTextField! 9 | 10 | let newLogSelected = Signal() 11 | private var log: Log? 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | newLogSelected.next { log in 17 | self.log = log 18 | self.imageBrowser.reloadData() 19 | self.titleTextField.stringValue = log.title 20 | print("OK \(log.valid) errors") 21 | 22 | } 23 | } 24 | 25 | override func imageBrowserSelectionDidChange(aBrowser: IKImageBrowserView!) { 26 | 27 | } 28 | 29 | override func imageBrowser(aBrowser: IKImageBrowserView!, itemAtIndex index: Int) -> AnyObject! { 30 | return "" 31 | } 32 | 33 | override func numberOfItemsInImageBrowser(aBrowser: IKImageBrowserView!) -> Int { 34 | guard let log = log else { return 0 } 35 | return log.snapshotErrors.count + log.newSnapshots.count 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Snapshots/NSColor+ORSnapshotColours.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSColor+ORSnapshotColours.h 3 | // SnapshotDiffs 4 | // 5 | // Created by Orta on 6/15/14. 6 | // Copyright (c) 2014 Orta. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSColor (ORSnapshotColours) 12 | 13 | + (NSColor *)or_orangeColour; 14 | + (NSColor *)or_blueColour; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Snapshots/NSColor+ORSnapshotColours.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSColor+ORSnapshotColours.m 3 | // SnapshotDiffs 4 | // 5 | // Created by Orta on 6/15/14. 6 | // Copyright (c) 2014 Orta. All rights reserved. 7 | // 8 | 9 | #import "NSColor+ORSnapshotColours.h" 10 | 11 | @implementation NSColor (ORSnapshotColours) 12 | 13 | + (NSColor *)or_orangeColour 14 | { 15 | return [NSColor colorWithCalibratedRed:0.842 green:0.495 blue:0.202 alpha:1.000]; 16 | } 17 | 18 | + (NSColor *)or_blueColour 19 | { 20 | return [NSColor colorWithCalibratedRed:0.556 green:0.605 blue:0.802 alpha:1.000]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Snapshots/NSFileManager+RecursiveFind.h: -------------------------------------------------------------------------------- 1 | @interface NSFileManager (ORRecursiveFind) 2 | 3 | - (NSString *)or_findFileWithNamePrefix:(NSString *)name inFolder:(NSString *)folder; 4 | 5 | @end -------------------------------------------------------------------------------- /Snapshots/NSFileManager+RecursiveFind.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @implementation NSFileManager (ORRecursiveFind) 5 | 6 | - (NSString *)or_findFileWithNamePrefix:(NSString *)name inFolder:(NSString *)folder 7 | { 8 | @try { 9 | // Sure glad I built OROpenInAppCode already :) 10 | 11 | NSDocument *document = [NSApp orderedDocuments].firstObject; 12 | NSURL *workspaceDirectoryURL = [[[document valueForKeyPath:@"_workspace.representingFilePath.fileURL"] URLByDeletingLastPathComponent] filePathURL]; 13 | 14 | NSFileManager *fileManager = [NSFileManager defaultManager]; 15 | NSDirectoryEnumerator *enumerator = nil; 16 | enumerator = [fileManager enumeratorAtURL:workspaceDirectoryURL 17 | includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey] 18 | options:NSDirectoryEnumerationSkipsHiddenFiles 19 | errorHandler:^BOOL(NSURL *url, NSError *error) { 20 | NSLog(@"[Error] %@ (%@)", error, url); 21 | return YES; 22 | }]; 23 | 24 | NSMutableArray *mutableFileURLs = [NSMutableArray array]; 25 | for (NSURL *fileURL in enumerator) { 26 | 27 | NSString *filename; 28 | [fileURL getResourceValue:&filename forKey:NSURLNameKey error:nil]; 29 | 30 | NSNumber *isDirectory; 31 | [fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil]; 32 | 33 | // Skip directories with '_' prefix, for example 34 | if ([filename hasPrefix:@"_"] && [isDirectory boolValue]) { 35 | [enumerator skipDescendants]; 36 | continue; 37 | } 38 | 39 | if (![isDirectory boolValue]) { 40 | [mutableFileURLs addObject:fileURL]; 41 | } 42 | 43 | if ([filename hasPrefix:name] && [fileURL.absoluteString rangeOfString:folder].location != NSNotFound) { 44 | return fileURL.absoluteString; 45 | } 46 | } 47 | } 48 | @catch (NSException *exception) { 49 | return nil; 50 | } 51 | 52 | return nil; 53 | } 54 | 55 | @end -------------------------------------------------------------------------------- /Snapshots/NSString+StringBetweenStrings.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NSString (StringBetweenStrings) 4 | 5 | // on a string "hello world" with arguments "e" and "d" 6 | // will return "llo world" or nil if it can't find the start or end. 7 | 8 | - (NSString *)or_substringBetween:(NSString *)start and:(NSString *)end; 9 | @end 10 | -------------------------------------------------------------------------------- /Snapshots/NSString+StringBetweenStrings.m: -------------------------------------------------------------------------------- 1 | #import "NSString+StringBetweenStrings.h" 2 | 3 | @implementation NSString (StringBetweenStrings) 4 | 5 | - (NSString *)or_substringBetween:(NSString *)start and:(NSString *)end { 6 | NSRange startingRange = [self rangeOfString:start]; 7 | NSRange endingRange = [self rangeOfString:end]; 8 | 9 | if (startingRange.location == NSNotFound || endingRange.location == NSNotFound) { 10 | return nil; 11 | } 12 | 13 | NSUInteger length = endingRange.location - startingRange.location - startingRange.length; 14 | NSUInteger location = startingRange.location + startingRange.length; 15 | 16 | if (length + location > self.length) { 17 | return nil; 18 | } 19 | 20 | return [self substringWithRange:NSMakeRange(location, length)]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Snapshots/ORKaleidoscopeController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ORKaleidoscopeController : NSObject 4 | 5 | + (BOOL)isInstalled; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /Snapshots/ORKaleidoscopeController.m: -------------------------------------------------------------------------------- 1 | #import "ORKaleidoscopeController.h" 2 | #import 3 | 4 | @implementation ORKaleidoscopeController 5 | 6 | + (BOOL)isInstalled 7 | { 8 | // If no application could be found, 9 | // * NULL is returned and outError (if not NULL) is populated with kLSApplicationNotFoundErr. 10 | 11 | CFErrorRef error = NULL; 12 | CFArrayRef result = LSCopyApplicationURLsForBundleIdentifier (CFSTR("com.blackpixel.kaleidoscope"), &error ); 13 | if (result) { CFRelease(result); } 14 | return error != nil; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Snapshots/ORLogReader.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "ORTestsSuiteModels.h" 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | @interface ORLogReader : NSObject 7 | 8 | - (void)readLog:(NSString *)log; 9 | 10 | - (NSArray *)uniqueDiffCommands; 11 | - (NSArray *)ksdiffCommands; 12 | - (NSArray *)newSnapshots; 13 | 14 | - (NSArray *)testSuites; 15 | 16 | - (BOOL)hasNewSnapshots; 17 | - (BOOL)hasCGErrors; 18 | - (BOOL)hasSnapshotTestErrors; 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /Snapshots/ORLogReader.m: -------------------------------------------------------------------------------- 1 | #import "ORLogReader.h" 2 | #import "NSFileManager+RecursiveFind.h" 3 | #import "NSString+StringBetweenStrings.h" 4 | 5 | @interface ORLogReader() 6 | @property (nonatomic, readwrite, assign) BOOL hasCGErrors; 7 | @property (nonatomic, readonly, strong) NSMutableOrderedSet *mutableTestSuites; 8 | @property (nonatomic, readonly, strong) NSMutableOrderedSet *mutableDiffCommands; 9 | @property (nonatomic, readonly, strong) NSMutableOrderedSet *mutableSnapshotCreations; 10 | @end 11 | 12 | @implementation ORLogReader 13 | 14 | - (instancetype)init 15 | { 16 | self = [super init]; 17 | if (!self) return nil; 18 | 19 | _mutableDiffCommands = [NSMutableOrderedSet orderedSet]; 20 | _mutableTestSuites = [NSMutableOrderedSet orderedSet]; 21 | _mutableSnapshotCreations = [NSMutableOrderedSet orderedSet]; 22 | _hasCGErrors = NO; 23 | 24 | return self; 25 | } 26 | 27 | - (NSArray *)testSuites 28 | { 29 | return self.mutableTestSuites.array; 30 | } 31 | 32 | 33 | - (ORTestSuite *)latestTestSuite 34 | { 35 | return self.mutableTestSuites.lastObject; 36 | } 37 | 38 | 39 | - (void)readLog:(NSString *)log 40 | { 41 | for (NSString *line in [log componentsSeparatedByCharactersInSet:NSCharacterSet.newlineCharacterSet]) { 42 | if ([line rangeOfString:@"Test Suite"].location != NSNotFound) { 43 | ORTestSuite *suite = [ORTestSuite suiteFromString:line]; 44 | if (suite) [self.mutableTestSuites addObject:suite]; 45 | } 46 | 47 | if ([line rangeOfString:@"Test Case"].location != NSNotFound) { 48 | if ([line rangeOfString:@"started."].location != NSNotFound) { 49 | ORTestCase *testCase = [ORTestCase caseFromString:line]; 50 | if (testCase) [self.latestTestSuite.testCases addObject:testCase]; 51 | 52 | } else if ([line rangeOfString:@"' failed ("].location != NSNotFound) { 53 | for (ORKaleidoscopeCommand *command in self.mutableDiffCommands) { 54 | [command setFails:YES]; 55 | } 56 | } 57 | } 58 | 59 | if ([line rangeOfString:@"ksdiff"].location != NSNotFound) { 60 | NSString *commandString = [self extractCommandFromLine:line]; 61 | ORKaleidoscopeCommand *command = [ORKaleidoscopeCommand commandFromString:commandString]; 62 | if (command) { 63 | [self.mutableDiffCommands addObject:command]; 64 | [self.latestTestSuite.latestTestCase addCommand:command]; 65 | } 66 | } 67 | 68 | if ([line rangeOfString:@"This application, or a library it uses, is using an invalid context"].location != NSNotFound) { 69 | _hasCGErrors = YES; 70 | } 71 | 72 | if ([line rangeOfString:@"]"].location != NSNotFound && [line rangeOfString:@"expected a matching snapshot"].location != NSNotFound) { 73 | NSString *filepathAndLine = [line or_substringBetween:@"] " and:@" expected a matching snapshot"]; 74 | if (filepathAndLine && filepathAndLine.length > 1) { 75 | [self.latestTestSuite.latestTestCase.commands makeObjectsPerformSelector:@selector(setProjectLocation:) withObject:filepathAndLine]; 76 | } 77 | } 78 | 79 | if ([line rangeOfString:@"successfully recorded"].location != NSNotFound) { 80 | ORSnapshotCreationReference *snapshot = [ORSnapshotCreationReference referenceFromString:line]; 81 | 82 | if (snapshot) { 83 | ORTestCase *testCase = self.latestTestSuite.latestTestCase; 84 | NSString *path = [[NSFileManager defaultManager] or_findFileWithNamePrefix:snapshot.name inFolder:self.latestTestSuite.name]; 85 | path = [path stringByReplacingOccurrencesOfString:@"file://" withString:@""]; 86 | snapshot.path = [path stringByReplacingOccurrencesOfString:@"%20" withString:@" "]; 87 | 88 | if (![self.mutableSnapshotCreations containsObject:snapshot]) { 89 | [self.mutableSnapshotCreations addObject:snapshot]; 90 | [testCase addSnapshot:snapshot]; 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | - (NSString *)extractCommandFromLine:(NSString *)line 98 | { 99 | return [line componentsSeparatedByString:@"diff:\n"].lastObject; 100 | } 101 | 102 | - (BOOL)hasNewSnapshots 103 | { 104 | return (self.mutableSnapshotCreations.count > 0); 105 | } 106 | 107 | - (BOOL)hasSnapshotTestErrors 108 | { 109 | return (self.uniqueDiffCommands.count > 0); 110 | } 111 | 112 | - (NSArray *)ksdiffCommands 113 | { 114 | return [self.mutableDiffCommands.array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(ORKaleidoscopeCommand *command, NSDictionary *bindings) { 115 | return command.fails; 116 | }]]; 117 | } 118 | 119 | - (NSArray *)uniqueDiffCommands 120 | { 121 | NSArray *commands = [self ksdiffCommands]; 122 | if (commands.count == 1) return commands; 123 | 124 | return [commands filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(ORKaleidoscopeCommand *command, NSDictionary *bindings) { 125 | return [[NSFileManager defaultManager] contentsEqualAtPath:command.afterPath andPath:command.beforePath] == NO; 126 | }]]; 127 | } 128 | 129 | - (NSArray *)newSnapshots 130 | { 131 | return _mutableSnapshotCreations.array; 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /Snapshots/ORSlidingImageView.h: -------------------------------------------------------------------------------- 1 | @import Cocoa; 2 | 3 | @interface ORSlidingImageView : NSView 4 | 5 | @property (nonatomic, strong) NSImage *frontImage; 6 | @property (nonatomic, strong) NSImage *backImage; 7 | 8 | @property (readwrite, nonatomic, strong) NSString *frontMessage; 9 | @property (readwrite, nonatomic, strong) NSString *backMessage; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /Snapshots/ORSlidingImageView.m: -------------------------------------------------------------------------------- 1 | @import Quartz; 2 | 3 | #import "ORSlidingImageView.h" 4 | #import "NSColor+ORSnapshotColours.h" 5 | #import "ARLabel.h" 6 | 7 | static CGFloat ORContentInset = 8; 8 | 9 | @interface ORSlidingImageView() 10 | @property (nonatomic, weak) NSImageView *frontImageView; 11 | @property (nonatomic, weak) NSImageView *backImageView; 12 | 13 | @property (nonatomic, strong) NSTextField *frontViewLabel; 14 | @property (nonatomic, strong) NSTextField *backViewLabel; 15 | 16 | @property (nonatomic, strong) NSTrackingArea *trackingArea; 17 | @property (nonatomic, weak) NSView *divider; 18 | @end 19 | 20 | @implementation ORSlidingImageView 21 | 22 | - (void)setFrontImage:(NSImage *)frontImage 23 | { 24 | _frontImage = frontImage; 25 | 26 | if (!self.frontImageView) { 27 | NSImageView *frontImageView = [self createImageView]; 28 | [self addSubview:frontImageView]; 29 | 30 | self.frontImageView = frontImageView; 31 | self.frontImageView.layer.borderColor = [NSColor or_blueColour].CGColor; 32 | } 33 | 34 | self.frontImageView.image = frontImage; 35 | [self moveLabelsToFront]; 36 | } 37 | 38 | - (void)setBackImage:(NSImage *)backImage 39 | { 40 | _backImage = backImage; 41 | if (!self.backImageView) { 42 | NSImageView *backImageView = [self createImageView]; 43 | 44 | NSView *front = self.frontImageView; 45 | [self.frontImageView removeFromSuperview]; 46 | [self addSubview:backImageView]; 47 | [self addSubview:front]; 48 | 49 | backImageView.layer.borderColor = [NSColor or_orangeColour].CGColor; 50 | self.backImageView = backImageView; 51 | 52 | [self createDivider]; 53 | } 54 | 55 | self.backImageView.image = backImage; 56 | [self maskViewsWithX:CGRectGetMidX(self.bounds)]; 57 | [self moveLabelsToFront]; 58 | } 59 | 60 | - (void)setBackMessage:(NSString *)backMessage 61 | { 62 | if (!self.backViewLabel) { 63 | NSTextField *textViewLabel = [[ARLabel alloc] initWithFrame:[self frameForTheBottomOfImageView:self.backImageView]]; 64 | textViewLabel.drawsBackground = YES; 65 | textViewLabel.backgroundColor = [NSColor colorWithRed:1 green:1 blue:1 alpha:0.5]; 66 | 67 | self.backViewLabel = textViewLabel; 68 | [self.backImageView addSubview:textViewLabel]; 69 | } 70 | 71 | self.backViewLabel.stringValue = backMessage; 72 | [self moveLabelsToFront]; 73 | } 74 | 75 | - (void)setFrontMessage:(NSString *)frontMessage 76 | { 77 | if (!self.frontViewLabel) { 78 | NSTextField *textViewLabel = [[ARLabel alloc] initWithFrame:[self frameForTheBottomOfImageView:self.frontImageView]]; 79 | textViewLabel.drawsBackground = YES; 80 | textViewLabel.backgroundColor = [NSColor colorWithRed:1 green:1 blue:1 alpha:0.5]; 81 | 82 | self.frontViewLabel = textViewLabel; 83 | 84 | [self.frontImageView addSubview:textViewLabel]; 85 | } 86 | 87 | self.frontViewLabel.stringValue = frontMessage; 88 | [self moveLabelsToFront]; 89 | } 90 | 91 | - (void)moveLabelsToFront 92 | { 93 | [self.frontViewLabel removeFromSuperview]; 94 | [self.frontImageView addSubview:self.frontViewLabel positioned:NSWindowAbove relativeTo:nil]; 95 | 96 | [self.backViewLabel removeFromSuperview]; 97 | [self.backImageView addSubview:self.backViewLabel positioned:NSWindowAbove relativeTo:nil]; 98 | } 99 | 100 | - (CGRect)frameForTheBottomOfImageView:(NSImageView *)imageView 101 | { 102 | return CGRectMake(6, 0, 64, 20); 103 | } 104 | 105 | - (BOOL) wantsDefaultClipping 106 | { 107 | return NO; 108 | } 109 | 110 | - (void)createDivider 111 | { 112 | if (self.divider) return; 113 | 114 | NSView *divider = [[NSView alloc] initWithFrame:self.boundsForDivider]; 115 | divider.wantsLayer = YES; 116 | divider.layer.backgroundColor = [NSColor blackColor].CGColor; 117 | [self addSubview:divider]; 118 | self.divider = divider; 119 | } 120 | 121 | - (NSImageView *)createImageView 122 | { 123 | CGRect frame = CGRectInset(self.bounds, 0, ORContentInset); 124 | NSImageView *imageView = [[NSImageView alloc] initWithFrame:frame]; 125 | imageView.imageFrameStyle = NSImageFrameNone; 126 | imageView.wantsLayer = YES; 127 | imageView.layer.masksToBounds = YES; 128 | imageView.layer.borderWidth = 2; 129 | return imageView; 130 | } 131 | 132 | - (void)ensureTrackingArea 133 | { 134 | if (self.trackingArea == nil) { 135 | _trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingInVisibleRect | NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; 136 | } 137 | } 138 | 139 | - (void)updateTrackingAreas 140 | { 141 | [super updateTrackingAreas]; 142 | [self ensureTrackingArea]; 143 | if (![[self trackingAreas] containsObject:self.trackingArea]) { 144 | [self addTrackingArea:self.trackingArea]; 145 | } 146 | } 147 | 148 | - (void)mouseMoved:(NSEvent *)event 149 | { 150 | NSPoint location = [self convertPoint:event.locationInWindow fromView:nil]; 151 | [self maskViewsWithX:location.x]; 152 | } 153 | 154 | - (void)mouseDragged:(NSEvent *)theEvent 155 | { 156 | [self mouseMoved:theEvent]; 157 | } 158 | 159 | - (void)maskViewsWithX:(CGFloat)x 160 | { 161 | x = roundf(x); 162 | 163 | CGPathRef mask = [self newRightMaskPathWithX:x]; 164 | CAShapeLayer *rightBasedMaskLayer = [CAShapeLayer layer]; 165 | [rightBasedMaskLayer setPath:mask]; 166 | CGPathRelease(mask); 167 | 168 | self.frontImageView.layer.mask = rightBasedMaskLayer; 169 | 170 | mask = [self newLeftMaskPathWithX:x]; 171 | CAShapeLayer *leftBasedMaskLayer = [CAShapeLayer layer]; 172 | [leftBasedMaskLayer setPath:mask]; 173 | CGPathRelease(mask); 174 | 175 | self.backImageView.layer.mask = leftBasedMaskLayer; 176 | 177 | CGRect frame = [self boundsForDivider]; 178 | frame.origin.x = x; 179 | self.divider.frame = frame; 180 | } 181 | 182 | - (CGRect)boundsForDivider 183 | { 184 | return CGRectMake(0, -20, 1, CGRectGetHeight(self.bounds) + 40); 185 | } 186 | 187 | - (CGPathRef)newRightMaskPathWithX:(CGFloat)x; 188 | { 189 | CGMutablePathRef maskPath = CGPathCreateMutable(); 190 | 191 | CGRect rect = CGRectMake(0, 0, x, CGRectGetHeight(self.bounds)); 192 | CGPathAddRect(maskPath, NULL, rect); 193 | 194 | CGPathCloseSubpath(maskPath); 195 | 196 | return maskPath; 197 | } 198 | 199 | - (CGPathRef)newLeftMaskPathWithX:(CGFloat)x; 200 | { 201 | CGMutablePathRef maskPath = CGPathCreateMutable(); 202 | 203 | CGRect rect = CGRectMake(x, 0, CGRectGetWidth(self.bounds) - x, CGRectGetHeight(self.bounds)); 204 | CGPathAddRect(maskPath, NULL, rect); 205 | 206 | CGPathCloseSubpath(maskPath); 207 | 208 | return maskPath; 209 | } 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /Snapshots/ORTestsSuiteModels.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class ORTestCase, ORKaleidoscopeCommand, ORSnapshotCreationReference; 4 | 5 | NS_ASSUME_NONNULL_BEGIN 6 | 7 | @interface ORTestSuite : NSObject 8 | @property (nonatomic, copy) NSString *name; 9 | @property (nonatomic, strong) NSMutableArray *testCases; 10 | 11 | - (NSArray *)failingTestCases; 12 | - (BOOL)hasFailingTests; 13 | - (BOOL)hasNewSnapshots; 14 | 15 | + (ORTestSuite *)suiteFromString:(NSString *)line; 16 | - (ORTestCase *)latestTestCase; 17 | @end 18 | 19 | @interface ORTestCase : NSObject 20 | 21 | @property (nonatomic, copy) NSString *name; 22 | @property (nonatomic, strong) NSMutableArray *commands; 23 | @property (nonatomic, strong) NSMutableArray *snapshots; 24 | 25 | - (NSArray *)uniqueDiffCommands; 26 | 27 | @property (nonatomic, assign) BOOL hasFailingTests; 28 | 29 | + (ORTestCase *)caseFromString:(NSString *)line; 30 | - (void)addCommand:(ORKaleidoscopeCommand *)command; 31 | - (void)addSnapshot:(ORSnapshotCreationReference *)snapshot; 32 | - (ORKaleidoscopeCommand *)latestCommand; 33 | @end 34 | 35 | 36 | @interface ORKaleidoscopeCommand : NSObject 37 | 38 | - (void)launch; 39 | - (void)swapImages; 40 | - (void)openInFinder; 41 | - (void)openInPreview; 42 | 43 | @property (nonatomic, assign) BOOL fails; 44 | @property (nonatomic, copy) NSString *beforePath; 45 | @property (nonatomic, copy) NSString *afterPath; 46 | @property (nonatomic, copy) NSString *fullCommand; 47 | @property (nonatomic, weak) ORTestCase *testCase; 48 | @property (nonatomic, copy) NSString *projectLocation; 49 | 50 | 51 | + (instancetype)commandFromString:(NSString *)command; 52 | @end 53 | 54 | @interface ORSnapshotCreationReference : NSObject 55 | 56 | @property (nonatomic, copy) NSString *name; 57 | @property (nonatomic, copy) NSString *path; 58 | @property (nonatomic, weak) ORTestCase *testCase; 59 | 60 | + (instancetype)referenceFromString:(NSString *)line; 61 | @end 62 | 63 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /Snapshots/ORTestsSuiteModels.m: -------------------------------------------------------------------------------- 1 | #import "ORTestsSuiteModels.h" 2 | #import "NSFileManager+RecursiveFind.h" 3 | #import "ORKaleidoscopeController.h" 4 | #import 5 | 6 | @implementation ORTestSuite 7 | 8 | + (ORTestSuite *)suiteFromString:(NSString *)line 9 | { 10 | NSArray *components = [line componentsSeparatedByString:@"Test Suite '"]; 11 | NSArray *endComponents = [line componentsSeparatedByString:@"' started at"]; 12 | 13 | if (components.count == 2 && endComponents.count == 2) { 14 | ORTestSuite *suite = [[ORTestSuite alloc] init]; 15 | suite.testCases = [NSMutableArray array]; 16 | suite.name = [[components.lastObject componentsSeparatedByString:@"'"] firstObject]; 17 | return suite; 18 | } 19 | 20 | return nil; 21 | } 22 | 23 | - (ORTestCase *)latestTestCase 24 | { 25 | return self.testCases.lastObject; 26 | } 27 | 28 | - (NSArray *)failingTestCases 29 | { 30 | return [self.testCases filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(ORTestCase *testCase, NSDictionary *bindings) { 31 | return testCase.hasFailingTests; 32 | }]]; 33 | } 34 | 35 | - (BOOL)hasFailingTests 36 | { 37 | return self.failingTestCases.count > 0; 38 | } 39 | 40 | - (BOOL)hasNewSnapshots 41 | { 42 | for (ORTestCase *testCase in self.testCases) { 43 | if (testCase.snapshots.count > 0) { 44 | return YES; 45 | } 46 | } 47 | return NO; 48 | } 49 | 50 | 51 | @end 52 | 53 | @implementation ORTestCase 54 | 55 | + (ORTestCase *)caseFromString:(NSString *)line 56 | { 57 | NSArray *components = [line componentsSeparatedByString:@"Test Case '-["]; 58 | NSArray *endComponents = [line componentsSeparatedByString:@"]' started."]; 59 | 60 | if (components.count == 2 && endComponents.count == 2) { 61 | ORTestCase *testCase = [[ORTestCase alloc] init]; 62 | testCase.commands = [NSMutableArray array]; 63 | testCase.snapshots = [NSMutableArray array]; 64 | 65 | // Let's make it readable 66 | NSString *name = [[components.lastObject componentsSeparatedByString:@"'"] firstObject]; 67 | name = [[name componentsSeparatedByString:@" "] lastObject]; 68 | name = [[name componentsSeparatedByString:@"]"] firstObject]; 69 | name = [name stringByReplacingOccurrencesOfString:@"_" withString:@" "]; 70 | 71 | // We avoid hitting ends of words by adding the space, but that potentially misses the first one 72 | name = [@" " stringByAppendingString:name]; 73 | name = [name stringByReplacingOccurrencesOfString:@" hasn t" withString:@" hasn't"]; 74 | name = [name stringByReplacingOccurrencesOfString:@" isn t" withString:@" isn't"]; 75 | name = [name stringByReplacingOccurrencesOfString:@" won t" withString:@" won't"]; 76 | name = [name stringByReplacingOccurrencesOfString:@" don t" withString:@" don't"]; 77 | name = [name stringByReplacingOccurrencesOfString:@" doesn t" withString:@" doesn't"]; 78 | name = [name stringByReplacingOccurrencesOfString:@" shouldn t" withString:@" shouldn't"]; 79 | name = [name stringByReplacingOccurrencesOfString:@" can t" withString:@" can't"]; 80 | 81 | // So we take the 2nd char and move it to be the first 82 | NSString *firstCharacterCaps = [[name substringWithRange:NSMakeRange(1, 1)] uppercaseString]; 83 | name = [name stringByReplacingCharactersInRange:NSMakeRange(0,2) withString:firstCharacterCaps]; 84 | testCase.name = name; 85 | return testCase; 86 | } 87 | 88 | return nil; 89 | } 90 | 91 | - (void)addCommand:(ORKaleidoscopeCommand *)command 92 | { 93 | [self.commands addObject:command]; 94 | command.testCase = self; 95 | } 96 | 97 | - (void)addSnapshot:(ORSnapshotCreationReference *)snapshot 98 | { 99 | [self.snapshots addObject:snapshot]; 100 | snapshot.testCase = self; 101 | } 102 | 103 | - (ORKaleidoscopeCommand *)latestCommand 104 | { 105 | return self.commands.lastObject; 106 | } 107 | 108 | - (BOOL)hasFailingTests 109 | { 110 | return self.uniqueDiffCommands.count > 0; 111 | } 112 | 113 | - (NSArray *)uniqueDiffCommands 114 | { 115 | NSOrderedSet *commands = [NSOrderedSet orderedSetWithArray: [self.commands filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(ORKaleidoscopeCommand *command, NSDictionary *bindings) { 116 | return command.fails; 117 | }]]]; 118 | 119 | if (commands.count == 1) return commands.array; 120 | 121 | return [commands.array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(ORKaleidoscopeCommand *command, NSDictionary *bindings) { 122 | return [[NSFileManager defaultManager] contentsEqualAtPath:command.afterPath andPath:command.beforePath] == NO; 123 | }]]; 124 | } 125 | 126 | 127 | @end 128 | 129 | @implementation ORKaleidoscopeCommand 130 | 131 | + (instancetype)commandFromString:(NSString *)command 132 | { 133 | NSArray *components = [command componentsSeparatedByString:@"\""]; 134 | if(components.count > 4){ 135 | ORKaleidoscopeCommand *obj = [[self alloc] init]; 136 | obj.fullCommand = command; 137 | obj.beforePath = components[1]; 138 | obj.afterPath = components[3]; 139 | return obj; 140 | } 141 | return nil; 142 | } 143 | 144 | - (NSURL *)beforeURL 145 | { 146 | return [NSURL fileURLWithPath:self.beforePath]; 147 | } 148 | 149 | - (NSURL *)afterURL 150 | { 151 | return [NSURL fileURLWithPath:self.afterPath]; 152 | } 153 | 154 | - (BOOL)isEqual:(ORKaleidoscopeCommand *)anObject 155 | { 156 | return [self.beforePath isEqual:anObject.beforePath] && [self.afterPath isEqual:anObject.afterPath] && self.fails == anObject.fails; 157 | } 158 | 159 | - (NSUInteger)hash 160 | { 161 | return [self.beforePath stringByAppendingString:self.afterPath].hash; 162 | } 163 | 164 | - (void)launch 165 | { 166 | NSTask *task = [[NSTask alloc] init]; 167 | if ([ORKaleidoscopeController isInstalled]) { 168 | [task setLaunchPath: @"/usr/local/bin/ksdiff"]; 169 | }else{ 170 | [task setLaunchPath: @"/usr/bin/open"]; 171 | } 172 | NSArray *arguments = @[ self.beforePath, self.afterPath]; 173 | [task setArguments: arguments]; 174 | [task launch]; 175 | } 176 | 177 | - (void)swapImages 178 | { 179 | NSError *error = nil; 180 | 181 | NSArray *components = [self.beforeURL pathComponents]; 182 | NSString *name = [components.lastObject stringByReplacingOccurrencesOfString:@"reference_" withString:@""]; 183 | NSString *folder = components[components.count -2]; 184 | 185 | NSString *originalReference = [[NSFileManager defaultManager] or_findFileWithNamePrefix:name inFolder:folder]; 186 | originalReference = [originalReference stringByReplacingOccurrencesOfString:@"file://" withString:@""]; 187 | originalReference = [originalReference stringByReplacingOccurrencesOfString:@"%20" withString:@" "]; 188 | NSURL *originalFileURL = [NSURL fileURLWithPath:originalReference]; 189 | 190 | if (![[NSFileManager defaultManager] removeItemAtURL:originalFileURL error:&error]) { 191 | NSLog(@"Error deleting before %@", error); 192 | } 193 | 194 | 195 | if (![[NSFileManager defaultManager] copyItemAtURL:[self afterURL] toURL:originalFileURL error:&error]) { 196 | NSLog(@"Error moving before to after %@", error); 197 | } 198 | } 199 | 200 | - (void)openInFinder 201 | { 202 | [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[ self.beforeURL ]]; 203 | } 204 | 205 | - (void)openInPreview 206 | { 207 | NSString *lastPathComponent = [self.beforePath lastPathComponent]; 208 | NSRange underscoreRange = [lastPathComponent rangeOfString:@"_"]; 209 | NSString *diffFilename = [lastPathComponent stringByReplacingCharactersInRange:NSMakeRange(0, underscoreRange.location) withString:@"diff"]; 210 | NSString *diffPath = [self.beforePath stringByReplacingOccurrencesOfString:lastPathComponent withString:diffFilename]; 211 | [[NSWorkspace sharedWorkspace] openFile:diffPath withApplication:@"Preview"]; 212 | } 213 | 214 | @end 215 | 216 | 217 | @implementation ORSnapshotCreationReference 218 | 219 | + (instancetype)referenceFromString:(NSString *)line 220 | { 221 | // 2014-06-16 11:34:57.579 ArtsyFolio[45418:60b] /Users/orta/dev/ios/energy/ArtsyFolio Tests/ARAdminPartnerSelectViewControllerTests.m:40 snapshot looks_right_on_phone successfully recorded, replace recordSnapshot with a check 222 | 223 | // 2014-07-28 14:17:39.979 FolioDev[15613:607] /Users/orta/spiel/ios/energy/ArtsyFolio Tests/ARExpectaExtensions.m:18 snapshot default state as ipad dark successfully recorded, replace recordSnapshot with a check 224 | 225 | // /Users/orta/dev/ios/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionBidHistoryViewControllerTests.swift:28: error: -[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_open_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionBidHistoryViewControllerTestsswift_21] : failed - , got snapshot nil successfully recorded, replace recordSnapshot with a check 226 | 227 | NSArray *components = [line componentsSeparatedByString:@"snapshot "]; 228 | NSArray *endComponents = [line componentsSeparatedByString:@" successfully recorded, replace recordSnapshot with a check"]; 229 | 230 | if (components.count == 2 && endComponents.count == 2) { 231 | 232 | ORSnapshotCreationReference *obj = [[self alloc] init]; 233 | obj.name = [endComponents.firstObject componentsSeparatedByString:@"snapshot "].lastObject; 234 | return obj; 235 | } 236 | return nil; 237 | } 238 | 239 | 240 | - (BOOL)isEqual:(ORSnapshotCreationReference *)anObject 241 | { 242 | return [self.path isEqual:anObject.path]; 243 | } 244 | 245 | - (NSUInteger)hash 246 | { 247 | return self.path.hash; 248 | } 249 | 250 | 251 | @end -------------------------------------------------------------------------------- /Snapshots/SnapshotsViewController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import PXSourceList 3 | import Interstellar 4 | import FileKit 5 | 6 | class SnapshotsViewController: NSViewController { 7 | 8 | let dev = DeveloperDirWatcher() 9 | 10 | @IBOutlet var sourceListDelegate: SourceListController! 11 | @IBOutlet var appsDataSource: SourceListDataSource! 12 | @IBOutlet var logsDataSource: SnapshotLogDataSource! 13 | 14 | @IBOutlet weak var sourceList: PXSourceList! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | dev.startParsing() 20 | 21 | dev.appsWithMetadataUpdatedSignal.next { names in 22 | self.appsDataSource.updateNames(names) 23 | self.sourceList.reloadData() 24 | } 25 | 26 | sourceListDelegate.appSelected.next { app in 27 | self.dev.getLogsForApp(app.name).next { paths in 28 | app.logs = paths.map { Log(path: $0) } 29 | 30 | self.logsDataSource.logsForSelectedApp = app.logs 31 | app.readLogs() 32 | } 33 | } 34 | 35 | logsDataSource.logSelected.next { log in 36 | let s = self.snapshotsPreviewController 37 | s.newLogSelected.update(log) 38 | } 39 | } 40 | 41 | @IBAction func reloadAppsForSnapshots(sender: AnyObject) { 42 | dev.startParsing() 43 | } 44 | 45 | var splitController: NSSplitViewController { 46 | return childViewControllers.filter { $0.isKindOfClass(NSSplitViewController) }.first! as! NSSplitViewController 47 | } 48 | 49 | 50 | var snapshotsPreviewController: LogSnapshotsPreviewViewController { 51 | return splitController.childViewControllers.filter { $0.isKindOfClass(LogSnapshotsPreviewViewController) }.first! as! LogSnapshotsPreviewViewController 52 | } 53 | } 54 | 55 | class SnapshotLogController: NSObject { 56 | let reader = ORLogReader() 57 | } 58 | 59 | class SnapshotLogDataSource: NSObject { 60 | dynamic var logsForSelectedApp = [Log]() 61 | 62 | let logSelected = Signal() 63 | 64 | @IBOutlet weak var logsTableView: NSTableView! 65 | 66 | override func awakeFromNib() { 67 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(selectionChanged), name: NSTableViewSelectionDidChangeNotification, object: logsTableView) 68 | } 69 | 70 | deinit { 71 | NSNotificationCenter.defaultCenter().removeObserver(self) 72 | } 73 | 74 | func selectionChanged(notification: NSNotification) { 75 | guard let table = notification.object as? NSTableView else { return } 76 | if table.selectedRow >= 0 { 77 | logSelected.update(logsForSelectedApp[table.selectedRow]) 78 | } 79 | } 80 | } 81 | 82 | class SourceListController: NSObject, PXSourceListDelegate { 83 | 84 | let appSelected = Signal() 85 | 86 | func sourceList(aSourceList: PXSourceList!, isGroupAlwaysExpanded group: AnyObject!) -> Bool { 87 | return true 88 | } 89 | 90 | func sourceList(sourceList: PXSourceList!, viewForItem item: AnyObject!) -> NSView! { 91 | guard let item = item as? PXSourceListItem else { return NSView() } 92 | 93 | let identifier = sourceList.levelForItem(item) == 0 ? "HeaderCell" : "MainCell" 94 | guard let cellView = sourceList.makeViewWithIdentifier(identifier, owner: nil) as? PXSourceListTableCellView else { return NSView() } 95 | 96 | guard let app = item.representedObject as? App else { 97 | // Must be a header 98 | cellView.textField?.stringValue = item.title 99 | return cellView 100 | } 101 | 102 | cellView.textField?.stringValue = app.prettyName 103 | return cellView; 104 | } 105 | 106 | func sourceListSelectionDidChange(notification: NSNotification!) { 107 | let sourceList = notification.object as! PXSourceList 108 | guard let newSelectedItem = sourceList.itemAtRow(sourceList.selectedRow) as? PXSourceListItem else { return } 109 | guard let app = newSelectedItem.representedObject as? App else { return } 110 | 111 | appSelected.update(app) 112 | } 113 | } 114 | 115 | class SourceListDataSource: NSObject, PXSourceListDataSource { 116 | 117 | var snapshots = PXSourceListItem(title: "Snapshots", identifier: "root") 118 | 119 | var sourceListItems = [PXSourceListItem]() 120 | 121 | var rootNodes: [PXSourceListItem] { 122 | return [snapshots] 123 | } 124 | 125 | func updateNames(apps: [App]) { 126 | snapshots.removeChildItems(snapshots.children) 127 | let useful:[PXSourceListItem] = apps.filter { $0.logs.isEmpty == false }.map { 128 | return PXSourceListItem(representedObject: $0, icon:nil) 129 | } 130 | snapshots.children = useful 131 | } 132 | 133 | func sourceList(aSourceList: PXSourceList!, child index: UInt, ofItem item: AnyObject!) -> AnyObject! { 134 | if item == nil { return rootNodes[Int(index)] } 135 | guard let item = item as? PXSourceListItem else { return nil } 136 | return item.children[Int(index)] 137 | } 138 | 139 | func sourceList(sourceList: PXSourceList!, numberOfChildrenOfItem item: AnyObject!) -> UInt { 140 | if item == nil { return UInt(rootNodes.count) } 141 | guard let item = item as? PXSourceListItem else { return 0 } 142 | return UInt(item.children.count) 143 | } 144 | 145 | func sourceList(aSourceList: PXSourceList!, isItemExpandable item: AnyObject!) -> Bool { 146 | guard let item = item as? PXSourceListItem else { return false } 147 | return item.hasChildren() 148 | } 149 | 150 | func sourceList(aSourceList: PXSourceList!, objectValueForItem item: AnyObject!) -> AnyObject! { 151 | return NSObject() 152 | } 153 | } 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /Snapshots/ViewController.swift: -------------------------------------------------------------------------------- 1 | 2 | import Cocoa 3 | 4 | class ViewController: NSViewController { 5 | 6 | override func viewDidLoad() { 7 | super.viewDidLoad() 8 | 9 | // Do any additional setup after loading the view. 10 | } 11 | 12 | override var representedObject: AnyObject? { 13 | didSet { 14 | // Update the view, if already loaded. 15 | } 16 | } 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /SnapshotsTests/DeveloperDirWatcherSpecs.swift: -------------------------------------------------------------------------------- 1 | import Quick 2 | import Nimble 3 | 4 | class DeveloperDirWatcherSpecs: QuickSpec { 5 | override func spec() { 6 | 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SnapshotsTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SnapshotsTests/LogReaderSpecs.swift: -------------------------------------------------------------------------------- 1 | import Quick 2 | import Nimble 3 | import Cocoa 4 | 5 | @testable import Snapshots 6 | 7 | class LogReaderSpecs: QuickSpec { 8 | 9 | func getLog(named: String) -> String { 10 | let bundle = NSBundle(forClass: LogReaderSpecs.self) 11 | let logPath = bundle.pathForResource(named, ofType:".log")! 12 | return try! NSString(contentsOfFile: logPath, encoding: NSUTF8StringEncoding) as String 13 | } 14 | 15 | override func spec() { 16 | it("gives an expected amout of tests") { 17 | let log = self.getLog("MultipleSnapshotsErrors") 18 | let reader = ORLogReader() 19 | reader.readLog(log) 20 | 21 | expect(reader.hasSnapshotTestErrors()) == true 22 | expect(reader.hasNewSnapshots()) == false 23 | 24 | expect(reader.ksdiffCommands().count) == 8 25 | expect(reader.uniqueDiffCommands().count) == 8 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /SnapshotsTests/MultipleSnapshotsErrors.log: -------------------------------------------------------------------------------- 1 | 2 | Test Suite 'ARTopViewControllerSpec' started at 2015-04-22 15:52:50 +0000 3 | Test Case '-[ARTopViewControllerSpec test_visuals__default_state_as_ipad]' started. 4 | 2015-04-22 11:52:50.063 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 5 | 2015-04-22 11:52:50.301 FolioDev[15845:607] If you have Kaleidoscope installed you can run this command to see an image diff: 6 | ksdiff "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/reference_default state as ipad dark@2x.png" "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/failed_default state as ipad dark@2x.png" 7 | 2015-04-22 11:52:50.301 FolioDev[15845:607] /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:44 expected a matching snapshot named default state as ipad dark 8 | NSLocalizedDescription: Images different 9 | /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:44: error: -[ARTopViewControllerSpec test_visuals__default_state_as_ipad] : expected a matching snapshot named default state as ipad dark 10 | NSLocalizedDescription: Images different 11 | 2015-04-22 11:52:50.305 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 12 | 2015-04-22 11:52:50.541 FolioDev[15845:607] If you have Kaleidoscope installed you can run this command to see an image diff: 13 | ksdiff "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/reference_default state as ipad light@2x.png" "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/failed_default state as ipad light@2x.png" 14 | 2015-04-22 11:52:50.542 FolioDev[15845:607] /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:44 expected a matching snapshot named default state as ipad light 15 | NSLocalizedDescription: Images different 16 | /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:44: error: -[ARTopViewControllerSpec test_visuals__default_state_as_ipad] : expected a matching snapshot named default state as ipad light 17 | NSLocalizedDescription: Images different 18 | Test Case '-[ARTopViewControllerSpec test_visuals__default_state_as_ipad]' failed (0.496 seconds). 19 | Test Case '-[ARTopViewControllerSpec test_visuals__default_state_as_iphone]' started. 20 | 2015-04-22 11:52:50.559 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 21 | 2015-04-22 11:52:50.584 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 22 | Test Case '-[ARTopViewControllerSpec test_visuals__default_state_as_iphone]' passed (0.059 seconds). 23 | Test Case '-[ARTopViewControllerSpec test_visuals__default_state_for_collector_folio_as_ipad]' started. 24 | 2015-04-22 11:52:50.620 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 25 | 2015-04-22 11:52:50.853 FolioDev[15845:607] If you have Kaleidoscope installed you can run this command to see an image diff: 26 | ksdiff "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/reference_default state for collector folio as ipad dark@2x.png" "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/failed_default state for collector folio as ipad dark@2x.png" 27 | 2015-04-22 11:52:50.853 FolioDev[15845:607] /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:50 expected a matching snapshot named default state for collector folio as ipad dark 28 | NSLocalizedDescription: Images different 29 | /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:50: error: -[ARTopViewControllerSpec test_visuals__default_state_for_collector_folio_as_ipad] : expected a matching snapshot named default state for collector folio as ipad dark 30 | NSLocalizedDescription: Images different 31 | 2015-04-22 11:52:50.857 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 32 | 2015-04-22 11:52:51.091 FolioDev[15845:607] If you have Kaleidoscope installed you can run this command to see an image diff: 33 | ksdiff "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/reference_default state for collector folio as ipad light@2x.png" "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/failed_default state for collector folio as ipad light@2x.png" 34 | 2015-04-22 11:52:51.092 FolioDev[15845:607] /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:50 expected a matching snapshot named default state for collector folio as ipad light 35 | NSLocalizedDescription: Images different 36 | /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:50: error: -[ARTopViewControllerSpec test_visuals__default_state_for_collector_folio_as_ipad] : expected a matching snapshot named default state for collector folio as ipad light 37 | NSLocalizedDescription: Images different 38 | Test Case '-[ARTopViewControllerSpec test_visuals__default_state_for_collector_folio_as_ipad]' failed (0.491 seconds). 39 | Test Case '-[ARTopViewControllerSpec test_visuals__default_state_for_collector_folio_as_iphone]' started. 40 | 2015-04-22 11:52:51.110 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 41 | 2015-04-22 11:52:51.133 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 42 | Test Case '-[ARTopViewControllerSpec test_visuals__default_state_for_collector_folio_as_iphone]' passed (0.059 seconds). 43 | Test Case '-[ARTopViewControllerSpec test_visuals__showing_edit_in_albums_mode_as_ipad]' started. 44 | 2015-04-22 11:52:51.168 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 45 | 2015-04-22 11:52:51.409 FolioDev[15845:607] If you have Kaleidoscope installed you can run this command to see an image diff: 46 | ksdiff "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/reference_showing edit in albums mode as ipad dark@2x.png" "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/failed_showing edit in albums mode as ipad dark@2x.png" 47 | 2015-04-22 11:52:51.409 FolioDev[15845:607] /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:58 expected a matching snapshot named showing edit in albums mode as ipad dark 48 | NSLocalizedDescription: Images different 49 | /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:58: error: -[ARTopViewControllerSpec test_visuals__showing_edit_in_albums_mode_as_ipad] : expected a matching snapshot named showing edit in albums mode as ipad dark 50 | NSLocalizedDescription: Images different 51 | 2015-04-22 11:52:51.413 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 52 | 2015-04-22 11:52:51.654 FolioDev[15845:607] If you have Kaleidoscope installed you can run this command to see an image diff: 53 | ksdiff "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/reference_showing edit in albums mode as ipad light@2x.png" "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/failed_showing edit in albums mode as ipad light@2x.png" 54 | 2015-04-22 11:52:51.654 FolioDev[15845:607] /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:58 expected a matching snapshot named showing edit in albums mode as ipad light 55 | NSLocalizedDescription: Images different 56 | /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:58: error: -[ARTopViewControllerSpec test_visuals__showing_edit_in_albums_mode_as_ipad] : expected a matching snapshot named showing edit in albums mode as ipad light 57 | NSLocalizedDescription: Images different 58 | Test Case '-[ARTopViewControllerSpec test_visuals__showing_edit_in_albums_mode_as_ipad]' failed (0.504 seconds). 59 | Test Case '-[ARTopViewControllerSpec test_visuals__showing_edit_in_albums_mode_as_iphone]' started. 60 | 2015-04-22 11:52:51.673 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 61 | 2015-04-22 11:52:51.697 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 62 | Test Case '-[ARTopViewControllerSpec test_visuals__showing_edit_in_albums_mode_as_iphone]' passed (0.062 seconds). 63 | Test Case '-[ARTopViewControllerSpec test_visuals__showing_edit_mode_for_albums_as_ipad]' started. 64 | 2015-04-22 11:52:51.750 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 65 | 2015-04-22 11:52:51.982 FolioDev[15845:607] If you have Kaleidoscope installed you can run this command to see an image diff: 66 | ksdiff "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/reference_showing edit mode for albums as ipad dark@2x.png" "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/failed_showing edit mode for albums as ipad dark@2x.png" 67 | 2015-04-22 11:52:51.982 FolioDev[15845:607] /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:66 expected a matching snapshot named showing edit mode for albums as ipad dark 68 | NSLocalizedDescription: Images different 69 | /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:66: error: -[ARTopViewControllerSpec test_visuals__showing_edit_mode_for_albums_as_ipad] : expected a matching snapshot named showing edit mode for albums as ipad dark 70 | NSLocalizedDescription: Images different 71 | 2015-04-22 11:52:52.001 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 72 | 2015-04-22 11:52:52.232 FolioDev[15845:607] If you have Kaleidoscope installed you can run this command to see an image diff: 73 | ksdiff "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/reference_showing edit mode for albums as ipad light@2x.png" "/Users/orta/Library/Developer/CoreSimulator/Devices/91CEFD41-40FA-4240-B239-B68D3BB54333/data/Applications/6B54B9B7-81C1-402A-9016-806EBC3A6D84/tmp/ARTopViewControllerSpec/failed_showing edit mode for albums as ipad light@2x.png" 74 | 2015-04-22 11:52:52.232 FolioDev[15845:607] /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:66 expected a matching snapshot named showing edit mode for albums as ipad light 75 | NSLocalizedDescription: Images different 76 | /Users/orta/dev/ios/energy/ArtsyFolio Tests/View Controllers/ARTopViewControllerTests.m:66: error: -[ARTopViewControllerSpec test_visuals__showing_edit_mode_for_albums_as_ipad] : expected a matching snapshot named showing edit mode for albums as ipad light 77 | NSLocalizedDescription: Images different 78 | Test Case '-[ARTopViewControllerSpec test_visuals__showing_edit_mode_for_albums_as_ipad]' failed (0.517 seconds). 79 | Test Case '-[ARTopViewControllerSpec test_visuals__showing_edit_mode_for_albums_as_iphone]' started. 80 | 2015-04-22 11:52:52.261 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 81 | 2015-04-22 11:52:52.290 FolioDev[15845:607] -[WYPopoverController positionPopover:] (2541) : popoverContainerView.frame = {{0, 0}, {0, 0}} 82 | -------------------------------------------------------------------------------- /SnapshotsTests/NewSnapshotsRecorded.log: -------------------------------------------------------------------------------- 1 | (void *) $0 = 0x00007ff56360c1e0 2 | (void *) $1 = 0x0000000000000000 3 | (void *) $2 = 0x0000000000000000 4 | 2016-04-07 13:11:12.276 Artsy[48558:11314806] INFO: Reveal Server started (Protocol Version 25). 5 | 2016-04-07 13:11:12.302 Artsy[48558:11314806] iRate did not prompt for rating because the user has already rated this version 6 | 2016-04-07 13:11:12.302 Artsy[48558:11316006] iRate is checking http://itunes.apple.com/US/lookup?bundleId=net.artsy.artsy to retrieve the App Store details... 7 | 13:11:12.424 Artsy[48558:11316013] _XCT_testBundleReadyWithProtocolVersion:minimumVersion: reply received 8 | 13:11:12.424 Artsy[48558:11315788] _IDE_startExecutingTestPlanWithProtocolVersion:16 9 | Pending: looks good by default 10 | Test Suite 'Selected tests' started at 2016-04-07 13:11:12.887 11 | Test Suite 'Artsy Tests.xctest' started at 2016-04-07 13:11:12.888 12 | Test Suite 'LiveAuctionBidButtonTests' started at 2016-04-07 13:11:12.888 13 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_upcoming]' started. 14 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_upcoming]' passed (0.017 seconds). 15 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_sold]' started. 16 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_sold]' passed (0.006 seconds). 17 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_failed]' started. 18 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_failed]' passed (0.005 seconds). 19 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_closed]' started. 20 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_closed]' passed (0.005 seconds). 21 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_waiting]' started. 22 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_waiting]' passed (0.006 seconds). 23 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_trial]' started. 24 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_trial]' passed (0.009 seconds). 25 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_biddable]' started. 26 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_biddable]' passed (0.005 seconds). 27 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_max_bidder]' started. 28 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_max_bidder]' passed (0.007 seconds). 29 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_not_max_bidder]' started. 30 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_not_max_bidder]' passed (0.005 seconds). 31 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_in_progress]' started. 32 | Test Case '-[Artsy_Tests.LiveAuctionBidButtonTests has_valid_snapshot_in_progress]' passed (0.005 seconds). 33 | Test Suite 'LiveAuctionBidButtonTests' passed at 2016-04-07 13:11:12.965. 34 | Executed 10 tests, with 0 failures (0 unexpected) in 0.071 (0.077) seconds 35 | Test Suite 'LiveAuctionBidHistoryViewControllerTests' started at 2016-04-07 13:11:12.965 36 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_open]' started. 37 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_open]' passed (0.021 seconds). 38 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_closed]' started. 39 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_closed]' passed (0.006 seconds). 40 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_bid]' started. 41 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_bid]' passed (0.008 seconds). 42 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_final_call]' started. 43 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_final_call]' passed (0.007 seconds). 44 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_fair_warning]' started. 45 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_fair_warning]' passed (0.009 seconds). 46 | Test Suite 'LiveAuctionBidHistoryViewControllerTests' passed at 2016-04-07 13:11:13.020. 47 | Executed 5 tests, with 0 failures (0 unexpected) in 0.052 (0.055) seconds 48 | Test Suite 'LiveAuctionBidViewControllerSpecs' started at 2016-04-07 13:11:13.021 49 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs looks_right_on_phones]' started. 50 | 51 | 52 | 2016-04-07 13:13:00.073 Artsy[48558:11315966] CFNetwork SSLHandshake failed (-9806) 53 | 54 | 55 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs looks_right_on_phones]' passed (111.855 seconds). 56 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs updating__updates_when_new_events_come_in]' started. 57 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs updating__updates_when_new_events_come_in]' passed (0.013 seconds). 58 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs updating__ensures_the_bid_is_moved_above_the_current_max_bid]' started. 59 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs updating__ensures_the_bid_is_moved_above_the_current_max_bid]' passed (0.014 seconds). 60 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_network_issues]' started. 61 | 2016-04-07 13:13:04.945 Artsy[48558:11314806] Reference image save at: /Users/orta/dev/ios/apps/eigen/Artsy_Tests/ReferenceImages/LiveAuctionBidViewControllerTests/networking__has_valid_snapshot_network_issues@2x.png 62 | /Users/orta/dev/ios/apps/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionBidViewControllerTests.swift:82: error: -[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_network_issues] : , got snapshot networking__has_valid_snapshot_network_issues successfully recorded, replace recordSnapshot with a check 63 | 64 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_network_issues]' failed (0.043 seconds). 65 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_waiting]' started. 66 | 2016-04-07 13:13:04.982 Artsy[48558:11314806] Reference image save at: /Users/orta/dev/ios/apps/eigen/Artsy_Tests/ReferenceImages/LiveAuctionBidViewControllerTests/networking__has_valid_snapshot_waiting@2x.png 67 | /Users/orta/dev/ios/apps/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionBidViewControllerTests.swift:82: error: -[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_waiting] : , got snapshot networking__has_valid_snapshot_waiting successfully recorded, replace recordSnapshot with a check 68 | 69 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_waiting]' failed (0.038 seconds). 70 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_sold]' started. 71 | 2016-04-07 13:13:05.019 Artsy[48558:11314806] Reference image save at: /Users/orta/dev/ios/apps/eigen/Artsy_Tests/ReferenceImages/LiveAuctionBidViewControllerTests/networking__has_valid_snapshot_sold@2x.png 72 | /Users/orta/dev/ios/apps/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionBidViewControllerTests.swift:82: error: -[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_sold] : , got snapshot networking__has_valid_snapshot_sold successfully recorded, replace recordSnapshot with a check 73 | 74 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_sold]' failed (0.050 seconds). 75 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_not_max_bidder]' started. 76 | 2016-04-07 13:13:05.065 Artsy[48558:11314806] Reference image save at: /Users/orta/dev/ios/apps/eigen/Artsy_Tests/ReferenceImages/LiveAuctionBidViewControllerTests/networking__has_valid_snapshot_not_max_bidder@2x.png 77 | /Users/orta/dev/ios/apps/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionBidViewControllerTests.swift:82: error: -[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_not_max_bidder] : , got snapshot networking__has_valid_snapshot_not_max_bidder successfully recorded, replace recordSnapshot with a check 78 | 79 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_not_max_bidder]' failed (0.036 seconds). 80 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_in_progress]' started. 81 | 2016-04-07 13:13:05.101 Artsy[48558:11314806] Reference image save at: /Users/orta/dev/ios/apps/eigen/Artsy_Tests/ReferenceImages/LiveAuctionBidViewControllerTests/networking__has_valid_snapshot_in_progress@2x.png 82 | /Users/orta/dev/ios/apps/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionBidViewControllerTests.swift:82: error: -[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_in_progress] : , got snapshot networking__has_valid_snapshot_in_progress successfully recorded, replace recordSnapshot with a check 83 | 84 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_in_progress]' failed (0.030 seconds). 85 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_is_max_bidder]' started. 86 | 2016-04-07 13:13:05.131 Artsy[48558:11314806] Reference image save at: /Users/orta/dev/ios/apps/eigen/Artsy_Tests/ReferenceImages/LiveAuctionBidViewControllerTests/networking__has_valid_snapshot_is_max_bidder@2x.png 87 | /Users/orta/dev/ios/apps/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionBidViewControllerTests.swift:82: error: -[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_is_max_bidder] : , got snapshot networking__has_valid_snapshot_is_max_bidder successfully recorded, replace recordSnapshot with a check 88 | 89 | Test Case '-[Artsy_Tests.LiveAuctionBidViewControllerSpecs networking__has_valid_snapshot_is_max_bidder]' failed (0.034 seconds). 90 | Test Suite 'LiveAuctionBidViewControllerSpecs' failed at 2016-04-07 13:13:05.139. 91 | Executed 9 tests, with 6 failures (0 unexpected) in 112.112 (112.118) seconds 92 | Test Suite 'LiveAuctionLotViewControllerTests' started at 2016-04-07 13:13:05.139 93 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_closed_lots]' started. 94 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_closed_lots]' passed (0.061 seconds). 95 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_live_lots]' started. 96 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_live_lots]' passed (0.062 seconds). 97 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_upcoming_lots]' started. 98 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_upcoming_lots]' passed (0.068 seconds). 99 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__doesnt_show_a_live_auction_call_to_action_when_auction_is_closed]' started. 100 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__doesnt_show_a_live_auction_call_to_action_when_auction_is_closed]' passed (0.066 seconds). 101 | Test Suite 'LiveAuctionLotViewControllerTests' passed at 2016-04-07 13:13:05.402. 102 | Executed 4 tests, with 0 failures (0 unexpected) in 0.258 (0.263) seconds 103 | Test Suite 'LiveAuctionSocketCommunicatorSpec' started at 2016-04-07 13:13:05.402 104 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec configures_the_socket_with_the_correct_host]' started. 105 | Connected: [] 106 | Authenticated: [] 107 | Joining sale 108 | Listening for socket events. 109 | Updated auction state: [] 110 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec configures_the_socket_with_the_correct_host]' passed (0.002 seconds). 111 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec connects_the_socket_on_initialization]' started. 112 | Connected: [] 113 | Authenticated: [] 114 | Joining sale 115 | Listening for socket events. 116 | Updated auction state: [] 117 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec connects_the_socket_on_initialization]' passed (0.939 seconds). 118 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec disconnects_the_socket_when_deallocated]' started. 119 | Connected: [] 120 | Authenticated: [] 121 | Joining sale 122 | Listening for socket events. 123 | Updated auction state: [] 124 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec disconnects_the_socket_when_deallocated]' passed (0.002 seconds). 125 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec authenticates_the_socket_connection]' started. 126 | Connected: [] 127 | Authenticated: [] 128 | Joining sale 129 | Listening for socket events. 130 | Updated auction state: [] 131 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec authenticates_the_socket_connection]' passed (0.004 seconds). 132 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec authenticated__joins_the_auction]' started. 133 | Connected: [] 134 | Authenticated: [] 135 | Joining sale 136 | Listening for socket events. 137 | Updated auction state: [] 138 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec authenticated__joins_the_auction]' passed (0.015 seconds). 139 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec authenticated__listens_for_updated_auction_state]' started. 140 | Connected: [] 141 | Authenticated: [] 142 | Joining sale 143 | Listening for socket events. 144 | Updated auction state: [] 145 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec authenticated__listens_for_updated_auction_state]' passed (0.002 seconds). 146 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec authenticated__sends_its_delegate_its_updated_auction_state]' started. 147 | Connected: [] 148 | Authenticated: [] 149 | Joining sale 150 | Listening for socket events. 151 | Updated auction state: [] 152 | Updated auction state: [state!] 153 | Test Case '-[Artsy_Tests.LiveAuctionSocketCommunicatorSpec authenticated__sends_its_delegate_its_updated_auction_state]' passed (0.002 seconds). 154 | Test Suite 'LiveAuctionSocketCommunicatorSpec' passed at 2016-04-07 13:13:06.450. 155 | Executed 7 tests, with 0 failures (0 unexpected) in 0.965 (1.048) seconds 156 | Test Suite 'LiveAuctionStateFetcherSpec' started at 2016-04-07 13:13:06.450 157 | Test Case '-[Artsy_Tests.LiveAuctionStateFetcherSpec configures_its_sale_ID_correctly]' started. 158 | Test Case '-[Artsy_Tests.LiveAuctionStateFetcherSpec configures_its_sale_ID_correctly]' passed (0.002 seconds). 159 | Test Case '-[Artsy_Tests.LiveAuctionStateFetcherSpec configures_its_host_correctly]' started. 160 | Test Case '-[Artsy_Tests.LiveAuctionStateFetcherSpec configures_its_host_correctly]' passed (0.002 seconds). 161 | Test Case '-[Artsy_Tests.LiveAuctionStateFetcherSpec fetches_the_sale_state]' started. 162 | Test Case '-[Artsy_Tests.LiveAuctionStateFetcherSpec fetches_the_sale_state]' passed (0.002 seconds). 163 | Test Suite 'LiveAuctionStateFetcherSpec' passed at 2016-04-07 13:13:06.459. 164 | Executed 3 tests, with 0 failures (0 unexpected) in 0.006 (0.008) seconds 165 | Test Suite 'LiveAuctionStateManagerSpec' started at 2016-04-07 13:13:06.459 166 | Test Case '-[Artsy_Tests.LiveAuctionStateManagerSpec sets_its_saleID_upon_initialization]' started. 167 | 168 | 169 | Test Case '-[Artsy_Tests.LiveAuctionStateManagerSpec sets_its_saleID_upon_initialization]' passed (0.024 seconds). 170 | Test Case '-[Artsy_Tests.LiveAuctionStateManagerSpec creates_an_appropriate_socket_communicator]' started. 171 | 172 | Test Case '-[Artsy_Tests.LiveAuctionStateManagerSpec creates_an_appropriate_socket_communicator]' passed (0.031 seconds). 173 | Test Case '-[Artsy_Tests.LiveAuctionStateManagerSpec sets_itself_as_the_socket_communicator_delegate]' started. 174 | 175 | 176 | Test Case '-[Artsy_Tests.LiveAuctionStateManagerSpec sets_itself_as_the_socket_communicator_delegate]' passed (2.607 seconds). 177 | Test Case '-[Artsy_Tests.LiveAuctionStateManagerSpec invokes_the_state_reconciler_when_new_snapshot_data_avaialble]' started. 178 | 179 | 180 | 181 | Test Case '-[Artsy_Tests.LiveAuctionStateManagerSpec invokes_the_state_reconciler_when_new_snapshot_data_avaialble]' passed (0.006 seconds). 182 | Test Suite 'LiveAuctionStateManagerSpec' passed at 2016-04-07 13:13:09.130. 183 | Executed 4 tests, with 0 failures (0 unexpected) in 2.668 (2.671) seconds 184 | Test Suite 'LiveAuctionStateReconcilerSpec' started at 2016-04-07 13:13:09.131 185 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_are_no_lots]' started. 186 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_are_no_lots]' passed (0.002 seconds). 187 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_is_no_sale]' started. 188 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_is_no_sale]' passed (0.002 seconds). 189 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_are_no_sale_s_lots]' started. 190 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_are_no_sale_s_lots]' passed (0.002 seconds). 191 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_is_no_current_lot_id]' started. 192 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_is_no_current_lot_id]' passed (0.002 seconds). 193 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_are_no_lot_events__list_is_empty_]' started. 194 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec doesn_t_do_anything_if_there_are_no_lot_events__list_is_empty_]' passed (0.003 seconds). 195 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec sends_fresh_lots_on_first_update]' started. 196 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec sends_fresh_lots_on_first_update]' passed (0.002 seconds). 197 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec sends_current_lot]' started. 198 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec sends_current_lot]' passed (0.003 seconds). 199 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec orders_lots_correctly]' started. 200 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec orders_lots_correctly]' passed (0.003 seconds). 201 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__does_not_send_lots]' started. 202 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__does_not_send_lots]' passed (0.003 seconds). 203 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__does_not_send_sale]' started. 204 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__does_not_send_sale]' passed (0.006 seconds). 205 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__does_not_send_current_lot_if_it_has_not_changed]' started. 206 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__does_not_send_current_lot_if_it_has_not_changed]' passed (0.006 seconds). 207 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__sends_new_current_lot_when_the_lot_changes]' started. 208 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__sends_new_current_lot_when_the_lot_changes]' passed (0.007 seconds). 209 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__updates_lot_view_model_with_new_events]' started. 210 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__updates_lot_view_model_with_new_events]' passed (0.004 seconds). 211 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__doesn_t_update_lot_view_model_with_events_that_aren_t_new]' started. 212 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__doesn_t_update_lot_view_model_with_events_that_aren_t_new]' passed (0.004 seconds). 213 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__updates_lot_view_model_with_online_asking_price]' started. 214 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__updates_lot_view_model_with_online_asking_price]' passed (0.004 seconds). 215 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__updates_lot_view_model_with_reserve_status]' started. 216 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__updates_lot_view_model_with_reserve_status]' passed (0.003 seconds). 217 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__sends_lots_when_number_of_lots_change]' started. 218 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__sends_lots_when_number_of_lots_change]' passed (0.005 seconds). 219 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__sends_updated_sale_availability_if_changed]' started. 220 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__sends_updated_sale_availability_if_changed]' passed (0.006 seconds). 221 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__doesn_t_send_updated_sale_availability_if_not_changed]' started. 222 | Test Case '-[Artsy_Tests.LiveAuctionStateReconcilerSpec on_subsequent_state_update__doesn_t_send_updated_sale_availability_if_not_changed]' passed (0.006 seconds). 223 | Test Suite 'LiveAuctionStateReconcilerSpec' passed at 2016-04-07 13:13:09.356. 224 | Executed 19 tests, with 0 failures (0 unexpected) in 0.074 (0.225) seconds 225 | Test Suite 'LiveAuctionStaticDataFetcherSpec' started at 2016-04-07 13:13:09.356 226 | Test Case '-[Artsy_Tests.LiveAuctionStaticDataFetcherSpec configures_its_sale_ID_correctly]' started. 227 | Test Case '-[Artsy_Tests.LiveAuctionStaticDataFetcherSpec configures_its_sale_ID_correctly]' passed (0.002 seconds). 228 | Test Case '-[Artsy_Tests.LiveAuctionStaticDataFetcherSpec fetches_the_static_data]' started. 229 | 230 | 231 | /Users/orta/dev/ios/apps/eigen/Artsy_Tests/Networking_Tests/Live_Auctions/LiveAuctionStaticDataFetcherSpec.swift:34: error: -[Artsy_Tests.LiveAuctionStaticDataFetcherSpec fetches_the_static_data] : expected to equal <{id = "sale_id";}>, got (use beNil() to match nils) 232 | 233 | Test Case '-[Artsy_Tests.LiveAuctionStaticDataFetcherSpec fetches_the_static_data]' failed (0.037 seconds). 234 | Test Suite 'LiveAuctionStaticDataFetcherSpec' failed at 2016-04-07 13:13:09.396. 235 | Executed 2 tests, with 1 failure (0 unexpected) in 0.038 (0.040) seconds 236 | Test Suite 'Artsy Tests.xctest' failed at 2016-04-07 13:13:09.397. 237 | Executed 63 tests, with 7 failures (0 unexpected) in 116.244 (116.509) seconds 238 | Test Suite 'Quick.framework' started at 2016-04-07 13:13:09.398 239 | Test Suite 'Quick.framework' passed at 2016-04-07 13:13:09.398. 240 | Executed 0 tests, with 0 failures (0 unexpected) in 0.000 (0.000) seconds 241 | Test Suite 'Specta.framework' started at 2016-04-07 13:13:09.399 242 | Test Suite 'Specta.framework' passed at 2016-04-07 13:13:09.399. 243 | Executed 0 tests, with 0 failures (0 unexpected) in 0.000 (0.000) seconds 244 | Test Suite 'FBSnapshotTestCase.framework' started at 2016-04-07 13:13:09.400 245 | Test Suite 'FBSnapshotTestCase.framework' passed at 2016-04-07 13:13:09.456. 246 | Executed 0 tests, with 0 failures (0 unexpected) in 0.000 (0.056) seconds 247 | Test Suite 'Selected tests' failed at 2016-04-07 13:13:09.456. 248 | Executed 63 tests, with 7 failures (0 unexpected) in 116.244 (116.570) seconds 249 | 250 | 251 | Test session log: 252 | /Users/orta/Library/Developer/Xcode/DerivedData/Artsy-exhciswyomputsenvihzexoyuznk/Logs/Test/74FDAD03-6F93-498B-83D5-6CA91F36DF6E/Session-2016-04-07_13:09:10-pJRMDc.log 253 | 254 | -------------------------------------------------------------------------------- /SnapshotsTests/QuickJustNewSnapshot.log: -------------------------------------------------------------------------------- 1 | 2 | Test Suite 'LiveAuctionBidHistoryViewControllerTests' started at 2016-03-09 21:44:01.331 3 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_open_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionBidHistoryViewControllerTestsswift_21]' started. 4 | Mar 9 21:44:01 Artsy[9174] : CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 5 | Mar 9 21:44:01 Artsy[9174] : CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 6 | Mar 9 21:44:01 Artsy[9174] : CGContextSetBlendMode: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 7 | Mar 9 21:44:01 Artsy[9174] : CGContextSetAlpha: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 8 | Mar 9 21:44:01 Artsy[9174] : CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 9 | Mar 9 21:44:01 Artsy[9174] : CGContextScaleCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 10 | Mar 9 21:44:01 Artsy[9174] : CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 11 | Mar 9 21:44:01 Artsy[9174] : CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 12 | Mar 9 21:44:01 Artsy[9174] : CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 13 | 2016-03-09 21:44:01.347 Artsy[9174:24386969] Reference image save at: /Users/orta/dev/ios/eigen/Artsy_Tests/ReferenceImages/LiveAuctionBidHistoryViewControllerTests/cells__looks_right_for_open@2x.png 14 | /Users/orta/dev/ios/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionBidHistoryViewControllerTests.swift:28: error: -[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_open_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionBidHistoryViewControllerTestsswift_21] : failed - , got snapshot nil successfully recorded, replace recordSnapshot with a check 15 | 16 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_open_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionBidHistoryViewControllerTestsswift_21]' failed (0.025 seconds). 17 | Test Suite 'LiveAuctionBidHistoryViewControllerTests' failed at 2016-03-09 21:44:01.357. 18 | Executed 1 test, with 1 failure (0 unexpected) in 0.025 (0.026) seconds 19 | -------------------------------------------------------------------------------- /SnapshotsTests/QuickNewSnapshot.log: -------------------------------------------------------------------------------- 1 | Test Suite 'LiveAuctionLotViewControllerTests' started at 2016-03-09 21:44:01.358 2 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_closed_lots_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionLotViewControllerTestsswift_21]' started. 3 | Mar 9 21:44:01 Artsy[9174] : CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 4 | Mar 9 21:44:01 Artsy[9174] : CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 5 | Mar 9 21:44:01 Artsy[9174] : CGContextSetBlendMode: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 6 | Mar 9 21:44:01 Artsy[9174] : CGContextSetAlpha: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 7 | Mar 9 21:44:01 Artsy[9174] : CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 8 | Mar 9 21:44:01 Artsy[9174] : CGContextScaleCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 9 | Mar 9 21:44:01 Artsy[9174] : CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 10 | Mar 9 21:44:01 Artsy[9174] : CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 11 | Mar 9 21:44:01 Artsy[9174] : CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 12 | 2016-03-09 21:44:01.471 Artsy[9174:24386969] If you have Kaleidoscope installed you can run this command to see an image diff: 13 | ksdiff "/Users/orta/Library/Developer/CoreSimulator/Devices/D86744A5-08C8-459F-B77A-8EFC7E4DF3ED/data/Containers/Data/Application/4F5FFF12-C55E-4C7E-8EDE-0E65713AE95F/tmp/LiveAuctionLotViewControllerTests/reference_snapshots__looks_good_for_closed_lots@2x.png" "/Users/orta/Library/Developer/CoreSimulator/Devices/D86744A5-08C8-459F-B77A-8EFC7E4DF3ED/data/Containers/Data/Application/4F5FFF12-C55E-4C7E-8EDE-0E65713AE95F/tmp/LiveAuctionLotViewControllerTests/failed_snapshots__looks_good_for_closed_lots@2x.png" 14 | /Users/orta/dev/ios/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionLotViewControllerTests.swift:25: error: -[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_closed_lots_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionLotViewControllerTestsswift_21] : failed - , got expected a matching snapshot 15 | 16 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_closed_lots_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionLotViewControllerTestsswift_21]' failed (0.116 seconds). 17 | Test Case '-[Artsy_Tests.LiveAuctionLotViewControllerTests snapshots__looks_good_for_live_lots_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionLotViewControllerTestsswift_28]' started. 18 | Mar 9 21:44:01 Artsy[9174] : CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 19 | Mar 9 21:44:01 Artsy[9174] : CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 20 | Test Suite 'LiveAuctionBidHistoryViewControllerTests' started at 2016-03-09 21:44:01.331 21 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_open_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionBidHistoryViewControllerTestsswift_21]' started. 22 | Mar 9 21:44:01 Artsy[9174] : CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 23 | Mar 9 21:44:01 Artsy[9174] : CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 24 | Mar 9 21:44:01 Artsy[9174] : CGContextSetBlendMode: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 25 | Mar 9 21:44:01 Artsy[9174] : CGContextSetAlpha: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 26 | Mar 9 21:44:01 Artsy[9174] : CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 27 | Mar 9 21:44:01 Artsy[9174] : CGContextScaleCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 28 | Mar 9 21:44:01 Artsy[9174] : CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 29 | Mar 9 21:44:01 Artsy[9174] : CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 30 | Mar 9 21:44:01 Artsy[9174] : CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable. 31 | 2016-03-09 21:44:01.347 Artsy[9174:24386969] Reference image save at: /Users/orta/dev/ios/eigen/Artsy_Tests/ReferenceImages/LiveAuctionBidHistoryViewControllerTests/cells__looks_right_for_open@2x.png 32 | /Users/orta/dev/ios/eigen/Artsy_Tests/View_Controller_Tests/Live_Auction/LiveAuctionBidHistoryViewControllerTests.swift:28: error: -[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_open_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionBidHistoryViewControllerTestsswift_21] : failed - , got snapshot nil successfully recorded, replace recordSnapshot with a check 33 | 34 | Test Case '-[Artsy_Tests.LiveAuctionBidHistoryViewControllerTests cells__looks_right_for_open_UsersortadevioseigenArtsyTestsViewControllerTestsLiveAuctionLiveAuctionBidHistoryViewControllerTestsswift_21]' failed (0.025 seconds). 35 | Test Suite 'LiveAuctionBidHistoryViewControllerTests' failed at 2016-03-09 21:44:01.357. 36 | Executed 1 test, with 1 failure (0 unexpected) in 0.025 (0.026) seconds 37 | -------------------------------------------------------------------------------- /design/Browse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/Snapshots-app/e09a2353ec44f9999d814a45d47c210222fdd51b/design/Browse.png -------------------------------------------------------------------------------- /design/Run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/Snapshots-app/e09a2353ec44f9999d814a45d47c210222fdd51b/design/Run.png -------------------------------------------------------------------------------- /design/snapshots-app-design.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orta/Snapshots-app/e09a2353ec44f9999d814a45d47c210222fdd51b/design/snapshots-app-design.sketch --------------------------------------------------------------------------------