copier/tests/test_prompt.py
Jairo Llopis 0441b86f0c
Refactor (#314)
* Refactor
* Fix #110.
* Rewrite test_config_exclude, test_config_exclude_overridden and test_config_include. These tests were badly designed, using a monkeypatch that would never happen in the real world, and actually producing false positives. I moved them to test_exclude.py and rewritten to test the what and not the how.
* Fix #214 by removing skip option. Relevant tests use the better skip_if_exists=["**"].
* Remove subdirectory flag from API/CLI. It was confusing and could lead to bad maintenance situations. Fixes #315.
* Remove extra_paths and fix #321
* Remember that you cannot use _copier_conf.src_path as a path now
* use dataclasses
* Create errors module, simplify some tests, fix many others
* Fix some tests, complete EnvOps removal
* Fix #214 and some tests related to it
* Reorder code
* Update docs and imports
* Modularize test_complex_questions
* Interlink worker and questionary a bit better
* Removal of Questionary class, which only had 1 meaningful method that is now merged into Worker to avoid circular dependencies.
* Fix #280 in a simple way: only user answers are type-casted inside API, and CLI transforms all `--data` using YAML always. Predictable.
* Use prereleases correctly.
* Reorder AnswersMap to have a more significative repr.
* Simpler cache for old `Question.get_choices()` method (renamed now).
* fix wrong test
* Fix test_subdirectory
* Fix test_tasks (and remove tests/demo_tasks)
* Fix path filter tests, and move it to test_exclude, where it belongs
* Make test_config pass
* Fix more wrongly designed tests
* Use cached_property backport if needed
* xfail test known to fail on mac
* force posix paths on windows
* Add typing_extensions for python < 3.8
* Sort dependencies in pyproject.toml
* Support python 3.6 str-to-datetime conversion
* Workaround https://bugs.python.org/issue43095
* xfail test_path_filter on windows
* Upgrade mkdocs and other dependencies to fix https://github.com/pawamoy/mkdocstrings/issues/222
* Add missing reference docs.
* Add workaround for https://github.com/pawamoy/mkdocstrings/pull/209
* Docs.
* Remove validators module
* Add workaround for https://github.com/pawamoy/mkdocstrings/issues/225
* Restore docs autorefs as explained in https://github.com/pawamoy/mkdocstrings/issues/226#issuecomment-775413562.
* Workaround https://github.com/pawamoy/pytkdocs/issues/86
2021-02-09 18:22:47 +00:00

330 lines
10 KiB
Python

from pathlib import Path
import pexpect
import pytest
import yaml
from plumbum import local
from plumbum.cmd import git
from .helpers import COPIER_PATH, Keyboard, build_file_tree
DEFAULT = object()
MARIO_TREE = {
"copier.yml": """\
in_love:
type: bool
default: yes
your_name:
type: str
default: Mario
help: If you have a name, tell me now.
your_enemy:
type: str
default: Bowser
secret: yes
help: Secret enemy name
what_enemy_does:
type: str
default: "[[ your_enemy ]] hates [[ your_name ]]"
""",
"[[ _copier_conf.answers_file ]].tmpl": "[[_copier_answers|to_nice_yaml]]",
}
@pytest.mark.parametrize("name", [DEFAULT, None, "Luigi"])
def test_copy_default_advertised(tmp_path_factory, spawn, name):
"""Test that the questions for the user are OK"""
template, subproject = (
tmp_path_factory.mktemp("template"),
tmp_path_factory.mktemp("subproject"),
)
with local.cwd(template):
build_file_tree(MARIO_TREE)
git("init")
git("add", ".")
git("commit", "-m", "v1")
git("tag", "v1")
git("commit", "--allow-empty", "-m", "v2")
git("tag", "v2")
with local.cwd(subproject):
# Copy the v1 template
args = []
if name is not DEFAULT:
args.append(f"--data=your_name={name}")
else:
name = "Mario" # Default in the template
name = str(name)
tui = spawn(
[COPIER_PATH, str(template), ".", "--vcs-ref=v1"] + args, timeout=10
)
# Check what was captured
tui.expect_exact(["in_love?", "Format: bool", "(Y/n)"])
tui.sendline()
tui.expect_exact(["Yes"])
if not args:
tui.expect_exact(
["If you have a name, tell me now.", "your_name?", "Format: str", name]
)
tui.sendline()
tui.expect_exact(["Secret enemy name", "your_enemy?", "Format: str", "******"])
tui.sendline()
tui.expect_exact(["what_enemy_does?", "Format: str", f"Bowser hates {name}"])
tui.sendline()
tui.expect_exact(pexpect.EOF)
assert "_commit: v1" in Path(".copier-answers.yml").read_text()
# Update subproject
git("init")
git("add", ".")
assert "_commit: v1" in Path(".copier-answers.yml").read_text()
git("commit", "-m", "v1")
tui = spawn([COPIER_PATH], timeout=20)
# Check what was captured
tui.expect_exact(["in_love?", "Format: bool", "(Y/n)"])
tui.sendline()
tui.expect_exact(
["If you have a name, tell me now.", "your_name?", "Format: str", name]
)
tui.sendline()
tui.expect_exact(["Secret enemy name", "your_enemy?", "Format: str", "Bowser"])
tui.sendline()
tui.expect_exact(["what_enemy_does?", "Format: str", f"Bowser hates {name}"])
tui.sendline()
tui.expect_exact(["Overwrite", ".copier-answers.yml", "[Y/n]"])
tui.sendline()
tui.expect_exact(pexpect.EOF)
assert "_commit: v2" in Path(".copier-answers.yml").read_text()
@pytest.mark.parametrize(
"question_1",
# All these values evaluate to true
(
True,
1,
1.1,
-1,
-0.001,
"1",
"1.1",
"1st",
"-1",
"0.1",
"00001",
"000.0001",
"-0001",
"-000.001",
"+001",
"+000.001",
"hello! 🤗",
),
)
@pytest.mark.parametrize(
"question_2_when, asks",
(
(True, True),
(False, False),
("trUe", True),
("faLse", False),
("Yes", True),
("nO", False),
("Y", True),
("N", False),
("on", True),
("off", False),
("~", False),
("NonE", False),
("nulL", False),
("[[ question_1 ]]", True),
("[[ not question_1 ]]", False),
("[% if question_1 %]YES[% endif %]", True),
("[% if question_1 %]FALSE[% endif %]", False),
),
)
def test_when(tmp_path_factory, question_1, question_2_when, spawn, asks):
"""Test that the 2nd question is skipped or not, properly."""
template, subproject = (
tmp_path_factory.mktemp("template"),
tmp_path_factory.mktemp("subproject"),
)
questions = {
"question_1": question_1,
"question_2": {"default": "something", "when": question_2_when},
}
build_file_tree(
{
template / "copier.yml": yaml.dump(questions),
template
/ "[[ _copier_conf.answers_file ]].tmpl": "[[ _copier_answers|to_nice_yaml ]]",
}
)
tui = spawn([COPIER_PATH, str(template), str(subproject)], timeout=10)
tui.expect_exact(["question_1?", f"Format: {type(question_1).__name__}", "(Y/n)"])
tui.sendline()
if asks:
tui.expect_exact(["question_2?", "Format: yaml"])
tui.sendline()
tui.expect_exact(pexpect.EOF)
answers = yaml.safe_load((subproject / ".copier-answers.yml").read_text())
assert answers == {
"_src_path": str(template),
"question_1": question_1,
"question_2": "something",
}
def test_placeholder(tmp_path_factory, spawn):
template, subproject = (
tmp_path_factory.mktemp("template"),
tmp_path_factory.mktemp("subproject"),
)
build_file_tree(
{
template
/ "copier.yml": yaml.dump(
{
"question_1": "answer 1",
"question_2": {
"type": "str",
"help": "write a list of answers",
"placeholder": "Write something like [[ question_1 ]], but better",
},
}
),
template
/ "[[ _copier_conf.answers_file ]].tmpl": "[[ _copier_answers|to_nice_yaml ]]",
}
)
tui = spawn([COPIER_PATH, str(template), str(subproject)], timeout=10)
tui.expect_exact(["question_1?", "Format: str", "answer 1"])
tui.sendline()
tui.expect_exact(
["question_2?", "Format: yaml", "Write something like answer 1, but better"]
)
tui.sendline()
tui.expect_exact(pexpect.EOF)
answers = yaml.safe_load((subproject / ".copier-answers.yml").read_text())
assert answers == {
"_src_path": str(template),
"question_1": "answer 1",
"question_2": None,
}
@pytest.mark.parametrize("type_", ("str", "yaml", "json"))
def test_multiline(tmp_path_factory, spawn, type_):
template, subproject = (
tmp_path_factory.mktemp("template"),
tmp_path_factory.mktemp("subproject"),
)
build_file_tree(
{
template
/ "copier.yml": yaml.dump(
{
"question_1": "answer 1",
"question_2": {"type": type_},
"question_3": {"type": type_, "multiline": True},
"question_4": {
"type": type_,
"multiline": "[[ question_1 == 'answer 1' ]]",
},
"question_5": {
"type": type_,
"multiline": "[[ question_1 != 'answer 1' ]]",
},
}
),
template
/ "[[ _copier_conf.answers_file ]].tmpl": "[[ _copier_answers|to_nice_yaml ]]",
}
)
tui = spawn([COPIER_PATH, str(template), str(subproject)], timeout=10)
tui.expect_exact(["question_1?", "Format: str", "answer 1"])
tui.sendline()
tui.expect_exact(["question_2?", f"Format: {type_}"])
tui.sendline('"answer 2"')
tui.expect_exact(["question_3?", f"Format: {type_}"])
tui.sendline('"answer 3"')
tui.send(Keyboard.Alt + Keyboard.Enter)
tui.expect_exact(["question_4?", f"Format: {type_}"])
tui.sendline('"answer 4"')
tui.send(Keyboard.Alt + Keyboard.Enter)
tui.expect_exact(["question_5?", f"Format: {type_}"])
tui.sendline('"answer 5"')
tui.expect_exact(pexpect.EOF)
answers = yaml.safe_load((subproject / ".copier-answers.yml").read_text())
if type_ == "str":
assert answers == {
"_src_path": str(template),
"question_1": "answer 1",
"question_2": '"answer 2"',
"question_3": ('"answer 3"\n'),
"question_4": ('"answer 4"\n'),
"question_5": ('"answer 5"'),
}
else:
assert answers == {
"_src_path": str(template),
"question_1": "answer 1",
"question_2": ("answer 2"),
"question_3": ("answer 3"),
"question_4": ("answer 4"),
"question_5": ("answer 5"),
}
@pytest.mark.parametrize(
"choices",
(
[1, 2, 3],
[["one", 1], ["two", 2], ["three", 3]],
{"one": 1, "two": 2, "three": 3},
),
)
def test_update_choice(tmp_path_factory, spawn, choices):
"""Choices are properly remembered and selected in TUI when updating."""
print(choices)
template, subproject = (
tmp_path_factory.mktemp("template"),
tmp_path_factory.mktemp("subproject"),
)
# Create template
build_file_tree(
{
template
/ "copier.yml": f"""
pick_one:
type: float
default: 3
choices: {choices}
""",
template
/ "[[ _copier_conf.answers_file ]].tmpl": "[[ _copier_answers|to_nice_yaml ]]",
}
)
with local.cwd(template):
git("init")
git("add", ".")
git("commit", "-m one")
git("tag", "v1")
# Copy
tui = spawn([COPIER_PATH, str(template), str(subproject)], timeout=10)
tui.expect_exact(["pick_one?"])
tui.sendline(Keyboard.Up)
tui.expect_exact(pexpect.EOF)
answers = yaml.safe_load((subproject / ".copier-answers.yml").read_text())
assert answers["pick_one"] == 2.0
with local.cwd(subproject):
git("init")
git("add", ".")
git("commit", "-m1")
# Update
tui = spawn([COPIER_PATH, str(subproject)], timeout=10)
tui.expect_exact(["pick_one?"])
tui.sendline(Keyboard.Down)
tui.expect_exact(["Overwrite", ".copier-answers.yml", "[Y/n]"])
tui.sendline("y")
tui.expect_exact(pexpect.EOF)
answers = yaml.safe_load((subproject / ".copier-answers.yml").read_text())
assert answers["pick_one"] == 3.0