diff --git a/rtv/__main__.py b/rtv/__main__.py index 2992d8ff..bfe4d9a6 100755 --- a/rtv/__main__.py +++ b/rtv/__main__.py @@ -41,6 +41,7 @@ from .terminal import Terminal from .content import RequestHeaderRateLimiter from .objects import curses_session, patch_webbrowser +from .page import PageStack from .subreddit_page import SubredditPage from .submission_page import SubmissionPage from .exceptions import ConfigError, SubredditError, SubmissionError @@ -233,8 +234,9 @@ def main(): except Exception as e: _logger.exception(e) raise SubmissionError('Unable to load {0}'.format(url)) - while page: - page = page.loop() + PageStack.add(page) + ps = PageStack() + ps.run() page = None name = config['subreddit'] @@ -251,8 +253,9 @@ def main(): raise SubredditError('Unable to load {0}'.format(name)) # Launch the subreddit page - while page: - page = page.loop() + PageStack.add(page) + ps = PageStack() + ps.run() except ConfigError as e: _logger.exception(e) diff --git a/rtv/inbox_page.py b/rtv/inbox_page.py index 7968c4d9..f9ed5240 100644 --- a/rtv/inbox_page.py +++ b/rtv/inbox_page.py @@ -25,22 +25,6 @@ def __init__(self, reddit, term, config, oauth, content_type='all'): self.nav = Navigator(self.content.get) self.content_type = content_type - def handle_selected_page(self): - """ - Open the subscription and submission pages subwindows, but close the - current page if any other type of page is selected. - """ - if not self.selected_page: - pass - if self.selected_page.name in ('subscription', 'submission'): - # Launch page in a subwindow - self.selected_page = self.selected_page.loop() - elif self.selected_page.name in ('subreddit', 'inbox'): - # Replace the current page - self.active = False - else: - raise RuntimeError(self.selected_page.name) - @logged_in def refresh_content(self, order=None, name=None): """ @@ -108,13 +92,6 @@ def inbox_reply(self): """ self.reply() - @InboxController.register(Command('INBOX_EXIT')) - def close_inbox(self): - """ - Close inbox and return to the previous page. - """ - self.active = False - @InboxController.register(Command('INBOX_VIEW_CONTEXT')) @logged_in def view_context(self): @@ -123,7 +100,7 @@ def view_context(self): """ url = self.get_selected_item().get('context') if url: - self.selected_page = self.open_submission_page(url) + self.open_submission_page(url) @InboxController.register(Command('INBOX_OPEN_SUBMISSION')) @logged_in @@ -133,7 +110,7 @@ def open_submission(self): """ url = self.get_selected_item().get('submission_permalink') if url: - self.selected_page = self.open_submission_page(url) + self.open_submission_page(url) def _draw_item(self, win, data, inverted): diff --git a/rtv/page.py b/rtv/page.py index bf21377c..b7941125 100644 --- a/rtv/page.py +++ b/rtv/page.py @@ -38,6 +38,51 @@ class PageController(Controller): character_map = {} +class PageStack(object): + stack = [] + + def __init__(self, max_size=20): + self.max_size = max_size + + @staticmethod + def add(page): + PageStack.stack.append(page) + + @staticmethod + def init(page=None): + if page: + PageStack.stack = [page] + else: + PageStack.stack = [] + + @staticmethod + def pop(): + return PageStack.stack.pop() + + @staticmethod + def size(): + return len(PageStack.stack) + + @staticmethod + def current_page(): + return PageStack.stack[-1] + + def run(self): + """ + Runs the PageStack. It takes the most recently added page and calls + the calls the pages' wait method to draw the page and wait for + user input. + """ + while PageStack.stack: + self._stay_within_max_size() + page = PageStack.current_page() + page.wait() + + def _stay_within_max_size(self): + if len(PageStack.stack) > self.max_size: + PageStack.stack = PageStack.stack[1:] + + class Page(object): BANNER = None @@ -52,8 +97,6 @@ def __init__(self, reddit, term, config, oauth): self.nav = None self.controller = None - self.active = True - self.selected_page = None self._row = 0 self._subwindows = None @@ -69,68 +112,13 @@ def get_selected_item(self): """ return self.content.get(self.nav.absolute_index) - def loop(self): + def wait(self): """ - Main control loop runs the following steps: - 1. Re-draw the screen - 2. Wait for user to press a key (includes terminal resizing) - 3. Trigger the method registered to the input key - 4. Check if there are any nested pages that need to be looped over - - The loop will run until self.active is set to False from within one of - the methods. + Draw the page and wait for user input. """ - self.active = True - - # This needs to be called once before the main loop, in case a subpage - # was pre-selected before the loop started. This happens in __main__.py - # with ``page.open_submission(url=url)`` - while self.selected_page and self.active: - self.handle_selected_page() - - while self.active: - self.draw() - ch = self.term.stdscr.getch() - self.controller.trigger(ch) - - while self.selected_page and self.active: - self.handle_selected_page() - - return self.selected_page - - def handle_selected_page(self): - """ - Some commands will result in an action that causes a new page to open. - Examples include selecting a submission, viewing subscribed subreddits, - or opening the user's inbox. With these commands, the newly selected - page will be pre-loaded and stored in ``self.selected_page`` variable. - It's up to each page type to determine what to do when another page is - selected. - - - It can start a nested page.loop(). This would allow the user to - return to their previous screen after exiting the sub-page. For - example, this is what happens when opening an individual submission - from within a subreddit page. When the submission is closed, the - user resumes the subreddit that they were previously viewing. - - - It can close the current self.loop() and bubble the selected page up - one level in the loop stack. For example, this is what happens when - the user opens their subscriptions and selects a subreddit. The - subscription page loop is closed and the selected subreddit is - bubbled up to the root level loop. - - Care should be taken to ensure the user can never enter an infinite - nested loop, as this could lead to memory leaks and recursion errors. - - # Example of an unsafe nested loop - subreddit_page.loop() - -> submission_page.loop() - -> subreddit_page.loop() - -> submission_page.loop() - ... - - """ - raise NotImplementedError + self.draw() + ch = self.term.stdscr.getch() + self.controller.trigger(ch) @PageController.register(Command('REFRESH')) def reload_page(self): @@ -240,6 +228,13 @@ def move_page_bottom(self): self.nav.cursor_index = 0 self.nav.inverted = True + @PageController.register(Command('RETURN')) + def page_return(self): + if PageStack.size() == 1: + self.term.flash() + else: + PageStack.pop() + @PageController.register(Command('UPVOTE')) @logged_in def upvote(self): @@ -476,7 +471,7 @@ def send_private_message(self): raise TemporaryFileError() else: self.term.show_notification('Message sent!') - self.selected_page = self.open_inbox_page('sent') + self.open_inbox_page('sent') def prompt_and_select_link(self): """ @@ -551,7 +546,7 @@ def subscriptions(self): """ View a list of the user's subscribed subreddits """ - self.selected_page = self.open_subscription_page('subreddit') + self.open_subscription_page('subreddit') @PageController.register(Command('MULTIREDDITS')) @logged_in @@ -559,7 +554,7 @@ def multireddits(self): """ View a list of the user's subscribed multireddits """ - self.selected_page = self.open_subscription_page('multireddit') + self.open_subscription_page('multireddit') @PageController.register(Command('PROMPT')) def prompt(self): @@ -589,7 +584,7 @@ def inbox(self): """ View the user's inbox. """ - self.selected_page = self.open_inbox_page('all') + self.open_inbox_page('all') def open_inbox_page(self, content_type): """ @@ -601,6 +596,7 @@ def open_inbox_page(self, content_type): page = InboxPage(self.reddit, self.term, self.config, self.oauth, content_type=content_type) if not self.term.loader.exception: + PageStack.add(page) return page def open_subscription_page(self, content_type): @@ -613,6 +609,7 @@ def open_subscription_page(self, content_type): page = SubscriptionPage(self.reddit, self.term, self.config, self.oauth, content_type=content_type) if not self.term.loader.exception: + PageStack.add(page) return page def open_submission_page(self, url=None, submission=None): @@ -625,6 +622,7 @@ def open_submission_page(self, url=None, submission=None): page = SubmissionPage(self.reddit, self.term, self.config, self.oauth, url=url, submission=submission) if not self.term.loader.exception: + PageStack.add(page) return page def open_subreddit_page(self, name): @@ -637,6 +635,7 @@ def open_subreddit_page(self, name): page = SubredditPage(self.reddit, self.term, self.config, self.oauth, name) if not self.term.loader.exception: + PageStack.add(page) return page def clear_input_queue(self): diff --git a/rtv/submission_page.py b/rtv/submission_page.py index 6f5db44e..33cf7576 100644 --- a/rtv/submission_page.py +++ b/rtv/submission_page.py @@ -34,22 +34,6 @@ def __init__(self, reddit, term, config, oauth, url=None, submission=None): # Start at the submission post, which is indexed as -1 self.nav = Navigator(self.content.get, page_index=-1) - def handle_selected_page(self): - """ - Open the subscription page in a subwindow, but close the current page - if any other type of page is selected. - """ - if not self.selected_page: - pass - elif self.selected_page.name == 'subscription': - # Launch page in a subwindow - self.selected_page = self.selected_page.loop() - elif self.selected_page.name in ('subreddit', 'submission', 'inbox'): - # Replace the current page - self.active = False - else: - raise RuntimeError(self.selected_page.name) - def refresh_content(self, order=None, name=None): """ Re-download comments and reset the page index @@ -110,13 +94,6 @@ def toggle_comment(self): self.nav.flip(len(self._subwindows) - 1) self.nav.top_item_height = n_rows - @SubmissionController.register(Command('SUBMISSION_EXIT')) - def exit_submission(self): - """ - Close the submission and return to the subreddit page - """ - self.active = False - @SubmissionController.register(Command('SUBMISSION_OPEN_IN_BROWSER')) def open_link(self): """ diff --git a/rtv/subreddit_page.py b/rtv/subreddit_page.py index 86b352e5..a07bad02 100644 --- a/rtv/subreddit_page.py +++ b/rtv/subreddit_page.py @@ -32,21 +32,6 @@ def __init__(self, reddit, term, config, oauth, name): self.nav = Navigator(self.content.get) self.toggled_subreddit = None - def handle_selected_page(self): - """ - Open all selected pages in subwindows except other subreddit pages. - """ - if not self.selected_page: - pass - elif self.selected_page.name in ('subscription', 'submission', 'inbox'): - # Launch page in a subwindow - self.selected_page = self.selected_page.loop() - elif self.selected_page.name == 'subreddit': - # Replace the current page - self.active = False - else: - raise RuntimeError(self.selected_page.name) - def refresh_content(self, order=None, name=None): """ Re-download all submissions and reset the page index @@ -165,7 +150,7 @@ def open_submission(self, url=None): if data.get('url_type') == 'selfpost': self.config.history.add(data['url_full']) - self.selected_page = self.open_submission_page(url) + self.open_submission_page(url) @SubredditController.register(Command('SUBREDDIT_OPEN_IN_BROWSER')) def open_link(self): @@ -217,7 +202,7 @@ def post_submission(self): if not self.term.loader.exception: # Open the newly created submission - self.selected_page = self.open_submission_page(submission=submission) + self.open_submission_page(submission=submission) @SubredditController.register(Command('SUBREDDIT_HIDE')) @logged_in @@ -233,7 +218,7 @@ def hide(self): with self.term.loader('Hiding'): data['object'].hide() data['hidden'] = True - + def _draw_item(self, win, data, inverted): n_rows, n_cols = win.getmaxyx() diff --git a/rtv/subscription_page.py b/rtv/subscription_page.py index da6cc9d2..b6821da4 100644 --- a/rtv/subscription_page.py +++ b/rtv/subscription_page.py @@ -26,13 +26,6 @@ def __init__(self, reddit, term, config, oauth, content_type='subreddit'): self.nav = Navigator(self.content.get) self.content_type = content_type - def handle_selected_page(self): - """ - Always close the current page when another page is selected. - """ - if self.selected_page: - self.active = False - def refresh_content(self, order=None, name=None): """ Re-download all subscriptions and reset the page index @@ -54,14 +47,7 @@ def select_subreddit(self): Store the selected subreddit and return to the subreddit page """ name = self.get_selected_item()['name'] - self.selected_page = self.open_subreddit_page(name) - - @SubscriptionController.register(Command('SUBSCRIPTION_EXIT')) - def close_subscriptions(self): - """ - Close subscriptions and return to the subreddit page - """ - self.active = False + self.open_subreddit_page(name) def _draw_banner(self): # Subscriptions can't be sorted, so disable showing the order menu diff --git a/rtv/templates/rtv.cfg b/rtv/templates/rtv.cfg index 3cca8a2e..f6a482e0 100644 --- a/rtv/templates/rtv.cfg +++ b/rtv/templates/rtv.cfg @@ -149,12 +149,12 @@ COPY_URL = Y PRIVATE_MESSAGE = C SUBSCRIPTIONS = s MULTIREDDITS = S +RETURN = h, ; Submission page SUBMISSION_TOGGLE_COMMENT = 0x20 SUBMISSION_OPEN_IN_BROWSER = o, , SUBMISSION_POST = c -SUBMISSION_EXIT = h, SUBMISSION_OPEN_IN_PAGER = l, SUBMISSION_OPEN_IN_URLVIEWER = b SUBMISSION_GOTO_PARENT = K @@ -170,11 +170,9 @@ SUBREDDIT_HIDE = 0x20 ; Subscription page SUBSCRIPTION_SELECT = l, , , -SUBSCRIPTION_EXIT = h, s, S, , ; Inbox page INBOX_VIEW_CONTEXT = l, INBOX_OPEN_SUBMISSION = o, , INBOX_REPLY = c INBOX_MARK_READ = w -INBOX_EXIT = h, , diff --git a/tests/test_inbox.py b/tests/test_inbox.py index 53cfc4ce..bf7fd0dc 100644 --- a/tests/test_inbox.py +++ b/tests/test_inbox.py @@ -7,6 +7,7 @@ from rtv import exceptions from rtv.docs import FOOTER_INBOX from rtv.inbox_page import InboxPage +from rtv.page import PageStack from rtv.submission_page import SubmissionPage try: @@ -144,46 +145,45 @@ def test_inbox_mark_seen(inbox_page, terminal): assert data['is_new'] is True -def test_inbox_close(inbox_page, terminal): - - inbox_page.active = None - inbox_page.controller.trigger('h') - assert inbox_page.active is False - - def test_inbox_view_context(inbox_page, terminal): # Should be able to view the context of a comment + PageStack.init(inbox_page) + initial_stack_size = PageStack.size() inbox_page.controller.trigger('4') inbox_page.controller.trigger('l') - assert inbox_page.active - assert inbox_page.selected_page - assert isinstance(inbox_page.selected_page, SubmissionPage) + assert(PageStack.size() == initial_stack_size + 1) + assert isinstance(PageStack.current_page(), SubmissionPage) - inbox_page.selected_page = None + # Close the submission page + inbox_page.controller.trigger('h') # Should not be able to view the context of a private message + initial_stack_size = PageStack.size() inbox_page.controller.trigger('3') inbox_page.controller.trigger('l') - assert inbox_page.active - assert inbox_page.selected_page is None + assert PageStack.size() == initial_stack_size + assert PageStack.current_page() is inbox_page assert terminal.loader.exception is None def test_inbox_open_submission(inbox_page, terminal): # Should be able to open the submission that a comment was for + PageStack.init(inbox_page) + initial_stack_size = PageStack.size() inbox_page.controller.trigger('4') inbox_page.controller.trigger('o') - assert inbox_page.active - assert inbox_page.selected_page - assert isinstance(inbox_page.selected_page, SubmissionPage) + assert(PageStack.size() == initial_stack_size + 1) + assert isinstance(PageStack.current_page(), SubmissionPage) - inbox_page.selected_page = None + # Close the submission page: + inbox_page.controller.trigger('h') # Should not be able to open the submission for a private message + initial_stack_size = PageStack.size() inbox_page.controller.trigger('3') inbox_page.controller.trigger('o') - assert inbox_page.active - assert inbox_page.selected_page is None + assert PageStack.size() == initial_stack_size + assert PageStack.current_page() is inbox_page assert terminal.loader.exception is None diff --git a/tests/test_page.py b/tests/test_page.py index 09a52127..a7ea3e77 100644 --- a/tests/test_page.py +++ b/tests/test_page.py @@ -5,7 +5,7 @@ import pytest -from rtv.page import Page, PageController, logged_in +from rtv.page import Page, PageController, PageStack, logged_in try: from unittest import mock @@ -46,14 +46,6 @@ def test_page_unauthenticated(reddit, terminal, config, oauth): mock.patch.object(page, 'nav'), \ mock.patch.object(page, 'draw'): - # Loop - def func(_): - page.active = False - with mock.patch.object(page, 'controller'): - page.controller.trigger = mock.MagicMock(side_effect=func) - page.loop() - assert page.draw.called - # Quit, confirm terminal.stdscr.getch.return_value = ord('y') with mock.patch('sys.exit') as sys_exit: @@ -142,3 +134,60 @@ def test_page_cycle_theme(reddit, terminal, config, oauth): curses.has_colors.return_value = False page.controller.trigger(curses.KEY_F2) assert page.term.theme.required_colors == 0 + + +def test_page_stack(reddit, terminal, config, oauth): + ps = PageStack(max_size=3) + PageStack.init() + + assert PageStack.size() == 0 + + # Fill the page stack: + for _ in range(3): + PageStack.add(Page(reddit, terminal, config, oauth)) + ps._stay_within_max_size() + + assert PageStack.size() == 3 + + # Create a new page and add it to the already filled stack: + cpage = Page(reddit, terminal, config, oauth) + PageStack.add(cpage) + ps._stay_within_max_size() + + assert PageStack.size() == 3 + assert PageStack.current_page() is cpage + + # Remove the currently active page 'cpage' from the stack: + PageStack.pop() + ps._stay_within_max_size() + + assert PageStack.size() == 2 + assert PageStack.current_page() is not cpage + + +def test_page_back_button(reddit, terminal, config, oauth): + ps = PageStack(max_size=3) + PageStack.init() + + # Add two pages to the page stack: + page1 = Page(reddit, terminal, config, oauth) + page1.controller = PageController(page1, keymap=config.keymap) + page2 = Page(reddit, terminal, config, oauth) + page2.controller = PageController(page2, keymap=config.keymap) + + for page in (page1, page2): + PageStack.add(page) + ps._stay_within_max_size() + + assert PageStack.size() == 2 + assert PageStack.current_page() is page2 + + # Apply the back button: + PageStack.current_page().controller.trigger('h') + assert PageStack.size() == 1 + assert PageStack.current_page() is page1 + + # Apply the back button again (page1 is the only page in the page stack): + PageStack.current_page().controller.trigger('h') + assert PageStack.size() == 1 + assert PageStack.current_page() is page1 diff --git a/tests/test_submission.py b/tests/test_submission.py index b50a6d4d..920e4822 100644 --- a/tests/test_submission.py +++ b/tests/test_submission.py @@ -6,6 +6,7 @@ import pytest +from rtv.page import PageStack from rtv.submission_page import SubmissionPage from rtv.docs import FOOTER_SUBMISSION @@ -99,14 +100,6 @@ def test_submission_refresh(submission_page): submission_page.refresh_content() -def test_submission_exit(submission_page): - - # Exiting should set active to false - submission_page.active = True - submission_page.controller.trigger('h') - assert not submission_page.active - - def test_submission_unauthenticated(submission_page, terminal): # Unauthenticated commands @@ -137,24 +130,18 @@ def test_submission_prompt(submission_page, terminal): # Prompt for a different subreddit with mock.patch.object(terminal, 'prompt_input'): # Valid input - submission_page.active = True - submission_page.selected_page = None + initial_stack_size = PageStack.size() terminal.prompt_input.return_value = 'front/top' submission_page.controller.trigger('/') - submission_page.handle_selected_page() - assert not submission_page.active - assert submission_page.selected_page + stack_size_after_first_prompt = PageStack.size() + assert(stack_size_after_first_prompt == initial_stack_size + 1) # Invalid input - submission_page.active = True - submission_page.selected_page = None terminal.prompt_input.return_value = 'front/pot' submission_page.controller.trigger('/') - submission_page.handle_selected_page() - assert submission_page.active - assert not submission_page.selected_page + assert(PageStack.size() == stack_size_after_first_prompt) @pytest.mark.parametrize('prompt', PROMPTS.values(), ids=list(PROMPTS)) @@ -162,17 +149,15 @@ def test_submission_prompt_submission(submission_page, terminal, prompt): # Navigate to a different submission from inside a submission with mock.patch.object(terminal, 'prompt_input'): + initial_stack_size = PageStack.size() terminal.prompt_input.return_value = prompt submission_page.content.order = 'top' submission_page.controller.trigger('/') assert not terminal.loader.exception + assert(PageStack.size() == initial_stack_size + 1) - submission_page.handle_selected_page() - assert not submission_page.active - assert submission_page.selected_page - - assert submission_page.selected_page.content.order is None - data = submission_page.selected_page.content.get(-1) + assert PageStack.current_page().content.order is None + data = PageStack.current_page().content.get(-1) assert data['object'].id == '571dw3' diff --git a/tests/test_subreddit.py b/tests/test_subreddit.py index f6d57399..0f2c2763 100644 --- a/tests/test_subreddit.py +++ b/tests/test_subreddit.py @@ -8,6 +8,8 @@ import pytest from rtv import __version__ +from rtv.page import PageStack +from rtv.subscription_page import SubscriptionPage from rtv.subreddit_page import SubredditPage from rtv.packages.praw.errors import NotFound, HTTPException from requests.exceptions import ReadTimeout @@ -163,14 +165,14 @@ def test_subreddit_prompt(subreddit_page, terminal): # Prompt for a different subreddit with mock.patch.object(terminal, 'prompt_input'): + PageStack.init(subreddit_page) + initial_stack_size = PageStack.size() terminal.prompt_input.return_value = 'front/top' subreddit_page.controller.trigger('/') - subreddit_page.handle_selected_page() - assert not subreddit_page.active - assert subreddit_page.selected_page - assert subreddit_page.selected_page.content.name == '/r/front' - assert subreddit_page.selected_page.content.order == 'top' + assert(PageStack.size() == initial_stack_size + 1) + assert PageStack.current_page().content.name == '/r/front' + assert PageStack.current_page().content.order == 'top' @pytest.mark.parametrize('prompt', PROMPTS.values(), ids=list(PROMPTS)) @@ -293,15 +295,16 @@ def test_subreddit_order_search(subreddit_page, terminal): def test_subreddit_open(subreddit_page, terminal, config): + PageStack.init(subreddit_page) # Open the selected submission data = subreddit_page.content.get(subreddit_page.nav.absolute_index) with mock.patch.object(config.history, 'add'): + initial_stack_size = PageStack.size() data['url_type'] = 'selfpost' subreddit_page.controller.trigger('l') assert not terminal.loader.exception - assert subreddit_page.selected_page - assert subreddit_page.active + assert(PageStack.size() == initial_stack_size + 1) config.history.add.assert_called_with(data['url_full']) # Open the selected link externally @@ -360,6 +363,7 @@ def test_subreddit_unauthenticated(subreddit_page, terminal): def test_subreddit_post(subreddit_page, terminal, reddit, refresh_token): + PageStack.init(subreddit_page) # Log in subreddit_page.config.refresh_token = refresh_token @@ -389,11 +393,11 @@ def test_subreddit_post(subreddit_page, terminal, reddit, refresh_token): reddit.submit.return_value = submission subreddit_page.controller.trigger('c') assert reddit.submit.called - assert subreddit_page.selected_page.content._submission == submission - assert subreddit_page.active + assert PageStack.current_page().content._submission == submission def test_subreddit_open_subscriptions(subreddit_page, refresh_token): + PageStack.init(subreddit_page) # Log in subreddit_page.config.refresh_token = refresh_token @@ -401,12 +405,7 @@ def test_subreddit_open_subscriptions(subreddit_page, refresh_token): # Open subscriptions subreddit_page.controller.trigger('s') - assert subreddit_page.selected_page - assert subreddit_page.active - - with mock.patch('rtv.page.Page.loop') as loop: - subreddit_page.handle_selected_page() - assert loop.called + assert isinstance(PageStack.current_page(), SubscriptionPage) def test_subreddit_get_inbox_timeout(subreddit_page, refresh_token, terminal, vcr): @@ -425,6 +424,7 @@ def test_subreddit_get_inbox_timeout(subreddit_page, refresh_token, terminal, vc def test_subreddit_open_multireddits(subreddit_page, refresh_token): + PageStack.init(subreddit_page) # Log in subreddit_page.config.refresh_token = refresh_token @@ -432,12 +432,7 @@ def test_subreddit_open_multireddits(subreddit_page, refresh_token): # Open multireddits subreddit_page.controller.trigger('S') - assert subreddit_page.selected_page - assert subreddit_page.active - - with mock.patch('rtv.page.Page.loop') as loop: - subreddit_page.handle_selected_page() - assert loop.called + assert isinstance(PageStack.current_page(), SubscriptionPage) def test_subreddit_private_user_pages(subreddit_page, refresh_token): @@ -546,13 +541,13 @@ def test_subreddit_draw_header(subreddit_page, refresh_token, terminal): def test_subreddit_frontpage_toggle(subreddit_page, terminal): + PageStack.init(subreddit_page) with mock.patch.object(terminal, 'prompt_input'): terminal.prompt_input.return_value = 'aww' subreddit_page.controller.trigger('/') - subreddit_page.handle_selected_page() - new_page = subreddit_page.selected_page + new_page = PageStack.current_page() assert new_page is not None assert new_page.content.name == '/r/aww' @@ -591,50 +586,3 @@ def test_subreddit_hide_submission(subreddit_page, refresh_token): # Make sure that the status was actually updated on the server side data['object'].refresh() assert data['object'].hidden is False - - -def test_subreddit_handle_selected_page(subreddit_page, subscription_page): - - # Method should be a no-op if selected_page is unset - subreddit_page.active = True - subreddit_page.handle_selected_page() - assert subreddit_page.selected_page is None - assert subreddit_page.active - - # Open the subscription page and select a subreddit from the list of - # subscriptions - with mock.patch.object(subscription_page, 'loop', return_value=subreddit_page): - subreddit_page.selected_page = subscription_page - subreddit_page.handle_selected_page() - assert subreddit_page.selected_page == subreddit_page - assert subreddit_page.active - - # Now when handle_select_page() is called again, the current subreddit - # should be closed so the selected page can be opened - subreddit_page.handle_selected_page() - assert subreddit_page.selected_page == subreddit_page - assert not subreddit_page.active - - -def test_subreddit_page_loop_pre_select(subreddit_page, submission_page): - - # Set the selected_page before entering the loop(). This will cause the - # selected page to immediately open. If the selected page returns a - # different subreddit page (e.g. the user enters a subreddit into the - # prompt before they hit the `h` key), the initial loop should be closed - # immediately - subreddit_page.selected_page = submission_page - with mock.patch.object(submission_page, 'loop', return_value=subreddit_page): - selected_page = subreddit_page.loop() - - assert not subreddit_page.active - assert selected_page == subreddit_page - - -def test_subreddit_page_loop(subreddit_page, stdscr, terminal): - - stdscr.getch.return_value = ord('/') - - with mock.patch.object(terminal, 'prompt_input', return_value='all'): - new_page = subreddit_page.loop() - assert new_page.content.name == '/r/all' diff --git a/tests/test_subscription.py b/tests/test_subscription.py index dba6afd3..3f72e91d 100644 --- a/tests/test_subscription.py +++ b/tests/test_subscription.py @@ -5,6 +5,8 @@ import pytest +from rtv.page import PageStack +from rtv.subreddit_page import SubredditPage from rtv.subscription_page import SubscriptionPage try: @@ -68,28 +70,22 @@ def test_subscription_refresh(subscription_page): def test_subscription_prompt(subscription_page, terminal): + PageStack.init(subscription_page) # Prompt for a different subreddit with mock.patch.object(terminal, 'prompt_input'): # Valid input - subscription_page.active = True - subscription_page.selected_page = None terminal.prompt_input.return_value = 'front/top' subscription_page.controller.trigger('/') - subscription_page.handle_selected_page() - assert not subscription_page.active - assert subscription_page.selected_page + assert PageStack.size() == 2 + assert isinstance(PageStack.current_page(), SubredditPage) # Invalid input - subscription_page.active = True - subscription_page.selected_page = None terminal.prompt_input.return_value = 'front/pot' subscription_page.controller.trigger('/') - subscription_page.handle_selected_page() - assert subscription_page.active - assert not subscription_page.selected_page + assert PageStack.size() == 2 def test_subscription_move(subscription_page): @@ -128,21 +124,13 @@ def test_subscription_move(subscription_page): def test_subscription_select(subscription_page): + PageStack.init(subscription_page) # Select a subreddit subscription_page.controller.trigger(curses.KEY_ENTER) - subscription_page.handle_selected_page() - assert subscription_page.selected_page - assert not subscription_page.active - - -def test_subscription_close(subscription_page): - - # Close the subscriptions page - subscription_page.controller.trigger('h') - assert not subscription_page.selected_page - assert not subscription_page.active + assert PageStack.size() == 2 + assert isinstance(PageStack.current_page(), SubredditPage) def test_subscription_page_invalid(subscription_page, oauth, refresh_token):