diff --git a/rtv/__main__.py b/rtv/__main__.py index 6d6ee60..2ece43f 100755 --- a/rtv/__main__.py +++ b/rtv/__main__.py @@ -203,7 +203,7 @@ def main(): # Authorize on launch if the refresh token is present oauth = OAuthHelper(reddit, term, config) if config['autologin'] and config.refresh_token: - oauth.authorize() + oauth.authorize(autologin=True) page = None name = config['subreddit'] diff --git a/rtv/oauth.py b/rtv/oauth.py index 45269c7..efa04a2 100644 --- a/rtv/oauth.py +++ b/rtv/oauth.py @@ -137,7 +137,7 @@ class OAuthHelper(object): if '.compact' not in self.reddit.config.API_PATHS['authorize']: self.reddit.config.API_PATHS['authorize'] += '.compact' - def authorize(self): + def authorize(self, autologin=False): self.params.update(state=None, code=None, error=None) @@ -163,6 +163,14 @@ class OAuthHelper(object): raise InvalidRefreshToken( ' Invalid user credentials!\n' 'The cached refresh token has been removed') + + else: + if not autologin: + # Only show the welcome message if explicitly logging + # in, not when RTV first launches. + message = 'Welcome {}!'.format(self.reddit.user.name) + self.term.show_notification(message) + return state = uuid.uuid4().hex diff --git a/tests/cassettes/test_oauth_authorize_without_autologin.yaml b/tests/cassettes/test_oauth_authorize_without_autologin.yaml new file mode 100644 index 0000000..10f209c --- /dev/null +++ b/tests/cassettes/test_oauth_authorize_without_autologin.yaml @@ -0,0 +1,115 @@ +interactions: +- request: + body: grant_type=refresh_token&redirect_uri=http%3A%2F%2F127.0.0.1%3A65000%2F&refresh_token=********** + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: ['**********'] + Connection: [keep-alive] + Content-Length: ['122'] + Content-Type: [application/x-www-form-urlencoded] + User-Agent: [rtv test suite PRAW/3.6.1 Python/3.7.0 b'Darwin-17.7.0-x86_64-i386-64bit'] + method: POST + uri: https://api.reddit.com/api/v1/access_token/ + response: + body: {string: '{"access_token": "36515184-7KSQG1pABLI0BMdtBUBgVwJeGs4", "token_type": + "bearer", "expires_in": 3600, "scope": "edit history identity mysubreddits + privatemessages read report save submit subscribe vote"}'} + headers: + Accept-Ranges: [bytes] + Connection: [keep-alive] + Content-Length: ['202'] + Content-Type: [application/json; charset=UTF-8] + Date: ['Mon, 01 Oct 2018 05:42:09 GMT'] + Server: [snooserv] + Set-Cookie: [edgebucket=vOY6s0gRBGaMCKM2TW; Domain=reddit.com; Max-Age=63071999; + Path=/; secure] + Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload] + Via: [1.1 varnish] + X-Cache: [MISS] + X-Cache-Hits: ['0'] + X-Moose: [majestic] + X-Served-By: [cache-mdw17320-MDW] + X-Timer: ['S1538372530.899269,VS0,VE55'] + cache-control: ['max-age=0, must-revalidate'] + x-content-type-options: [nosniff] + x-frame-options: [SAMEORIGIN] + x-xss-protection: [1; mode=block] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: ['**********'] + Connection: [keep-alive] + User-Agent: [rtv test suite PRAW/3.6.1 Python/3.7.0 b'Darwin-17.7.0-x86_64-i386-64bit'] + method: GET + uri: https://oauth.reddit.com/api/v1/me.json + response: + body: + string: !!binary | + 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 + headers: + Accept-Ranges: [bytes] + Connection: [keep-alive] + Content-Encoding: [gzip] + Content-Length: ['1968'] + Content-Type: [application/json; charset=UTF-8] + Date: ['Mon, 01 Oct 2018 05:42:10 GMT'] + Server: [snooserv] + Set-Cookie: [edgebucket=vLcAJTa0wNDFSAVGya; Domain=reddit.com; Max-Age=63071999; + Path=/; secure] + Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload] + Vary: [accept-encoding] + Via: [1.1 varnish] + X-Cache: [MISS] + X-Cache-Hits: ['0'] + X-Moose: [majestic] + X-Served-By: [cache-mdw17322-MDW] + X-Timer: ['S1538372530.136880,VS0,VE86'] + cache-control: ['private, s-maxage=0, max-age=0, must-revalidate, max-age=0, + must-revalidate'] + expires: ['-1'] + x-content-type-options: [nosniff] + x-frame-options: [SAMEORIGIN] + x-ratelimit-remaining: ['596.0'] + x-ratelimit-reset: ['470'] + x-ratelimit-used: ['4'] + x-xss-protection: [1; mode=block] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_config.py b/tests/test_config.py index 15075e5..395db7c 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -14,7 +14,7 @@ except ImportError: def test_copy_default_config(): - "Make sure the default config file was included in the package" + """Make sure the default config file was included in the package""" with NamedTemporaryFile(suffix='.cfg') as fp: with mock.patch('rtv.config.six.moves.input', return_value='y'): @@ -26,7 +26,7 @@ def test_copy_default_config(): def test_copy_default_config_cancel(): - "Pressing ``n`` should cancel the copy" + """Pressing ``n`` should cancel the copy""" with NamedTemporaryFile(suffix='.cfg') as fp: with mock.patch('rtv.config.six.moves.input', return_value='n'): @@ -35,7 +35,7 @@ def test_copy_default_config_cancel(): def test_copy_config_interrupt(): - "Pressing ``Ctrl-C`` should cancel the copy" + """Pressing ``Ctrl-C`` should cancel the copy""" with NamedTemporaryFile(suffix='.cfg') as fp: with mock.patch('rtv.config.six.moves.input') as func: @@ -45,7 +45,7 @@ def test_copy_config_interrupt(): def test_copy_default_mailcap(): - "Make sure the example mailcap file was included in the package" + """Make sure the example mailcap file was included in the package""" with NamedTemporaryFile() as fp: with mock.patch('rtv.config.six.moves.input', return_value='y'): @@ -57,7 +57,7 @@ def test_copy_default_mailcap(): def test_config_interface(): - "Test setting and removing values" + """Test setting and removing values""" config = Config(ascii=True) assert config['ascii'] is True @@ -77,7 +77,7 @@ def test_config_interface(): def test_config_get_args(): - "Ensure that command line arguments are parsed properly" + """Ensure that command line arguments are parsed properly""" args = ['rtv', 'https://reddit.com/permalink •', @@ -92,7 +92,8 @@ def test_config_get_args(): '--enable-media', '--theme', 'molokai', '--list-themes', - '--no-flash'] + '--no-flash', + '--no-autologin'] with mock.patch('sys.argv', ['rtv']): config_dict = Config.get_args() @@ -117,6 +118,7 @@ def test_config_get_args(): assert config['theme'] == 'molokai' assert config['list_themes'] is True assert config['flash'] is False + assert config['autologin'] is False def test_config_link_deprecated(): @@ -137,7 +139,7 @@ def test_config_link_deprecated(): def test_config_from_file(): - "Ensure that config file arguments are parsed properly" + """Ensure that config file arguments are parsed properly""" args = { 'ascii': True, @@ -151,11 +153,14 @@ def test_config_from_file(): 'max_comment_cols': 150, 'hide_username': True, 'theme': 'molokai', - 'flash': True} + 'flash': True, + 'autologin': True + } bindings = { 'REFRESH': 'r, ', - 'UPVOTE': ''} + 'UPVOTE': '' + } with NamedTemporaryFile(suffix='.cfg') as fp: @@ -186,7 +191,7 @@ def test_config_from_file(): def test_config_refresh_token(): - "Ensure that the refresh token can be loaded, saved, and removed" + """Ensure that the refresh token can be loaded, saved, and removed""" with NamedTemporaryFile(delete=False) as fp: config = Config(token_file=fp.name) @@ -202,6 +207,7 @@ def test_config_refresh_token(): # Discard the token and delete the file config.delete_refresh_token() + config.delete_refresh_token() assert config.refresh_token is None assert not os.path.exists(fp.name) @@ -225,7 +231,7 @@ def test_config_refresh_token(): def test_config_history(): - "Ensure that the history can be loaded and saved" + """Ensure that the history can be loaded and saved""" # Should still be able to load if the file doesn't exist config = Config(history_file='/fake_path/fake_file') diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 6fd5410..ce6e73c 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -89,7 +89,7 @@ def test_oauth_authorize_invalid_token(oauth): def test_oauth_authorize_with_refresh_token(oauth, refresh_token): oauth.config.refresh_token = refresh_token - oauth.authorize() + oauth.authorize(autologin=True) assert oauth.server is None # We should be able to handle an oauth failure @@ -103,6 +103,18 @@ def test_oauth_authorize_with_refresh_token(oauth, refresh_token): assert oauth.config.refresh_token is None +def test_oauth_authorize_without_autologin(oauth, terminal, refresh_token): + + # The welcome message should be displayed when autologin is set to + # false, even if we're using an existing refresh token and not performing + # the whole login procedure. + oauth.config.refresh_token = refresh_token + oauth.authorize(autologin=False) + + text = 'Welcome civilization_phaze_3!'.encode('utf-8') + terminal.stdscr.subwin.addstr.assert_any_call(1, 1, text) + + def test_oauth_clear_data(oauth): oauth.config.refresh_token = 'secrettoken' oauth.reddit.refresh_token = 'secrettoken'