Index: src/wp-includes/author-template.php
===================================================================
--- src/wp-includes/author-template.php	(revision 34673)
+++ src/wp-includes/author-template.php	(working copy)
@@ -223,23 +223,17 @@
 }
 
 /**
- * Display an HTML link to the author page of the author of the current post.
+ * Retrieves an HTML link to the author page of the author of the current post.
  *
- * Does just echo get_author_posts_url() function, like the others do. The
- * reason for this, is that another function is used to help in printing the
- * link to the author's posts.
+ * Returns an HTML-formatted link using get_author_posts_url().
  *
- * @link https://codex.wordpress.org/Template_Tags/the_author_posts_link
- * @since 1.2.0
+ * @since 4.4.0
  *
  * @global object $authordata The current author's DB object.
  *
- * @param string $deprecated Deprecated.
+ * @return string An HTML link to the author page.
  */
-function the_author_posts_link($deprecated = '') {
-	if ( !empty( $deprecated ) )
-		_deprecated_argument( __FUNCTION__, '2.1' );
-
+function get_the_author_posts_link() {
 	global $authordata;
 	if ( ! is_object( $authordata ) ) {
 		return;
@@ -259,10 +253,25 @@
 	 *
 	 * @param string $link HTML link.
 	 */
-	echo apply_filters( 'the_author_posts_link', $link );
+	return apply_filters( 'the_author_posts_link', $link );
 }
 
 /**
+ * Displays an HTML link to the author page of the author of the current post.
+ *
+ * @since 1.2.0
+ * @since 4.4.0 Converted into a wrapper for get_the_author_posts_link()
+ *
+ * @param string $deprecated Unused.
+ */
+function the_author_posts_link( $deprecated = '' ) {
+	if ( ! empty( $deprecated ) ) {
+		_deprecated_argument( __FUNCTION__, '2.1' );
+	}
+	echo get_the_author_posts_link();
+}
+
+/**
  * Retrieve the URL to the author page for the user with the ID provided.
  *
  * @since 2.1.0
Index: tests/phpunit/tests/template/author.php
===================================================================
--- tests/phpunit/tests/template/author.php	(revision 0)
+++ tests/phpunit/tests/template/author.php	(working copy)
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * A set of unit tests for functions in wp-includes/author-template.php
+ *
+ * @group template
+ */
+class Tests_Author_Template extends WP_UnitTestCase {
+
+	/**
+	 * @ticket 30355
+	 */
+	public function test_get_the_author_posts_link_no_permalinks() {
+		$author = $this->factory->user->create_and_get( array(
+			'display_name'  => 'Foo',
+			'user_nicename' => 'bar'
+		) );
+
+		$GLOBALS['authordata'] = $author->data;
+
+		$link = get_the_author_posts_link();
+
+		$url = sprintf( 'http://%1$s/?author=%2$s', WP_TESTS_DOMAIN, $author->ID );
+
+		$this->assertContains( $url, $link );
+		$this->assertContains( 'Posts by Foo', $link );
+		$this->assertContains( '>Foo</a>', $link );
+
+		unset( $GLOBALS['authordata'] );
+	}
+
+	/**
+	 * @ticket 30355
+	 */
+	public function test_get_the_author_posts_link_with_permalinks() {
+		global $wp_rewrite;
+		$wp_rewrite->init();
+		$wp_rewrite->set_permalink_structure( '/%postname%/' );
+		$wp_rewrite->flush_rules();
+
+		$author = $this->factory->user->create_and_get( array(
+			'display_name'  => 'Foo',
+			'user_nicename' => 'bar'
+		) );
+
+		$GLOBALS['authordata'] = $author;
+
+		$link = get_the_author_posts_link();
+
+		$url = sprintf( 'http://%1$s/author/%2$s/', WP_TESTS_DOMAIN, $author->user_nicename );
+
+		$this->assertContains( $url, $link );
+		$this->assertContains( 'Posts by Foo', $link );
+		$this->assertContains( '>Foo</a>', $link );
+
+		// Cleanup.
+		$wp_rewrite->set_permalink_structure( '' );
+		unset( $GLOBALS['authordata'] );
+	}
+}
