From e44d6b59bf251127fb8d6eca026db0448c3ad82a Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:47:29 -0400 Subject: [PATCH] Added -Open flag to Publish-BloggerPost, Publish-MarkdownBloggerPost #27 --- src/public/Publish-BloggerPost.ps1 | 28 +- src/public/Publish-MarkdownBloggerPost.ps1 | 53 +++- src/tests/Publish-BloggerPost.Tests.ps1 | 273 ++++++++++++++++++ .../Publish-MarkdownBloggerPost.Tests.ps1 | 46 +++ 4 files changed, 377 insertions(+), 23 deletions(-) create mode 100644 src/tests/Publish-BloggerPost.Tests.ps1 diff --git a/src/public/Publish-BloggerPost.ps1 b/src/public/Publish-BloggerPost.ps1 index 1a37dc6..d8f4d36 100644 --- a/src/public/Publish-BloggerPost.ps1 +++ b/src/public/Publish-BloggerPost.ps1 @@ -23,6 +23,9 @@ .PARAMETER Draft Optional. If specified, the post will be saved as a draft instead of being published. +.PARAMETER Open + Optional. If specified, launches a browser to view the post after publishing. + #> Function Publish-BloggerPost { [CmdletBinding()] @@ -39,9 +42,14 @@ Function Publish-BloggerPost { [Parameter(Mandatory = $true)] [string]$Content, + [Parameter(Mandatory = $false)] [string[]]$Labels, - [switch]$Draft + [Parameter(Mandatory = $false)] + [switch]$Draft, + + [Parameter(Mandatory = $false)] + [switch]$Open ) $uri = "https://www.googleapis.com/blogger/v3/blogs/$BlogId/posts" @@ -77,15 +85,17 @@ Function Publish-BloggerPost { $post = Invoke-GApi -Uri $uri -Body ($body | ConvertTo-Json) -Method $method - $postUrl = ` - if ($Draft) { - "https://www.blogger.com/blog/post/edit/preview/$BlogId/$($post.id)" - } - else { - $post.url - } + if ($Open) { + $postUrl = ` + if ($Draft) { + "https://www.blogger.com/blog/post/edit/preview/$BlogId/$($post.id)" + } + else { + $post.url + } - Start-Process $postUrl + Start-Process $postUrl + } return $post } \ No newline at end of file diff --git a/src/public/Publish-MarkdownBloggerPost.ps1 b/src/public/Publish-MarkdownBloggerPost.ps1 index cf1b4d3..ff6454b 100644 --- a/src/public/Publish-MarkdownBloggerPost.ps1 +++ b/src/public/Publish-MarkdownBloggerPost.ps1 @@ -1,32 +1,49 @@ <# .SYNOPSIS - Publishes a markdown file as a blog post to Blogger, including uploading any local images to Google Drive. + Publishes a markdown file as a blog post to Blogger, including uploading any local images to Google Drive. .DESCRIPTION - This function processes a markdown file to publish it as a blog post. It handles: - - Extracting front matter from the markdown file - - Finding and uploading local images to Google Drive - - Converting markdown content to HTML - - Publishing the post to Blogger - - Updating the front matter with post information + This function processes a markdown file to publish it as a blog post. It handles: + - Extracting front matter from the markdown file + - Finding and uploading local images to Google Drive + - Converting markdown content to HTML + - Publishing the post to Blogger + - Updating the front matter with post information .PARAMETER File - The path to the markdown file to publish. + The path to the markdown file to publish. .PARAMETER BlogId - The ID of the blog to publish to. If not specified, uses the BlogId from the current BloggerSession. + The ID of the blog to publish to. If not specified, uses the BlogId from the current BloggerSession. .PARAMETER Draft - If specified, publishes the post as a draft rather than a published post. + If specified, publishes the post as a draft rather than a published post. .PARAMETER Force - If specified, will overwrite existing images in Google Drive with the same name. + If specified, will overwrite existing images in Google Drive with the same name. + +.PARAMETER Open + If specified, launches a browser to view the post after publishing. + +.EXAMPLE + # publish or update post + Publish-MarkdownBloggerPost -File "my-post.md" + +.EXAMPLE + # publish or update a draft post + Publish-MarkdownBloggerPost -File "my-post.md" -Draft .EXAMPLE - Publish-MarkdownBloggerPost -File "my-post.md" + # publish or update a post, updating google drive images if required + Publish-MarkdownBloggerPost -File "my-post.md" -Force .EXAMPLE - Publish-MarkdownBloggerPost -File "my-post.md" -Draft -Force + # publish or update a post, launching a web browser to view the published post + Publish-MarkdownBloggerPost -File "my-post.md" -Open + +.EXAMPLE + # publish or update a draft, launching a web browser with the page preview + Publish-MarkdownBloggerPost -File "my-post.md" -Draft -Open #> Function Publish-MarkdownBloggerPost { @@ -46,7 +63,10 @@ Function Publish-MarkdownBloggerPost [array]$ExcludeLabels = @(), [Parameter(Mandatory=$false)] - [switch]$Force + [switch]$Force, + + [Parameter(Mandatory=$false)] + [switch]$Open ) @@ -80,6 +100,7 @@ Function Publish-MarkdownBloggerPost Title = $postInfo.title Content = $content Draft = $Draft + Open = $Open } if ($postInfo["postId"]) { @@ -90,18 +111,22 @@ Function Publish-MarkdownBloggerPost $postArgs.Labels = [array]$postInfo.tags | Where-Object { $_ -notin $ExcludeLabels } } + Write-Verbose "Publishing blogger post with args: $($postArgs | ConvertTo-Json -Depth 5)" $post = Publish-BloggerPost @postArgs # update post id $postInfo["postId"] = $post.id if ($Draft) { + Write-Verbose "Adding 'wip' to front matter" $postInfo["wip"] = $true } else { if ($postInfo["wip"]) { + Write-Verbose "Removing 'wip' from front matter" $postInfo.Remove("wip") } } + Write-Verbose "Updating front matter with post id: $($postInfo['postId'])" Set-MarkdownFrontMatter -File $File -Replace $postInfo return $post diff --git a/src/tests/Publish-BloggerPost.Tests.ps1 b/src/tests/Publish-BloggerPost.Tests.ps1 new file mode 100644 index 0000000..bb0d8cf --- /dev/null +++ b/src/tests/Publish-BloggerPost.Tests.ps1 @@ -0,0 +1,273 @@ +Describe "Publish-BloggerPost" { + BeforeAll { + Import-Module $PSScriptRoot\_TestHelpers.ps1 -Force + } + + BeforeEach { + Import-Module $PSScriptRoot\..\PSBlogger.psm1 -Force + + $script:blogId = "123456789" + $script:title = "Test Post" + $script:content = "

Test content

" + $script:labels = @("tag1", "tag2") + + # Set up mock responses + InModuleScope PSBlogger { + Mock Invoke-GApi { + return [pscustomobject]@{ + id = "12345" + url = "https://example.blogspot.com/2023/01/test-post.html" + title = "Test Post" + content = "

Test content

" + } + } + Mock Start-Process {} + } + } + + Context "Creating a new post" { + It "Should call Blogger API with correct parameters for new post" { + # arrange + InModuleScope PSBlogger { + Mock Invoke-GApi -ParameterFilter { + $Uri -eq "https://www.googleapis.com/blogger/v3/blogs/123456789/posts" -and + $Method -eq "POST" + } -Verifiable { + return [pscustomobject]@{ id = "12345"; url = "https://example.blogspot.com/2023/01/test-post.html" } + } + } + $invokeArgs = @{ + BlogId = $script:blogId + Title = $script:title + Content = $script:content + Labels = $script:labels + } + + # act + Publish-BloggerPost @invokeArgs + + # assert + Should -InvokeVerifiable + } + + It "Should create a published post by default" { + # arrange + InModuleScope PSBlogger { + Mock Invoke-GApi -ParameterFilter { + $Uri -eq "https://www.googleapis.com/blogger/v3/blogs/123456789/posts" + } -Verifiable { + return [pscustomobject]@{ id = "12345"; url = "https://example.blogspot.com/2023/01/test-post.html" } + } + } + $invokeArgs = @{ + BlogId = $script:blogId + Title = $script:title + Content = $script:content + } + + # act + Publish-BloggerPost @invokeArgs + + # assert + Should -InvokeVerifiable + } + + It "Should create a draft post when Draft switch is specified" { + # arrange + InModuleScope PSBlogger { + Mock Invoke-GApi -ParameterFilter { + $Uri -eq "https://www.googleapis.com/blogger/v3/blogs/123456789/posts?isDraft=true" + } -Verifiable { + return @{ id = "12345"; url = "https://example.blogspot.com/2023/01/test-post.html" } + } + } + + # act + Publish-BloggerPost -BlogId $script:blogId -Title $script:title -Content $script:content -Draft + + # assert + Should -InvokeVerifiable + } + } + + Context "Updating an existing post" { + + BeforeEach { + $script:postId = "98765" + $script:invokeArgs = @{ + BlogId = $script:blogId + PostId = $script:postId + Title = $script:Title + Content = $script:Content + } + } + + It "Should call Blogger API with correct parameters for updating post" { + # arrange + InModuleScope PSBlogger { + Mock Invoke-GApi -ParameterFilter { + $Uri -eq "https://www.googleapis.com/blogger/v3/blogs/123456789/posts/98765?publish=true" -and + $Method -eq "PUT" + } -Verifiable { + return @{ id = "98765"; url = "https://example.blogspot.com/2023/01/updated-test-post.html" } + } + } + + # act + Publish-BloggerPost @script:invokeArgs + + # assert + Should -InvokeVerifiable + } + + It "Should update post as draft when Draft switch is specified" { + # arrange + $postId = "98765" + + InModuleScope PSBlogger { + Mock Invoke-GApi -ParameterFilter { + $Uri -eq "https://www.googleapis.com/blogger/v3/blogs/123456789/posts/98765" -and + $Method -eq "PUT" + } -Verifiable { + return @{ id = "98765"; url = "https://example.blogspot.com/2023/01/updated-test-post.html" } + } + } + + # act + Publish-BloggerPost -BlogId $script:blogId -PostId $postId -Title $script:title -Content $script:content -Draft + + # assert + Should -InvokeVerifiable + } + } + + Context "Open post after publishing" { + + BeforeEach { + $script:invokeArgs = @{ + BlogId = $script:blogId + Title = $script:title + Content = $script:content + } + } + + It "Should not launch browser by default" { + # arrange + InModuleScope PSBlogger { + Mock Start-Process { + throw "Start-Process should not be called without Open switch" + } + } + + # act / assert - should not throw + { + Publish-BloggerPost @script:invokeArgs + } | Should -Not -Throw + } + + It "Should launch browser when Open switch is specified for published post" { + # arrange + InModuleScope PSBlogger { + Mock Start-Process -Verifiable {} + } + + # act + Publish-BloggerPost @script:invokeArgs -Open + + # assert + Should -InvokeVerifiable + } + + It "Should launch browser with draft preview URL when Open and Draft switches are specified" { + # arrange + InModuleScope PSBlogger { + Mock Start-Process -Verifiable {} + } + + # act + Publish-BloggerPost @script:invokeArgs -Draft -Open + + # assert + Should -InvokeVerifiable + } + } + + Context "Request body validation" { + + BeforeEach { + $script:invokeArgs = @{ + BlogId = $script:blogId + Title = $script:title + Content = $script:content + } + } + + It "Should include all specified parameters in request body" { + # arrange + InModuleScope PSBlogger { + Mock Invoke-GApi -ParameterFilter { + $bodyObj = $Body | ConvertFrom-Json + $bodyObj.kind -eq "blogger#post" -and + $bodyObj.blog.id -eq "123456789" -and + $bodyObj.title -eq "Test Post" -and + $bodyObj.content -eq "

Test content

" -and + $bodyObj.labels.Count -eq 2 -and + $bodyObj.labels[0] -eq "tag1" -and + $bodyObj.labels[1] -eq "tag2" + } -Verifiable { + return [pscustomobject]@{ id = "12345"; url = "https://example.blogspot.com/2023/01/test-post.html" } + } + } + + # act + Publish-BloggerPost @script:invokeArgs -Labels @("tag1", "tag2") + + # assert + Should -InvokeVerifiable + } + + It "Should handle empty labels array" { + # arrange + + InModuleScope PSBlogger { + Mock Invoke-GApi -ParameterFilter { + $bodyObj = $Body | ConvertFrom-Json + $bodyObj.labels -eq $null -or $bodyObj.labels.Count -eq 0 + } -Verifiable { + return @{ id = "12345"; url = "https://example.blogspot.com/2023/01/test-post.html" } + } + } + + # act + Publish-BloggerPost @script:invokeArgs + + # assert + Should -InvokeVerifiable + } + } + + Context "Return value" { + It "Should return the post object from API response" { + # arrange + InModuleScope PSBlogger { + Mock Invoke-GApi { + return [pscustomobject]@{ + id = "12345" + url = "https://example.blogspot.com/2023/01/test-post.html" + title = "Test Post" + content = "

Test content

" + } + } + } + + # act + $result = Publish-BloggerPost @script:invokeArgs + + # assert + $result.id | Should -Be "12345" + $result.url | Should -Be "https://example.blogspot.com/2023/01/test-post.html" + $result.title | Should -Be "Test Post" + $result.content | Should -Be "

Test content

" + } + } +} diff --git a/src/tests/Publish-MarkdownBloggerPost.Tests.ps1 b/src/tests/Publish-MarkdownBloggerPost.Tests.ps1 index 372a4e9..04f666d 100644 --- a/src/tests/Publish-MarkdownBloggerPost.Tests.ps1 +++ b/src/tests/Publish-MarkdownBloggerPost.Tests.ps1 @@ -199,6 +199,52 @@ postId: "123456" $postInfo.postid | Should -Be "post1" -Because "Markdown file should be updated with post id after publishing for the first time." } + Context "Open post after publishing" { + + BeforeEach { + InModuleScope PSBlogger { + Mock Set-MarkdownFrontMatter {} + } + } + + It "Should pass Open parameter to Publish-BloggerPost when specified" { + # arrange + $testFile = "TestDrive:\testfile.md" + Set-Content -Path $testFile -Value "# hello world" + + InModuleScope PSBlogger { + Mock Publish-BloggerPost -ParameterFilter { $Open -eq $true } -Verifiable { + return @{ id = "123" } + } + } + + # act + Publish-MarkdownBloggerPost -File $testFile -BlogId 1234 -Open + + # assert + Should -InvokeVerifiable + } + + It "Should not pass Open parameter to Publish-BloggerPost by default" { + # arrange + $testFile = "TestDrive:\testfile.md" + Set-Content -Path $testFile -Value "# hello world" + + InModuleScope PSBlogger { + Mock Publish-BloggerPost -ParameterFilter { $Open -eq $false } -Verifiable { + return [pscustomobject]@{ id = "123" } + } + } + + # act + Publish-MarkdownBloggerPost -File $testFile -BlogId 1234 + + # assert + Should -InvokeVerifiable + } + + } + Context "Upload Images to Google Drive" { BeforeEach {