Commit 5f7f19c2 authored by Fabio Pelosin's avatar Fabio Pelosin

[Core Extraction] Adapted UserProjectIntegrator (with clean up).

parent 21af2670
......@@ -8,20 +8,35 @@ module Pod
class Installer
# The {UserProjectIntegrator} integrates the libraries generated by
# TargetDefinitions of the {Podfile} with their correspondent user project.
# TargetDefinitions of the {Podfile} with their correspondent user
# projects.
#
class UserProjectIntegrator
include Pod::Config::Mixin
# @return [Podfile] the podfile that should be integrated with the user
# projects.
#
attr_reader :podfile
# @return [Project] the pods project which contains the libraries to
# integrate.
#
attr_reader :pods_project
# @return [Pathname] the path of the installation.
#
# TODO: This is only used to compute the workspace path in case that it
# should be inferred by the project. If the workspace should be in
# the same dir of the project, this could be removed.
#
attr_reader :project_root
# @param [Podfile] podfile @see #podfile.
#
def initialize(podfile)
def initialize(podfile, pods_project, project_root)
@podfile = podfile
@pods_project = pods_project
@project_root = project_root
end
# Integrates the user projects associated with the {TargetDefinitions}
......@@ -30,75 +45,117 @@ module Pod
# @return [void]
#
def integrate!
create_workspace!
target_integrators.map(&:integrate!)
create_workspace
integrate_user_targets
warn_about_empty_podfile
end
# creates the workspace containing the Pods project and the user projects
# should be saved.
#--------------------------------------#
# @!group Integration steps
private
# Creates and saved the workspace containing the Pods project and the
# user projects.
#
# @return [void]
#
def create_workspace!
def create_workspace
workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
[pods_project_path, *user_project_paths].each do |project_path|
project_path = project_path.relative_path_from(config.project_root).to_s
workspace << project_path unless workspace.include?(project_path)
[pods_project.path, *user_project_paths].each do |project_path|
path = project_path.relative_path_from(workspace_path.dirname).to_s
workspace << path unless workspace.include?(path)
end
unless workspace_path.exist?
UI.notice "From now on use `#{workspace_path.basename}'."
end
workspace.save_as(workspace_path)
UI.notice "From now on use `#{workspace_path.basename}'." unless workspace_path.exist?
end
# @return [Pathname] the path where the workspace containing the Pods
# project and the user projects should be saved.
# Integrates the targets of the user projects with the libraries
# generated from the {Podfile}.
#
def workspace_path
podfile.workspace || raise(Informative, "Could not automatically select an Xcode workspace. " \
"Specify one in your Podfile.")
# @note {TargetDefinition} without dependencies are skipped prevent
# creating empty libraries for targets definitions which are only
# wrappers for others.
#
# @return [void]
#
def integrate_user_targets
pods_project.libraries.each do |lib|
next if lib.target_definition.empty?
TargetIntegrator.new(lib).integrate!
end
end
# @return [Pathname] the path of the Pods project.
# Warns the user if the podfile is empty.
#
# @note The workspace is created in any case and all the user projects
# are added to it, however the projects are not integrated as
# there is no way to discern between target definitions which are
# empty and target definitions which just serve the purpose to
# wrap other ones. This is not an issue because empty target
# definitions generate empty libraries.
#
def pods_project_path
config.project_root + "Pods/Pods.xcodeproj"
# @return [void]
#
def warn_about_empty_podfile
unless podfile.target_definitions.values.any?{ |td| !td.empty? }
UI.warn "[!] The Podfile does not contain any dependency."
end
end
# @return [Array<TargetIntegrator>] the target integrators for the non
# empty target definitions.
#--------------------------------------#
# @!group Helpers.
public
# @return [Pathname] the path where the workspace containing the Pods
# project and the user projects should be saved.
#
def target_integrators
@target_integrators ||= @podfile.target_definitions.values.map do |definition|
TargetIntegrator.new(definition) unless definition.empty?
end.compact
def workspace_path
if podfile.workspace_path
podfile.workspace_path
elsif user_project_paths.count == 1
project = user_project_paths.first.basename('.xcodeproj')
project_root + "#{project}.xcworkspace"
else
raise Informative, "Could not automatically select an Xcode " \
"workspace. Specify one in your Podfile like so:\n\n" \
" workspace 'path/to/Workspace.xcworkspace'\n"
end
end
# @return [Array<Pathname>] the paths of all the user projects referenced
# by the target definitons.
# by the target definitions.
#
# @note Empty target definitions are ignored.
#
def user_project_paths
@podfile.target_definitions.values.map do |td|
next if td.empty?
td.user_project.path
pods_project.libraries.map do |lib|
# TODO: remove
next if lib.target_definition.empty?
lib.user_project_path
end.compact.uniq
end
#-------------------------------------------------------------------------#
#-----------------------------------------------------------------------#
# This class is responsible for integrating the library generated by a
# {TargetDefinition} with its destination project.
#
class TargetIntegrator
include Pod::Config::Mixin
# @return [TargetDefinition]
# the target definition whose library should be integrated.
# @return [Library] the library that should be integrated.
#
attr_reader :target_definition
attr_reader :library
# @param [TargetDefinition] target_definition @see #target_definition
# @param [Library] library @see #target_definition
#
def initialize(target_definition)
@target_definition = target_definition
def initialize(library)
@library = library
end
# Integrates the user project targets. Only the targets that do **not**
......@@ -109,108 +166,107 @@ module Pod
#
def integrate!
return if targets.empty?
message = "Integrating `#{target_definition.lib_name}' into " \
"#{'target'.pluralize(targets.size)} `#{targets.map(&:name).to_sentence}' " \
"of project #{UI.path user_project_path}."
UI.section(message) do
UI.section(integration_message) do
add_xcconfig_base_configuration
add_pods_library
add_copy_resources_script_phase
user_project.save_as(target_definition.user_project.path)
save_user_project
end
end
# @return [Xcodeproj::Project]
# the project that will be integrated.
# @return [Array<PBXNativeTarget>] the list of targets that the Pods
# lib that need to be integrated.
#
# @note A target is considered integrated if it already references
#
def targets
@targets ||= library.user_targets.reject do |t|
t.frameworks_build_phase.files.any? do |bf|
bf.file_ref.name == library.name
end
end
end
def user_project
@user_project ||= Xcodeproj::Project.new(user_project_path)
library.user_project
end
# Returns the path of the user project that the {TargetDefinition}
# should integrate.
#
# @raises If the project is implicit and there are multiple projects.
#
# @raises If the path doesn't exits.
#
# @return [Pathname] the path of the user project.
# @return [String] a string representation suitable for debugging.
#
def user_project_path
path = target_definition.user_project.path
unless path
raise Informative, "Could not automatically select an Xcode project.\n" \
"Specify one in your Podfile like so:\n\n xcodeproj 'path/to/NAME.xcodeproj'"
def inspect
"#<#{self.class} for target `#{target_definition.label}'>"
end
unless path.exist?
raise Informative, "The Xcode project `#{path}' does not exist."
end
#--------------------------------------#
path
end
# @!group Integration steps
# Returns a list of the targets from the project of {TargetDefinition}
# that needs to be integrated.
private
# Adds the `xcconfig` configurations files generated for the current
# {TargetDefinition} to the build configurations of the targets that
# should be integrated.
#
# The method first looks if there is a target specified with the
# `link_with` option of the {TargetDefinition}. Otherwise it looks for
# the target that has the same name of the target definition.
# Finally if no target was found the first encountered target is
# returned (it is assumed to be the one to integrate in simple
# projects).
# @note It also checks if any build setting of the build
# configurations overrides the `xcconfig` file and warns the
# user.
#
# In addition this will only return targets that do **not** already
# have the Pods library in their frameworks build phase.
# TODO: If the xcconfig is already set don't override it and inform
# the user.
#
# @return [Array<PBXNativeTarget>]
# the list of targets that the Pods lib should be linked with.
# @return [void]
#
def targets
unless @targets
if link_with = target_definition.link_with
targets = user_project.targets.select { |t| link_with.include? t.name }
raise Informative, "Unable to find a target named `#{link_with.to_sentence}` to link with target definition `#{target_definition.name}`" if targets.empty?
elsif target_definition.name != :default
target = user_project.targets.find { |t| t.name == target_definition.name.to_s }
targets = [target]
raise Informative, "Unable to find a target named `#{target_definition.name.to_s}`" unless target
else
targets = [user_project.targets.first]
raise Informative, "Unable to find a target" if targets.empty?
end
@targets = targets.reject do |target|
target.frameworks_build_phase.files.any? { |bf| bf.file_ref.name == target_definition.lib_name }
def add_xcconfig_base_configuration
xcconfig = user_project.new_file(library.xcconfig_relative_path)
targets.each do |target|
check_overridden_build_settings(library.xcconfig, target)
target.build_configurations.each do |config|
config.base_configuration_reference = xcconfig
end
end
@targets
end
#@!group Integration
# Adds the `xcconfig` configurations files generated for the current
# {TargetDefinition} to the build configurations of the targets that
# should be integrated.
# Adds a file reference to the library of the {TargetDefinition} and
# adds it to the frameworks build phase of the targets.
#
# It also checks if any build setting of the build configurations
# overrides the `xcconfig` file and warns the user.
# @return [void]
#
# TODO: If the xcconfig is already set don't override it and inform the
# user.
def add_pods_library
frameworks = user_project.frameworks_group
pods_library = frameworks.new_static_library(library.label)
targets.each do |target|
target.frameworks_build_phase.add_file_reference(pods_library)
end
end
# Adds a shell script build phase responsible to copy the resources
# generated by the TargetDefinition to the bundle of the product of the
# targets.
#
# @return [void]
#
def add_xcconfig_base_configuration
xcconfig_ref = user_project.new_file(target_definition.xcconfig_relative_path)
def add_copy_resources_script_phase
targets.each do |target|
check_overridden_build_settings(target_definition.xcconfig, target)
target.build_configurations.each do |config|
config.base_configuration_reference = xcconfig_ref
phase = target.new_shell_script_build_phase('Copy Pods Resources')
path = library.copy_resources_script_relative_path
phase.shell_script = %{"#{path}"\n}
end
end
# Saves the changes to the user project to the disk.
#
# @return [void]
#
def save_user_project
user_project.save_as(library.user_project_path)
end
#--------------------------------------#
# @!group Private helpers.
private
# Informs the user about any build setting of the target which might
# override the given xcconfig file.
#
......@@ -231,40 +287,25 @@ module Pod
configs_by_overridden_key.each do |key, config_names|
name = "#{target.name} [#{config_names.join(' - ')}]"
actions = [ "Use the `$(inherited)' flag, or", "Remove the build settings from the target." ]
UI.warn("The target `#{name}' overrides the `#{key}' build setting defined in `#{target_definition.xcconfig_relative_path}'.", actions)
end
actions = [
"Use the `$(inherited)' flag, or",
"Remove the build settings from the target."
]
UI.warn("The target `#{name}' overrides the `#{key}' build " \
"setting defined in `#{xcconfig_relative_path}'.",
actions)
end
end
# Adds a file reference to the library of the {TargetDefinition} and
# adds it to the frameworks build phase of the targets.
#
# @return [void]
#
def add_pods_library
frameworks = user_project.frameworks_group
pods_library = frameworks.new_static_library(target_definition.label)
targets.each do |target|
target.frameworks_build_phase.add_file_reference(pods_library)
end
end
# Adds a shell script build phase responsible to copy the resources
# generated by the TargetDefinition to the bundle of the product of the
# targets.
#
# @return [void]
#
def add_copy_resources_script_phase
targets.each do |target|
phase = target.new_shell_script_build_phase('Copy Pods Resources')
phase.shell_script = %{"#{target_definition.copy_resources_script_relative_path}"\n}
end
end
def inspect
"#<#{self.class} for target `#{target_definition.label}'>"
# @return [String] the message that should be displayed for the target
# integration.
#
def integration_message
"Integrating `#{library.name}' into " \
"#{'target'.pluralize(targets.size)} " \
"`#{targets.map(&:name).to_sentence}` " \
"of project #{UI.path library.user_project_path}."
end
end
end
......
require File.expand_path('../../spec_helper', __FILE__)
describe Pod::Installer::UserProjectIntegrator do
extend SpecHelper::TemporaryDirectory
before do
sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
config.project_root = sample_project_path.dirname
@podfile = Pod::Podfile.new do
platform :ios
xcodeproj sample_project_path, 'Test' => :debug
link_with 'SampleProject' # this is an app target!
pod 'JSONKit'
target :test_runner, :exclusive => true do
link_with 'TestRunner'
pod 'Kiwi'
end
end
@sample_project_path = sample_project_path
@integrator = Pod::Installer::UserProjectIntegrator.new(@podfile)
@integrator.integrate!
@sample_project = Xcodeproj::Project.new(sample_project_path)
end
it 'adds references to the Pods static libraries to the Frameworks group' do
@sample_project["Frameworks/libPods.a"].should.not == nil
@sample_project["Frameworks/libPods-test_runner.a"].should.not == nil
end
it 'creates a workspace with a name matching the project' do
workspace_path = @sample_project_path.dirname + "SampleProject.xcworkspace"
workspace_path.should.exist
end
it 'adds the project being integrated to the workspace' do
workspace = Xcodeproj::Workspace.new_from_xcworkspace(@sample_project_path.dirname + "SampleProject.xcworkspace")
workspace.projpaths.sort.should == %w{ Pods/Pods.xcodeproj SampleProject.xcodeproj }
end
it 'adds the Pods project to the workspace' do
workspace = Xcodeproj::Workspace.new_from_xcworkspace(@sample_project_path.dirname + "SampleProject.xcworkspace")
workspace.projpaths.find { |path| path =~ /Pods.xcodeproj/ }.should.not.be.nil
end
it 'sets the Pods xcconfig as the base config for each build configuration' do
@podfile.target_definitions.each do |_, definition|
target = @sample_project.targets.find { |t| t.name == definition.link_with.first }
xcconfig_file = @sample_project.files.find { |f| f.path == "Pods/#{definition.xcconfig_name}" }
target.build_configurations.each do |config|
config.base_configuration_reference.should == xcconfig_file
end
end
end
it 'adds the libPods static library to the "Link binary with libraries" build phase of each target' do
@podfile.target_definitions.each do |_, definition|
target = @sample_project.targets.find { |t| t.name == definition.link_with.first }
target.frameworks_build_phase.files.find { |f| f.file_ref.name == definition.lib_name}.should.not == nil
end
end
it 'adds a Copy Pods Resources build phase to each target' do
@podfile.target_definitions.each do |_, definition|
target = @sample_project.targets.find { |t| t.name == definition.link_with.first }
phase = target.shell_script_build_phases.find { |bp| bp.name == "Copy Pods Resources" }
phase.shell_script.strip.should == "\"${SRCROOT}/Pods/#{definition.copy_resources_script_name}\""
end
end
before do
# Reset the cached TargetIntegrator#targets lists.
@integrator.instance_variable_set(:@target_integrators, nil)
end
it "only tries to integrate Pods libraries into user targets that haven't been integrated yet" do
app_integrator = @integrator.target_integrators.find { |t| t.target_definition.name == :default }
test_runner_integrator = @integrator.target_integrators.find { |t| t.target_definition.name == :test_runner }
# Remove libPods.a from the app target. But don't do it through TargetIntegrator#targets,
# as it will return only those that still need integration.
app_target = app_integrator.user_project.targets.find { |t| t.name == 'SampleProject' }
app_target.frameworks_build_phase.files.last.remove_from_project
app_integrator.expects(:add_pods_library)
test_runner_integrator.expects(:add_pods_library).never
@integrator.integrate!
end
it "does not even try to save the project if none of the target integrators had any work to do" do
@integrator.target_integrators.first.user_project.expects(:save_as).never
@integrator.integrate!
end
end
require File.expand_path('../../../spec_helper', __FILE__)
describe Pod::Installer::UserProjectIntegrator do
extend SpecHelper::TemporaryDirectory
describe UserProjectIntegrator = Pod::Installer::UserProjectIntegrator do
describe "In general" do
extend SpecHelper::TemporaryDirectory
before do
@sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
config.project_root = @sample_project_path.dirname
sample_project_path = @sample_project_path
@podfile = Pod::Podfile.new do
......@@ -18,61 +18,185 @@ describe Pod::Installer::UserProjectIntegrator do
end
end
@integrator = Pod::Installer::UserProjectIntegrator.new(@podfile)
@project_root = @sample_project_path.dirname
@pods_project = Pod::Project.new(config.sandbox)
@integrator = UserProjectIntegrator.new(@podfile, @pods_project, @project_root)
@podfile.target_definitions.values.each { |td| @pods_project.add_pod_library(td) }
end
it "returns the podfile" do
@integrator.podfile.should == @podfile
end
it "returns the pods project" do
@integrator.pods_project.should == @pods_project
end
it "returns the path to the workspace from the Podfile" do
@integrator.workspace_path.should == config.project_root + 'SampleProject.xcworkspace'
it "returns the project root" do
@integrator.project_root.should == @project_root
end
it "uses the path of the workspace defined in the podfile" do
path = "a_path"
@podfile.workspace path
@integrator.workspace_path.should == path + ".xcworkspace"
end
it "names the workspace after the user project if needed" do
@integrator.workspace_path.should == @sample_project_path.dirname + 'SampleProject.xcworkspace'
end
it "raises if no workspace could be selected" do
@podfile.stubs(:workspace)
@integrator.expects(:user_project_paths).returns(%w[ project1 project2 ])
lambda { @integrator.workspace_path }.should.raise Pod::Informative
end
it "returns the path to the Pods.xcodeproj document" do
@integrator.pods_project_path.should == config.project_root + 'Pods/Pods.xcodeproj'
it "returns the paths of the user projects" do
@integrator.user_project_paths.should == [ @sample_project_path ]
end
it "returns a Pod::Installer::UserProjectIntegrator::Target for each target definition in the Podfile" do
@integrator.target_integrators.map(&:target_definition).should == @podfile.target_definitions.values
end
#--------------------------------------#
describe "Integration" do
extend SpecHelper::TemporaryDirectory
before do
@target_integrator = @integrator.target_integrators.first
@sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
sample_project_path = @sample_project_path
@podfile = Pod::Podfile.new do
platform :ios
xcodeproj sample_project_path
pod 'JSONKit'
end
@project_root = @sample_project_path.dirname
@pods_project = Pod::Project.new(config.sandbox)
@integrator = UserProjectIntegrator.new(@podfile, @pods_project, @project_root)
@podfile.target_definitions.values.each { |td| @pods_project.add_pod_library(td) }
@integrator.integrate!
end
it "returns the the user's project, that contains the target, from the Podfile" do
@target_integrator.user_project_path.should == @sample_project_path
@target_integrator.user_project.should == Xcodeproj::Project.new(@sample_project_path)
it "adds the project being integrated to the workspace" do
workspace = Xcodeproj::Workspace.new_from_xcworkspace(@integrator.workspace_path)
workspace.projpaths.sort.should == %w{ ../Pods/Pods.xcodeproj SampleProject.xcodeproj }
end
it "raises if no project could be selected" do
@target_integrator.target_definition.user_project.stubs(:path).returns(nil)
lambda { @target_integrator.user_project_path }.should.raise Pod::Informative
it "adds the Pods project to the workspace" do
workspace = Xcodeproj::Workspace.new_from_xcworkspace(@integrator.workspace_path)
workspace.projpaths.find { |path| path =~ /Pods.xcodeproj/ }.should.not.be.nil
end
it "raises if the project path doesn't exist" do
@target_integrator.target_definition.user_project.path.stubs(:exist?).returns(false)
lambda { @target_integrator.user_project_path }.should.raise Pod::Informative
xit "warns if the podfile does not contain any dependency" do
Pod::UI.output.should.include?('The Podfile does not contain any dependency')
end
end
end
#-----------------------------------------------------------------------------#
describe TargetIntegrator = Pod::Installer::UserProjectIntegrator::TargetIntegrator do
it "uses the target with the same name if the name is different from `:default'" do
target_integrator = @integrator.target_integrators[1]
target_integrator.target_definition.stubs(:name).returns('TestRunner')
target_integrator.target_definition.stubs(:link_with).returns(nil)
target_integrator.targets.first.name.should == 'TestRunner'
describe "In general" do
before do
@sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
sample_project_path = @sample_project_path
@podfile = Pod::Podfile.new do
platform :ios
xcodeproj sample_project_path
end
@pods_project = Pod::Project.new(config.sandbox)
@podfile.target_definitions.values.each { |td| @pods_project.add_pod_library(td) }
@library = @pods_project.libraries.first
@target_integrator = TargetIntegrator.new(@library)
end
it "it raises if it can't find a target with the same name" do
target_integrator = @integrator.target_integrators.find { |ti| ti.target_definition.name == :test_runner }
target_integrator.target_definition.stubs(:link_with).returns(nil)
lambda { target_integrator.targets }.should.raise Pod::Informative
it "returns the Pod library that should be integrated" do
@target_integrator.library.should == @library
end
it "returns the user's project, that contains the target, from the Podfile" do
@target_integrator.user_project.should == Xcodeproj::Project.new(@sample_project_path)
end
it "uses the first target in the user's project if no explicit target is specified" do
target_integrator = @integrator.target_integrators.find { |ti| ti.target_definition.name == :default }
target_integrator.target_definition.stubs(:link_with).returns(nil)
target_integrator.targets.should == [Xcodeproj::Project.new(@sample_project_path).targets.first]
end
# before do
# sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
# config.project_root = sample_project_path.dirname
# @podfile = Pod::Podfile.new do
# platform :ios
# xcodeproj sample_project_path, 'Test' => :debug
# link_with 'SampleProject' # this is an app target!
# pod 'JSONKit'
#
# target :test_runner, :exclusive => true do
# link_with 'TestRunner'
# pod 'Kiwi'
# end
# end
# @sample_project_path = sample_project_path
# pods_project = Pod::Project.new(config.sandbox)
# @integrator = Pod::Installer::UserProjectIntegrator.new(@podfile, pods_project)
# @integrator.integrate!
# @sample_project = Xcodeproj::Project.new(sample_project_path)
# end
#
# it 'adds references to the Pods static libraries to the Frameworks group' do
# @sample_project["Frameworks/libPods.a"].should.not == nil
# @sample_project["Frameworks/libPods-test_runner.a"].should.not == nil
# end
#
#
# it 'sets the Pods xcconfig as the base config for each build configuration' do
# @podfile.target_definitions.each do |_, definition|
# target = @sample_project.targets.find { |t| t.name == definition.link_with.first }
# xcconfig_file = @sample_project.files.find { |f| f.path == "Pods/#{definition.xcconfig_name}" }
# target.build_configurations.each do |config|
# config.base_configuration_reference.should == xcconfig_file
# end
# end
# end
#
# it 'adds the libPods static library to the "Link binary with libraries" build phase of each target' do
# @podfile.target_definitions.each do |_, definition|
# target = @sample_project.targets.find { |t| t.name == definition.link_with.first }
# target.frameworks_build_phase.files.find { |f| f.file_ref.name == definition.lib_name}.should.not == nil
# end
# end
#
# it 'adds a Copy Pods Resources build phase to each target' do
# @podfile.target_definitions.each do |_, definition|
# target = @sample_project.targets.find { |t| t.name == definition.link_with.first }
# phase = target.shell_script_build_phases.find { |bp| bp.name == "Copy Pods Resources" }
# phase.shell_script.strip.should == "\"${SRCROOT}/Pods/#{definition.copy_resources_script_name}\""
# end
# end
#
# before do
# # Reset the cached TargetIntegrator#targets lists.
# @integrator.instance_variable_set(:@target_integrators, nil)
# end
#
# it "only tries to integrate Pods libraries into user targets that haven't been integrated yet" do
# app_integrator = @integrator.target_integrators.find { |t| t.target_definition.name == :default }
# test_runner_integrator = @integrator.target_integrators.find { |t| t.target_definition.name == :test_runner }
#
# # Remove libPods.a from the app target. But don't do it through TargetIntegrator#targets,
# # as it will return only those that still need integration.
# app_target = app_integrator.user_project.targets.find { |t| t.name == 'SampleProject' }
# app_target.frameworks_build_phase.files.last.remove_from_project
#
# app_integrator.expects(:add_pods_library)
# test_runner_integrator.expects(:add_pods_library).never
#
# @integrator.integrate!
# end
#
# it "does not even try to save the project if none of the target integrators had any work to do" do
# @integrator.target_integrators.first.user_project.expects(:save_as).never
# @integrator.integrate!
# end
# end
#
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment