1require 'xcodeproj'
2
3module Fastlane
4  module Actions
5    module SharedValues
6      GENERATE_TEST_SCHEME_CUSTOM_VALUE = :GENERATE_TEST_SCHEME_CUSTOM_VALUE
7    end
8
9    class GenerateTestSchemeAction < Action
10      def self.run(params)
11        targets_to_test = params[:targets].select { |target| !target.empty? }
12
13        scheme_name = params[:scheme_name]
14        generated_scheme_name = "#{scheme_name}_generated"
15
16        workspace_path = params[:workspace_path]
17        workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
18
19        scheme_project_path = workspace.schemes.find { |k, v| k == scheme_name }&.last
20        UI.user_error!("Couldn't find project for scheme '#{scheme_name}' in workspace '#{workspace_path}'") unless scheme_project_path
21
22        pods_project_path = workspace.schemes.find { |k, v| v.include?('Pods.xcodeproj') }&.last
23        UI.user_error!("Couldn't find project 'Pods.xcodeproj' in workspace '#{workspace_path}'") unless pods_project_path
24
25        main_project = Xcodeproj::Project.open(scheme_project_path)
26        pods_project = Xcodeproj::Project.open(pods_project_path)
27        UI.message "Found main project at: '#{main_project.path}'"
28        UI.message "Found pods project at: '#{pods_project.path}'"
29
30        scheme_path = File.join(main_project.path, 'xcshareddata', 'xcschemes', "#{scheme_name}.xcscheme")
31        UI.user_error!("Couldn't find scheme '#{scheme_name}' in project '#{scheme_project_path}'.
32          Make sure that the scheme is shared.") unless File.exist?(scheme_path)
33
34        scheme = Xcodeproj::XCScheme.new(scheme_path)
35        UI.message "Found scheme '#{scheme_name}' in project '#{main_project.path}'"
36
37        UI.message "Finding test targets in the Pods project..."
38        testables = pods_project.native_targets.select { |target|
39          target.test_target_type? == true && (targets_to_test.empty? || targets_to_test.include?(target.name))
40        }.map { |target|
41          UI.message "  - #{target.name}"
42          Xcodeproj::XCScheme::TestAction::TestableReference.new(target, main_project)
43        }
44
45        if testables.empty?
46          UI.user_error! "No test targets found in the Pods project" + (targets_to_test.empty? ? "" : " (filter: '#{targets_to_test.join(', ')}')") if testables.empty?
47        end
48
49        scheme.test_action.testables = testables
50        scheme.save_as(scheme_project_path, generated_scheme_name)
51        UI.message "Saved generated scheme '#{generated_scheme_name}' to project '#{scheme_project_path}'"
52
53        generated_scheme_name
54      end
55
56      #####################################################
57      # @!group Documentation
58      #####################################################
59
60      def self.description
61        "Generate a scheme containing all test targets from pods"
62      end
63
64      def self.details
65        "Creates a scheme aggegating all test targets from pod projects in given workspace.\nGenerated scheme is based on template scheme given as parameter."
66      end
67
68      def self.available_options
69        [
70          FastlaneCore::ConfigItem.new(key: :scheme_name,
71                                       description: "Initial scheme name for GenerateTestSchemeAction", # a short description of this parameter
72                                       verify_block: proc do |value|
73                                          UI.user_error!("No scheme for GenerateTestSchemeAction given, pass using `scheme_name: 'MyScheme'`") unless (value and not value.empty?)
74                                       end),
75          FastlaneCore::ConfigItem.new(key: :workspace_path,
76                                       description: "Path to workspace file", # a short description of this parameter
77                                       verify_block: proc do |value|
78                                          UI.user_error!("No workspace path given, pass using `workspace_path: 'path/to/file.xcworkspace'`") unless (value and not value.empty?)
79                                          UI.user_error!("Couldn't find workspace file at path '#{value}'") unless File.exist?(value)
80                                       end),
81          FastlaneCore::ConfigItem.new(key: :targets,
82                                       description: "List of targets to test. If not specified, all packages in the workspace will be tested",
83                                       is_string: false,
84                                       type: Array,
85                                       optional: true)
86        ]
87      end
88
89      def self.return_value
90        "Generated scheme name"
91      end
92
93      def self.is_supported?(platform)
94        platform == :ios
95      end
96    end
97  end
98end
99