Skip to content

Commit

Permalink
Adding code for autocomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
jossmoff committed Oct 24, 2023
1 parent 9fe0802 commit 44fdc7f
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 6 deletions.
14 changes: 8 additions & 6 deletions bookshelf/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
from bookshelf.models import Chapter, Story
from bookshelf.exceptions import StoryAlreadyExistsException, StoryNotFoundException, ChapterInProgressException, \
StoryAlreadyFinishedException
from bookshelf.param_types import StoryType

bookshelf_storage = BookshelfStorage()
bookshelf_console = BookshelfConsole(bookshelf_storage)
story_type = StoryType(bookshelf_storage)


def entry_point():
Expand All @@ -26,7 +28,7 @@ def bookshelf():


@bookshelf.command(name='create')
@click.argument('story_name')
@click.argument('story_name', type=story_type)
@click.option('-f',
'--force',
type=str,
Expand Down Expand Up @@ -64,7 +66,7 @@ def create_story_entry(story_name: str, force: bool, tags: str, start_chapter: b


@bookshelf.command(name='start')
@click.argument('story_name')
@click.argument('story_name', type=story_type)
def start_chapter_entry(story_name):
"""Start a new chapter for a story on your bookshelf"""
try:
Expand All @@ -90,7 +92,7 @@ def start_chapter_entry(story_name):


@bookshelf.command(name='stop')
@click.argument('story_name')
@click.argument('story_name', type=story_type)
def stop_chapter_entry(story_name):
"""Stop the current chapter of a story on your bookshelf"""

Expand All @@ -108,7 +110,7 @@ def stop_chapter_entry(story_name):


@bookshelf.command(name='finish')
@click.argument('story_name')
@click.argument('story_name', type=story_type)
def finish_story_entry(story_name):
"""Finish writing a story on your bookshelf"""
try:
Expand Down Expand Up @@ -136,7 +138,7 @@ def list_stories_entry(with_tags: str):


@bookshelf.command(name='rm')
@click.argument('story_name')
@click.argument('story_name', type=story_type)
def remove_story_entry(story_name):
"""Remove a story from your bookshelf"""
try:
Expand All @@ -149,7 +151,7 @@ def remove_story_entry(story_name):


@bookshelf.command(name='info')
@click.argument('story_name')
@click.argument('story_name', type=story_type)
def story_info_entry(story_name: str):
"""Displays the information for a given story on your bookshelf"""
try:
Expand Down
18 changes: 18 additions & 0 deletions bookshelf/param_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from click import ParamType
from click.shell_completion import CompletionItem

from bookshelf.storage import BookshelfStorage


class StoryType(ParamType):
name = "story"

def __init__(self, bookshelf_storage: BookshelfStorage) -> None:
super().__init__()
self.bookshelf_storage = bookshelf_storage

def shell_complete(self, ctx, param, incomplete):
return [
CompletionItem(story.name, help=story.tags)
for story in self.bookshelf_storage.get_all_stories_matching_incomplete_name(incomplete)
]
5 changes: 5 additions & 0 deletions bookshelf/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,8 @@ def get_all_stories(self) -> Generator[Story, None, None]:
except json.JSONDecodeError:
# TODO: Maybe add some logs here that can be enabled with --logs
pass

def get_all_stories_matching_incomplete_name(self, incomplete_name: str) -> Generator[Story, None, None]:
for story in self.get_all_stories():
if incomplete_name in story.name:
yield story
44 changes: 44 additions & 0 deletions test/test_param_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from unittest.mock import MagicMock
from assertpy import assert_that
from click.shell_completion import CompletionItem

from bookshelf.param_types import StoryType
from utils.storage_utils import create_test_story


def test_story_type_init():
# Create a MagicMock object for BookshelfStorage
bookshelf_storage = MagicMock()
story_type = StoryType(bookshelf_storage)
assert_that(story_type.name).is_equal_to('story')
assert_that(story_type.bookshelf_storage).is_equal_to(bookshelf_storage)


def test_story_type_shell_complete_filters_stories():
# Create a MagicMock object for BookshelfStorage
bookshelf_storage = MagicMock()
story_type = StoryType(bookshelf_storage)

# Mock the behavior of bookshelf_storage.get_all_stories_matching_incomplete_name
bookshelf_storage.get_all_stories_matching_incomplete_name.return_value = [
create_test_story('SampleStory1'),
create_test_story('SampleStory2')
]

# Create MagicMock objects for ctx, param, and incomplete
ctx = MagicMock()
param = MagicMock()
incomplete = 'Sam'

# Call the shell_complete method
completions = story_type.shell_complete(ctx, param, incomplete)

# Assert that the completions are as expected
expected_completion_1 = CompletionItem('SampleStory1')
expected_completion_2 = CompletionItem('SampleStory2')

assert_that(len(completions)).is_equal_to(2)

for completion in completions:
assert_that(completion).is_instance_of(CompletionItem)
assert_that(completion.value).is_in(expected_completion_1.value, expected_completion_2.value)
26 changes: 26 additions & 0 deletions test/test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,29 @@ def test_get_all_stories_with_non_json(bookshelf_storage):
stories = list(bookshelf_storage.get_all_stories())

assert_that(stories).is_empty()


def test_get_all_stories_matching_incomplete_name_filters_out_value(bookshelf_storage, mock_home):
test_story_1 = create_test_story(name='Test1')
test_story_2 = create_test_story(name='Test2')
test_story_3 = create_test_story(name='Test3')
test_story_4 = create_test_story(name='does_not_match')
test_stories = [test_story_1, test_story_2, test_story_3, test_story_4]

os.walk = MagicMock()
os.walk.return_value = [(BOOKSHELF_TASK_DIR,
[],
[f'{story.name}.json' for story in test_stories])]

def side_effect(*args, **kwargs):
# Use a generator to yield contents for each call
for story in test_stories:
yield str(story.to_json()).replace('\'', '"')

m_open = mock_open()
m_open().read.side_effect = side_effect()

with patch('builtins.open', m_open):
stories = list(bookshelf_storage.get_all_stories_matching_incomplete_name('Test'))

assert_that(stories).contains_only(test_story_1, test_story_2, test_story_3)

0 comments on commit 44fdc7f

Please sign in to comment.