@info "Running interactive tests" using PkgTemplates: @with_kw_noshow const CR = "\r" const LF = "\n" const UP = "\eOA" const DOWN = "\eOB" const ALL = "a" const NONE = "n" const DONE = "d" const SIGINT = "\x03" # Because the plugin selection dialog prints directly to stdin in the same way # as we do here, and our input prints happen first, we're going to need to insert # the plugin selection prints ourselves, and then "undo" the extra ones at the end # by consuming whatever is left in stdin. const NDEFAULTS = length(PT.default_plugins()) const SELECT_DEFAULTS = (CR * DOWN)^NDEFAULTS struct FromString s::String end @testset "Interactive mode" begin @testset "Input conversion" begin generic(T, x) = PT.convert_input(PT.Plugin, T, x) @test generic(String, "foo") == "foo" @test generic(Float64, "1.23") == 1.23 @test generic(Int, "01") == 1 @test generic(Bool, "yes") === true @test generic(Bool, "True") === true @test generic(Bool, "No") === false @test generic(Bool, "false") === false @test generic(Vector{Int}, "1, 2, 3") == [1, 2, 3] @test generic(Vector{String}, "a, b,c") == ["a", "b", "c"] @test generic(FromString, "hello") == FromString("hello") if VERSION < v"1.1" @test_broken generic(Union{String, Nothing}, "nothing") === nothing else @test generic(Union{String, Nothing}, "nothing") === nothing end @test_throws ArgumentError generic(Int, "hello") @test_throws ArgumentError generic(Float64, "hello") @test_throws ArgumentError generic(Bool, "hello") end @testset "input_tips" begin @test isempty(PT.input_tips(Int)) @test PT.input_tips(Vector{String}) == ["comma-delimited"] @test PT.input_tips(Union{Vector{String}, Nothing}) == ["'nothing' for nothing", "comma-delimited"] @test PT.input_tips(Union{String, Nothing}) == ["'nothing' for nothing"] @test PT.input_tips(Union{Vector{Secret}, Nothing}) == ["'nothing' for nothing", "comma-delimited", "name only"] end @testset "Interactive name/type pair collection" begin name = gensym() @eval begin PT.@plugin struct $name <: PT.Plugin x::Int = 0 y::String = "" end @test PT.interactive_pairs($name) == [:x => Int, :y => String] PT.customizable(::Type{$name}) = (:x => PT.NotCustomizable, :y => Float64, :z => Int) @test PT.interactive_pairs($name) == [:y => Float64, :z => Int] end end @testset "Simulated inputs" begin @testset "Default template" begin print( stdin.buffer, CR, # Select user DONE, # Finish menu USER, LF, # Enter user ) @test Template(; interactive=true) == Template(; user=USER) end @testset "Custom options, accept defaults" begin print( stdin.buffer, ALL, DONE, # Customize all fields "user", LF, # Enter user (don't assume we have default for this one). LF, # Enter authors LF, # Enter dir CR, # Enter host CR, # Enter julia SELECT_DEFAULTS, # Pre-select default plugins DONE, # Select no additional plugins DONE^NDEFAULTS, # Don't customize plugins ) @test Template(; interactive=true) == Template(; user="user") readavailable(stdin.buffer) end @testset "Custom options, custom values" begin nversions = VERSION.minor + 1 print( stdin.buffer, ALL, DONE, # Customize all fields "user", LF, # Enter user "a, b", LF, # Enter authors "~", LF, # Enter dir DOWN^3, CR, # Choose "Other" for host "x.com", LF, # Enter host DOWN^nversions, CR, # Choose "Other" for julia "0.7", LF, # Enter Julia version SELECT_DEFAULTS, # Pre-select default plugins DONE, # Select no additional plugins DONE^NDEFAULTS, # Don't customize plugins ) @test Template(; interactive=true) == Template(; user="user", authors=["a", "b"], dir="~", host="x.com", julia=v"0.7", ) readavailable(stdin.buffer) end @testset "Disabling default plugins" begin print( stdin.buffer, CR, DOWN^5, CR, DONE, # Customize user and plugins USER, LF, # Enter user SELECT_DEFAULTS, # Pre-select default plugins UP, CR, UP^2, CR, DONE, # Disable TagBot and Readme DONE^(NDEFAULTS - 2), # Don't customize plugins ) @test Template(; interactive=true) == Template(; user=USER, plugins=[!Readme, !TagBot], ) readavailable(stdin.buffer) end @testset "Plugins" begin print( stdin.buffer, ALL, DONE, # Customize all fields "true", LF, # Enable ARM64 "no", LF, # Disable coverage "1.1,v1.2", LF, # Enter extra versions "x.txt", LF, # Enter file "Yes", LF, # Enable Linux "false", LF, # Disable OSX "TRUE", LF, # Enable Windows "YES", LF, # Enable x64 "NO", LF, # Disable x86 ) @test PT.interactive(TravisCI) == TravisCI(; arm64=true, coverage=false, extra_versions=[v"1.1", v"1.2"], file="x.txt", linux=true, osx=false, windows=true, x64=true, x86=false, ) print( stdin.buffer, DOWN^2, CR, # Select GitLabCI DOWN, CR, DONE, # Customize index_md "x.txt", LF, # Enter index file ) @test PT.interactive(Documenter) == Documenter{GitLabCI}(; index_md="x.txt") print( stdin.buffer, CR, DOWN, CR, DONE, # Customize name and destination "COPYING", LF, # Enter destination CR, # Choose MIT for name (it's at the top) ) @test PT.interactive(License) == License(; destination="COPYING", name="MIT") end @testset "Union{T, Nothing} weirdness" begin print( stdin.buffer, DOWN, CR, DONE, # Customize changelog "hello", LF, # Enter changelog ) @test PT.interactive(TagBot) == TagBot(; changelog="hello") print( stdin.buffer, DOWN, CR, DONE, # Customize changelog "nothing", LF, # Set to null ) @test PT.interactive(TagBot) == TagBot(; changelog=nothing) end @testset "Only one field" begin print( stdin.buffer, DOWN, CR, DONE, # Select "None" option ) @test PT.interactive(Codecov) == Codecov() end @testset "Missing user" begin print( stdin.buffer, DONE, # Customize nothing "username", LF, # Enter user after it's prompted ) mock(PT.default_user => () -> "") do _du @test Template(; interactive=true) == Template(; user="username") end end @testset "Interrupts" begin print( stdin.buffer, SIGINT, # Send keyboard interrupt ) @test Template(; interactive=true) === nothing end println() end end