This commit is contained in:
Lia Launtz 2026-07-10 21:51:17 +05:00 committed by GitHub
commit 4770bd2b1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 140 additions and 0 deletions

140
tests/test_command_edit.py Normal file
View File

@ -0,0 +1,140 @@
from __future__ import annotations
import os
from contextlib import suppress
from pathlib import Path
from unittest import mock
import pytest
from scrapy.cmdline import ScrapyArgumentParser
from scrapy.commands.edit import Command
from scrapy.utils.project import get_project_settings
from tests.test_commands import TestProjectBase
from tests.utils.cmdline import call, proc
class TestEditCommand(TestProjectBase):
"""unit testing of the 'scrapy edit' command"""
@pytest.fixture
def create_spider(self, proj_path: Path):
"""creates spider needed for tests"""
# setup: add "cat" as test environment editor
editor_to_restore = None
if "EDITOR" in os.environ:
editor_to_restore = os.environ["EDITOR"]
os.environ["EDITOR"] = "cat"
# setup: preserve scrapy settings in local environment
scrapy_settings_to_restore = None
if "SCRAPY_SETTINGS_MODULE" in os.environ:
scrapy_settings_to_restore = os.environ["SCRAPY_SETTINGS_MODULE"]
# setup: create spider to edit
test_name = "test_name"
call("genspider", test_name, "test.com", cwd=proj_path)
spider = proj_path / self.project_name / "spiders" / "test_name.py"
try:
yield proj_path, spider, test_name
finally:
# teardown: remove spider from project
Path.unlink(spider)
# teardown: restore previous editor
if editor_to_restore is not None:
os.environ["EDITOR"] = editor_to_restore
else:
# remove editor from os.environ if it exists
with suppress(KeyError):
os.environ.pop("EDITOR")
# teardown: restore project settings in local environment
if scrapy_settings_to_restore is not None:
os.environ["SCRAPY_SETTINGS_MODULE"] = scrapy_settings_to_restore
else:
# remove "SCRAPY_SETTINGS_MODULE" from os.environ if it exists
with suppress(KeyError):
os.environ.pop("SCRAPY_SETTINGS_MODULE")
def test_edit_valid_spider(self, create_spider) -> None:
"""test call to edit command with correct spider name"""
proj_path, spider, test_name = create_spider
assert spider.exists()
assert call("edit", test_name, cwd=proj_path) == 0
def test_edit_nospider(self, proj_path: Path) -> None:
"""test call to edit if no spider has been specified"""
assert call("edit", "not_a_valid_spider", cwd=proj_path) == 1
def test_edit_short_desc(self, proj_path: Path) -> None:
"""Check that short description included in scrapy -h"""
rtn_code, out, _ = proc("-h", cwd=proj_path)
assert rtn_code == 0
cmd = Command()
assert cmd.short_desc() in out
def test_edit_command_valid_directory(self, create_spider):
"""calls editor command directly from project directory"""
proj_path, spider, test_name = create_spider
failures = []
# change into cwd
current = Path.cwd()
os.chdir(proj_path)
try:
# create edit command object
# teardown required as get_project_settings() mutates os.environ
cmd = Command()
cmd.settings = get_project_settings()
# grabs system editor to mock
editor = cmd.settings.get("EDITOR")
# parse commandline arguments
parser = ScrapyArgumentParser()
opts, _ = parser.parse_known_args(["edit", test_name])
with mock.patch(
"scrapy.commands.edit.os.system", return_value=0
) as mock_sys:
cmd.run([test_name], opts)
mock_sys.assert_called_once_with(f'{editor} "{spider}"')
# catch failure so we can restore cwd
except AssertionError as e:
failures.append(e)
finally:
# restore previous cwd
os.chdir(current)
# report test failure
if failures:
pytest.fail(f"{failures[0]}")
def test_edit_as_subprocess(self, create_spider):
"""check that subprocess calls editor"""
proj_path, _, test_name = create_spider
spider_text = """
class TestNameSpider(scrapy.Spider):
name = "test_name"
"""
_, out, _ = proc("edit", test_name, cwd=proj_path)
assert spider_text in out
def test_edit_subprocess_no_project(self, create_spider):
"""check that subprocess does not call editor outside project"""
proj_path, _, test_name = create_spider
no_proj = """
The edit command is not available from this location.
These commands are only available from within a project: check, crawl, edit, list, parse.
"""
_, out, _ = proc("edit", test_name, cwd=proj_path.parent)
assert no_proj in out