Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Preserve Private Visibility When Publishing a Custom Post

Regression test for the bug where publishing a REST custom post from the pre-publishing sheet flattens a user-selected `private` visibility to a public `publish`.

## Prerequisites
- Logged in to the app with the test account.
- The site has at least one custom post type registered with REST API support, and the custom post types entry is visible on the My Site screen. If no custom post type is available, fail with "Prerequisite not met: site has no REST custom post type".

## Steps
1. Navigate to the "My Site" tab.
2. From the blog details menu, tap the **"More"** row (uses an ellipsis icon) to open the Custom Post Types list.
3. Tap one of the available custom post types (e.g., "Books").
4. Tap the FAB (floating "+" button in the bottom-right corner) to create a new custom post.
5. Enter "CPT private preserve" as the post title.
6. Tap the "Publish" button in the top-right corner to open the pre-publish sheet.
7. From the pre-publish sheet, open "Post Settings".
8. Change the visibility setting to "Private".
9. Return to the pre-publish sheet.
10. Tap "Publish" to commit.
11. Dismiss the confirmation screen by tapping "Done".

## Verification (REST API)
- Use the WordPress REST API endpoint for the chosen custom post type (e.g., `/wp/v2/<cpt-rest-base>?search=CPT+private+preserve&status=private`) to look up the post by title. Authenticate with the application password (private posts are not returned to anonymous requests).
- Verify a post titled "CPT private preserve" exists.
- **Regression assertion:** the post's `status` field is exactly `"private"`, not `"publish"`. A `status` of `"publish"` indicates the bug has regressed.

## Cleanup (REST API)
- Use the WordPress REST API to trash the post created during this test, regardless of pass or fail.

## Expected Outcome
- The custom post is published with private visibility and the REST API confirms `status: "private"`.
- The user's `private` selection from Post Settings is preserved on the publish path.
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ class CustomPostEditorService {
let capabilities = PostSettingsCapabilities(from: details)
// At the moment, category & tags are separated from custom taxonomies. We can unify them as taxonomies later,
// by which point we won't need this filter logic.
self.taxonomies = (try? blog.taxonomies
.filter { capabilities.customTaxonomySlugs.contains($0.slug) }
.sorted(using: KeyPathComparator(\.name))) ?? []
self.taxonomies =
(try? blog.taxonomies
.filter { capabilities.customTaxonomySlugs.contains($0.slug) }
.sorted(using: KeyPathComparator(\.name))) ?? []

switch self.state {
case let .newPost(params):
Expand Down Expand Up @@ -122,12 +123,12 @@ class CustomPostEditorService {

case (.newPost(let existing), true):
var params = settings.makeCreateParameters(from: existing, taxonomies: taxonomies)
params.status = params.status?.normalizedPublishStatus() ?? .publish

// Update content
if let delegate {
let hasTitle = details.supports.map[.title] == .bool(true)
let editorContent = try await delegate.editorContent(for: self)
params.status = .publish
params.title = hasTitle ? editorContent.title : nil
params.content = editorContent.content
}
Expand All @@ -140,7 +141,7 @@ class CustomPostEditorService {

case (.existingPost(let post, _), true):
var params = settings.makeUpdateParameters(from: post, taxonomies: taxonomies)
params.status = .publish
params.status = PostStatus(settings.status).normalizedPublishStatus()

// Update content
if let delegate {
Expand All @@ -162,7 +163,7 @@ class CustomPostEditorService {
switch state {
case .newPost(let existing):
var params = existing
params.status = publish ? .publish : .draft
params.status = publish ? (existing.status?.normalizedPublishStatus() ?? .publish) : .draft
params.title = hasTitle ? content.title : nil
params.content = content.content
try await create(params: params)
Expand All @@ -181,7 +182,7 @@ class CustomPostEditorService {
params = PostUpdateParams(meta: nil)
}
if publish {
params.status = .publish
params.status = pending.map { PostStatus($0.status).normalizedPublishStatus() } ?? .publish
}
params.title = hasTitle ? content.title : nil
params.content = content.content
Expand All @@ -195,7 +196,8 @@ class CustomPostEditorService {
guard try await !hasBeenModified(post: post) else { throw PostUpdateError.conflicts }

let endpoint = details.toPostEndpointType()
let updatedPost = try await wpService.posts().updatePost(endpointType: endpoint, postId: post.id, params: params)
let updatedPost = try await wpService.posts()
.updatePost(endpointType: endpoint, postId: post.id, params: params)
state = .existingPost(updatedPost)
initialSettings = settings

Expand Down Expand Up @@ -265,7 +267,8 @@ extension PostCreateParams {
params.status = .draft

if let categoryID = blog.settings?.defaultCategoryID,
categoryID != PostCategory.uncategorized {
categoryID != PostCategory.uncategorized
{
params.categories = [TermId(categoryID.int64Value)]
}

Expand All @@ -278,3 +281,18 @@ extension PostCreateParams {
return params
}
}

private extension PostStatus {
/// Maps a user-selected status to the one used by a publish action.
/// `.future` and `.private` are preserved because they carry their own
/// publishing semantics (scheduled, password/private visibility); every
/// other selection — draft or pending — collapses to `.publish` so the
/// post is published normally.
func normalizedPublishStatus() -> PostStatus {
switch self {
case .future: return .future
case .private: return .private
default: return .publish
}
}
}