rendercv/tests/test_cli.py

556 lines
15 KiB
Python
Raw Normal View History

2024-02-11 18:36:56 +00:00
import os
2024-02-18 16:19:59 +00:00
import shutil
2024-05-19 10:55:01 +00:00
import subprocess
import sys
from datetime import date as Date
2024-02-11 18:36:56 +00:00
2024-02-09 19:32:58 +00:00
import pydantic
import ruamel.yaml
import pytest
2024-02-11 13:37:10 +00:00
import typer.testing
2024-05-17 12:29:31 +00:00
import rendercv.cli as cli
import rendercv.data_models as dm
from rendercv import __version__
2024-05-17 12:29:31 +00:00
def run_render_command(input_file_path, working_path, extra_arguments=[]):
# copy input file to the temporary directory to create the output directory there:
if not input_file_path == working_path / input_file_path.name:
shutil.copy(input_file_path, working_path)
# change the current working directory to the temporary directory:
os.chdir(working_path)
result = runner.invoke(cli.app, ["render", "John_Doe_CV.yaml"] + extra_arguments)
return result
2024-02-09 19:32:58 +00:00
def test_welcome():
2024-02-11 13:37:10 +00:00
cli.welcome()
2024-02-09 19:32:58 +00:00
def test_warning():
2024-02-11 13:37:10 +00:00
cli.warning("This is a warning message.")
2024-02-09 19:32:58 +00:00
def test_error():
2024-04-30 17:17:16 +00:00
with pytest.raises(typer.Exit):
cli.error("This is an error message.")
2024-02-09 19:32:58 +00:00
def test_information():
2024-02-11 13:37:10 +00:00
cli.information("This is an information message.")
2024-02-09 19:32:58 +00:00
def test_get_error_message_and_location_and_value_from_a_custom_error():
error_string = "('error message', 'location', 'value')"
2024-02-11 13:37:10 +00:00
result = cli.get_error_message_and_location_and_value_from_a_custom_error(
2024-02-09 19:32:58 +00:00
error_string
)
assert result == ("error message", "location", "value")
2024-02-14 18:50:00 +00:00
error_string = """("er'ror message", 'location', 'value')"""
result = cli.get_error_message_and_location_and_value_from_a_custom_error(
error_string
)
assert result == ("er'ror message", "location", "value")
2024-02-09 19:32:58 +00:00
error_string = "error message"
2024-02-11 13:37:10 +00:00
result = cli.get_error_message_and_location_and_value_from_a_custom_error(
2024-02-09 19:32:58 +00:00
error_string
)
2024-02-14 18:50:00 +00:00
assert result == (None, None, None)
2024-02-09 19:32:58 +00:00
2024-02-14 18:50:00 +00:00
@pytest.mark.parametrize(
"data_model_class, invalid_model",
[
(
dm.EducationEntry,
{
"area": "Mechanical Engineering",
"extra": "Extra",
},
),
(
dm.ExperienceEntry,
{
"company": "CERN",
},
),
(
dm.ExperienceEntry,
{
"position": "Researcher",
},
),
2024-05-19 10:55:01 +00:00
(
dm.ExperienceEntry,
{
"position": Date(2020, 10, 1),
},
),
2024-02-14 18:50:00 +00:00
(
dm.ExperienceEntry,
{
"company": "CERN",
"position": "Researcher",
"stat_date": "2023-12-08",
"end_date": "INVALID END DATE",
},
),
(
dm.PublicationEntry,
{
"doi": "10.1109/TASC.2023.3340648",
},
),
(
dm.ExperienceEntry,
{
"authors": ["John Doe", "Jane Doe"],
},
),
(
dm.OneLineEntry,
{
"name": "My One Line Entry",
},
),
(
dm.CurriculumVitae,
{
"name": "John Doe",
"sections": {
"education": [
{
"institution": "Boğaziçi University",
"area": "Mechanical Engineering",
"degree": "BS",
"date": "2028-12-08",
},
{
"degree": "BS",
},
]
},
},
),
],
)
def test_handle_validation_error(data_model_class, invalid_model):
try:
data_model_class(**invalid_model)
except pydantic.ValidationError as e:
2024-04-30 17:17:16 +00:00
with pytest.raises(typer.Exit):
cli.handle_validation_error(e)
2024-02-09 19:32:58 +00:00
@pytest.mark.parametrize(
"exception",
[
2024-02-24 19:48:34 +00:00
ruamel.yaml.YAMLError("message"),
RuntimeError("message"),
FileNotFoundError("message"),
ValueError("message"),
UnicodeDecodeError("utf-8", b"", 1, 2, "message"),
],
2024-02-09 19:32:58 +00:00
)
def test_handle_exceptions(exception):
2024-02-11 13:37:10 +00:00
@cli.handle_exceptions
2024-02-09 19:32:58 +00:00
def function_that_raises_exception():
2024-02-24 19:48:34 +00:00
raise exception
2024-04-30 17:52:33 +00:00
2024-04-30 17:17:16 +00:00
with pytest.raises(typer.Exit):
function_that_raises_exception()
2024-02-09 19:32:58 +00:00
def test_live_progress_reporter_class():
2024-02-11 13:37:10 +00:00
with cli.LiveProgressReporter(number_of_steps=3) as progress:
2024-02-09 19:32:58 +00:00
progress.start_a_step("Test step 1")
progress.finish_the_current_step()
progress.start_a_step("Test step 2")
progress.finish_the_current_step()
progress.start_a_step("Test step 3")
progress.finish_the_current_step()
2024-02-11 18:36:56 +00:00
2024-04-28 20:21:59 +00:00
@pytest.mark.parametrize(
"folder_name",
["markdown"] + dm.available_themes,
)
def test_copy_templates(tmp_path, folder_name):
copied_path = cli.copy_templates(
folder_name=folder_name,
copy_to=tmp_path,
)
assert copied_path.exists()
2024-04-30 17:02:53 +00:00
def test_copy_templates_with_new_folder_name(tmp_path):
copied_path = cli.copy_templates(
folder_name="markdown",
copy_to=tmp_path,
new_folder_name="new_folder",
)
assert copied_path.exists()
@pytest.mark.parametrize(
"folder_name",
["markdown"] + dm.available_themes,
)
def test_copy_templates_destinations_exist(tmp_path, folder_name):
(tmp_path / folder_name).mkdir()
copied_path = cli.copy_templates(
folder_name=folder_name,
copy_to=tmp_path,
)
assert copied_path is None
2024-02-11 18:36:56 +00:00
runner = typer.testing.CliRunner()
2024-02-18 16:19:59 +00:00
def test_render_command(tmp_path, input_file_path):
2024-05-17 12:29:31 +00:00
result = run_render_command(
input_file_path,
tmp_path,
)
2024-02-18 16:19:59 +00:00
2024-04-28 20:21:59 +00:00
output_folder_path = tmp_path / "rendercv_output"
pdf_file_path = output_folder_path / "John_Doe_CV.pdf"
latex_file_path = output_folder_path / "John_Doe_CV.tex"
markdown_file_path = output_folder_path / "John_Doe_CV.md"
html_file_path = output_folder_path / "John_Doe_CV_PASTETOGRAMMARLY.html"
png_file_path = output_folder_path / "John_Doe_CV_1.png"
assert output_folder_path.exists()
assert pdf_file_path.exists()
assert latex_file_path.exists()
assert markdown_file_path.exists()
assert html_file_path.exists()
assert png_file_path.exists()
assert "Your CV is rendered!" in result.stdout
2024-05-17 12:29:31 +00:00
def test_render_command_with_different_output_path(input_file_path, tmp_path):
result = run_render_command(
input_file_path,
tmp_path,
2024-04-28 20:21:59 +00:00
[
"--output-folder-name",
"test",
],
)
2024-05-17 12:29:31 +00:00
output_folder_path = tmp_path / "test"
2024-02-11 18:36:56 +00:00
assert result.exit_code == 0
2024-04-28 20:21:59 +00:00
assert output_folder_path.exists()
2024-02-11 18:36:56 +00:00
assert "Your CV is rendered!" in result.stdout
2024-05-17 12:29:31 +00:00
@pytest.mark.parametrize(
("option", "file_name"),
[
("--pdf-path", "test.pdf"),
("--latex-path", "test.tex"),
("--markdown-path", "test.md"),
("--html-path", "test.html"),
("--png-path", "test.png"),
],
)
def test_render_command_with_different_output_path_for_each_file(
option, file_name, tmp_path, input_file_path
):
result = run_render_command(
input_file_path,
tmp_path,
2024-04-28 20:21:59 +00:00
[
2024-05-17 12:29:31 +00:00
option,
file_name,
2024-04-28 20:21:59 +00:00
],
)
2024-05-17 12:29:31 +00:00
file_path = tmp_path / file_name
2024-04-28 20:21:59 +00:00
2024-05-17 12:29:31 +00:00
assert file_path.exists()
2024-04-28 20:21:59 +00:00
def test_render_command_with_custom_png_path_multiple_pages(tmp_path):
# create a new input file (for a CV with multiple pages) in the temporary directory:
os.chdir(tmp_path)
2024-05-25 13:06:17 +00:00
runner.invoke(cli.app, ["new", "John Doe"])
input_file_path = tmp_path / "John_Doe_CV.yaml"
2024-04-28 20:21:59 +00:00
2024-05-17 12:29:31 +00:00
run_render_command(
input_file_path,
tmp_path,
2024-04-28 20:21:59 +00:00
[
"--png-path",
2024-05-17 12:29:31 +00:00
"test.png",
2024-04-28 20:21:59 +00:00
],
)
png_page1_file_path = tmp_path / "test_1.png"
png_page2_file_path = tmp_path / "test_2.png"
assert png_page1_file_path.exists()
assert png_page2_file_path.exists()
2024-05-17 12:29:31 +00:00
@pytest.mark.parametrize(
("option", "file_name"),
[
("--dont-generate-markdown", "John_Doe_CV.md"),
("--dont-generate-html", "John_Doe_CV_PASTETOGRAMMARLY.html"),
("--dont-generate-png", "John_Doe_CV_1.png"),
],
)
def test_render_command_with_dont_generate_files(
tmp_path, input_file_path, option, file_name
):
result = run_render_command(
input_file_path,
tmp_path,
2024-04-28 20:21:59 +00:00
[
2024-05-17 12:29:31 +00:00
option,
2024-04-28 20:21:59 +00:00
],
)
2024-05-17 12:29:31 +00:00
file_path = tmp_path / "rendercv_output" / file_name
2024-04-28 20:21:59 +00:00
2024-05-17 12:29:31 +00:00
assert not file_path.exists()
2024-04-28 20:21:59 +00:00
2024-04-30 17:02:53 +00:00
2024-05-17 12:29:31 +00:00
def test_render_command_with_local_latex_command(tmp_path, input_file_path):
run_render_command(
input_file_path,
tmp_path,
2024-04-28 20:21:59 +00:00
[
2024-05-17 12:29:31 +00:00
"--use-local-latex-command",
"pdflatex",
2024-04-28 20:21:59 +00:00
],
)
2024-04-30 17:52:33 +00:00
@pytest.mark.parametrize(
"invalid_arguments",
[
["--keywithoutvalue"],
["--key", "value", "--keywithoutvalue"],
["keywithoutdashes", "value"],
["--cv.phone", "invalidphonenumber"],
["--cv.sections.arbitrary.10", "value"],
],
)
def test_render_command_with_invalid_arguments(
tmp_path, input_file_path, invalid_arguments
):
2024-05-17 12:29:31 +00:00
result = run_render_command(
input_file_path,
tmp_path,
invalid_arguments,
2024-04-30 17:52:33 +00:00
)
assert (
"There is a problem with the extra arguments!" in result.stdout
or "should start with double dashes!" in result.stdout
or "does not exist in the data model!" in result.stdout
or "There are some errors in the data model!" in result.stdout
)
2024-02-11 18:36:56 +00:00
def test_new_command(tmp_path):
# change the current working directory to the temporary directory:
os.chdir(tmp_path)
2024-05-25 12:53:12 +00:00
runner.invoke(cli.app, ["new", "Jahn Doe"])
2024-04-28 20:21:59 +00:00
markdown_source_files_path = tmp_path / "markdown"
theme_source_files_path = tmp_path / "classic"
2024-05-25 12:53:12 +00:00
input_file_path = tmp_path / "Jahn_Doe_CV.yaml"
2024-04-28 20:21:59 +00:00
assert markdown_source_files_path.exists()
assert theme_source_files_path.exists()
assert input_file_path.exists()
def test_new_command_with_invalid_theme(tmp_path):
# change the current working directory to the temporary directory:
os.chdir(tmp_path)
2024-05-25 12:53:12 +00:00
result = runner.invoke(cli.app, ["new", "Jahn Doe", "--theme", "invalid_theme"])
assert "The theme should be one of the following" in result.stdout
2024-05-17 12:29:31 +00:00
@pytest.mark.parametrize(
("option", "folder_name"),
[
("--dont-create-theme-source-files", "classic"),
("--dont-create-markdown-source-files", "markdown"),
],
)
def test_new_command_with_dont_create_files(tmp_path, option, folder_name):
2024-04-28 20:21:59 +00:00
# change the current working directory to the temporary directory:
os.chdir(tmp_path)
2024-05-25 12:53:12 +00:00
runner.invoke(cli.app, ["new", "Jahn Doe", option])
2024-04-28 20:21:59 +00:00
2024-05-17 12:29:31 +00:00
source_files_path = tmp_path / folder_name
2024-04-28 20:21:59 +00:00
2024-05-17 12:29:31 +00:00
assert not source_files_path.exists()
def test_new_command_with_only_input_file(tmp_path):
# change the current working directory to the temporary directory:
os.chdir(tmp_path)
runner.invoke(
cli.app,
[
"new",
2024-05-25 12:53:12 +00:00
"Jahn Doe",
"--dont-create-markdown-source-files",
"--dont-create-theme-source-files",
],
)
markdown_source_files_path = tmp_path / "markdown"
theme_source_files_path = tmp_path / "classic"
2024-05-25 12:53:12 +00:00
input_file_path = tmp_path / "Jahn_Doe_CV.yaml"
assert not markdown_source_files_path.exists()
assert not theme_source_files_path.exists()
assert input_file_path.exists()
2024-04-30 17:02:53 +00:00
2024-05-25 12:53:12 +00:00
@pytest.mark.parametrize(
"file_or_folder_name",
[
2024-05-25 13:06:17 +00:00
"Jahn_Doe_CV.yaml",
2024-05-25 12:53:12 +00:00
"markdown",
"classic",
],
)
def test_new_command_with_existing_files(tmp_path, file_or_folder_name):
# change the current working directory to the temporary directory:
os.chdir(tmp_path)
if file_or_folder_name == "Jahn_Doe_CV.yaml":
(tmp_path / file_or_folder_name).touch()
else:
(tmp_path / file_or_folder_name).mkdir()
result = runner.invoke(cli.app, ["new", "Jahn Doe"])
assert "already exists!" in result.stdout
2024-04-30 17:02:53 +00:00
@pytest.mark.parametrize(
"based_on",
dm.available_themes,
)
def test_create_theme_command(tmp_path, input_file_path, based_on):
# change the current working directory to the temporary directory:
os.chdir(tmp_path)
runner.invoke(cli.app, ["create-theme", "newtheme", "--based-on", based_on])
new_theme_source_files_path = tmp_path / "newtheme"
assert new_theme_source_files_path.exists()
# test if the new theme is actually working:
input_file_path = shutil.copy(input_file_path, tmp_path)
result = runner.invoke(
cli.app, ["render", str(input_file_path), "--design", "{'theme':'newtheme'}"]
)
output_folder_path = tmp_path / "rendercv_output"
pdf_file_path = output_folder_path / "John_Doe_CV.pdf"
latex_file_path = output_folder_path / "John_Doe_CV.tex"
markdown_file_path = output_folder_path / "John_Doe_CV.md"
html_file_path = output_folder_path / "John_Doe_CV_PASTETOGRAMMARLY.html"
png_file_path = output_folder_path / "John_Doe_CV_1.png"
assert output_folder_path.exists()
assert pdf_file_path.exists()
assert latex_file_path.exists()
assert markdown_file_path.exists()
assert html_file_path.exists()
assert png_file_path.exists()
assert "Your CV is rendered!" in result.stdout
2024-04-30 18:20:44 +00:00
def test_create_theme_command_invalid_based_on_theme(tmp_path):
result = runner.invoke(
cli.app, ["create-theme", "newtheme", "--based-on", "invalid_theme"]
)
assert "is not in the list of available themes" in result.stdout
def test_create_theme_command_theme_already_exists(tmp_path):
# change the current working directory to the temporary directory:
os.chdir(tmp_path)
(tmp_path / "newtheme").mkdir()
result = runner.invoke(cli.app, ["create-theme", "newtheme"])
assert "already exists!" in result.stdout
2024-05-19 10:55:01 +00:00
def test_main_file():
subprocess.run([sys.executable, "-m", "rendercv", "--help"], check=True)
def test_get_latest_version_number_from_pypi():
version = cli.get_latest_version_number_from_pypi()
assert isinstance(version, str)
def test_if_welcome_prints_new_version_available(monkeypatch):
monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: "99999")
import io
import contextlib
with contextlib.redirect_stdout(io.StringIO()) as f:
cli.welcome()
output = f.getvalue()
assert "A new version of RenderCV is available!" in output
def test_rendercv_version_when_there_is_a_new_version(monkeypatch):
monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: "99999")
result = runner.invoke(cli.app, ["--version"])
assert "A new version of RenderCV is available!" in result.stdout
def test_rendercv_version_when_there_is_not_a_new_version(monkeypatch):
monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: __version__)
result = runner.invoke(cli.app, ["--version"])
assert __version__ in result.stdout
def test_warn_if_new_version_is_available(monkeypatch):
monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: __version__)
assert not cli.warn_if_new_version_is_available()
monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: "999")
assert cli.warn_if_new_version_is_available()