From df7f8e25b3c547cd3573a8419aca4e3c0fe2f7e3 Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Sat, 26 Jul 2025 17:31:20 -0400 Subject: [PATCH 1/6] cleaned-up Find-MarkdownImages tests --- src/tests/Find-MarkdownImages.Tests.ps1 | 260 +++++++++++++++--------- 1 file changed, 160 insertions(+), 100 deletions(-) diff --git a/src/tests/Find-MarkdownImages.Tests.ps1 b/src/tests/Find-MarkdownImages.Tests.ps1 index 9b9a232..41843ef 100644 --- a/src/tests/Find-MarkdownImages.Tests.ps1 +++ b/src/tests/Find-MarkdownImages.Tests.ps1 @@ -34,19 +34,20 @@ Describe "Find-MarkdownImages" { } It "Should find images with alt text only" { + # arrange $markdownFile = "TestDrive:\basic.md" - $markdownContent = @" -# Test Post - -Here's an image: -![Alt text](test-image1.png) - -More content here. -"@ + $markdownContent = @( + "# Test Post" + "" + "Here's an image:" + "![Alt text](test-image1.png)" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].AltText | Should -Be "Alt text" $result[0].FileName | Should -Be "test-image1.png" @@ -56,16 +57,21 @@ More content here. } It "Should find images with alt text and title" { + # arrange $markdownFile = "TestDrive:\with-title.md" - $markdownContent = @" -# Test Post + $markdownContent = @( + "# Test Post" + "" + "Here's a screenshot:" + '![Screenshot](test-image1.png "Application Screenshot")' + ) -join [Environment]::NewLine -![Screenshot](test-image1.png "Application Screenshot") -"@ Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].AltText | Should -Be "Screenshot" $result[0].Title | Should -Be "Application Screenshot" @@ -73,23 +79,26 @@ More content here. } It "Should find multiple images in the same file" { + # arrange $markdownFile = "TestDrive:\multiple.md" - $markdownContent = @" -# Test Post - -First image: -![Image 1](test-image1.png) - -Second image: -![Image 2](subfolder/test-image2.jpg "Second image title") - -Third image: -![Image 3](test-image1.png "Reused image") -"@ + $markdownContent = @( + "# Test Post" + "" + "First image:" + "![Image 1](test-image1.png)" + "" + "Second image:" + "![Image 2](subfolder/test-image2.jpg `"Second image title`")" + "" + "Third image (reused):" + "![Image 3](test-image1.png `"Reused image`")" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 3 $result[0].FileName | Should -Be "test-image1.png" $result[1].FileName | Should -Be "test-image2.jpg" @@ -100,14 +109,18 @@ Third image: Context "Path resolution" { It "Should resolve relative paths correctly" { + # arrange $markdownFile = "TestDrive:\relative.md" - $markdownContent = @" -![Relative image](./subfolder/test-image2.jpg) -"@ + $markdownContent = @( + "" + "![Relative image](./subfolder/test-image2.jpg)" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].FileName | Should -Be "test-image2.jpg" $result[0].RelativePath | Should -Be "./subfolder/test-image2.jpg" @@ -115,32 +128,40 @@ Third image: } It "Should handle absolute paths" { + # arrange $markdownFile = "TestDrive:\absolute.md" $absolutePath = (Get-Item "TestDrive:\absolute-image.gif").FullName - $markdownContent = @" -![Absolute image]($absolutePath) -"@ + $markdownContent = @( + "" + "![Absolute image]($absolutePath)" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].FileName | Should -Be "absolute-image.gif" $result[0].LocalPath | Should -Be $absolutePath } It "Should handle parent directory references" { + # arrange # Create a markdown file in a subdirectory $subDir = "TestDrive:\subdir" New-Item -Path $subDir -ItemType Directory -Force $markdownFile = "$subDir\parent-ref.md" - $markdownContent = @" -![Parent image](../test-image1.png) -"@ + $markdownContent = @( + "" + "![Parent image](../test-image1.png)" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].FileName | Should -Be "test-image1.png" $result[0].RelativePath | Should -Be "../test-image1.png" @@ -149,45 +170,57 @@ Third image: Context "Filtering and validation" { It "Should skip images that are already web hosted (HTTP/HTTPS)" { + # arrange $markdownFile = "TestDrive:\with-urls.md" - $markdownContent = @" -![Local image](test-image1.png) -![HTTP image](http://example.com/image.jpg) -![HTTPS image](https://example.com/image.png) -"@ + $markdownContent = @( + "" + "![Local image](test-image1.png)" + "![HTTP image](http://example.com/image.jpg)" + "![HTTPS image](https://example.com/image.png)" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].FileName | Should -Be "test-image1.png" } It "Should skip non-existent files" { + # arrange $markdownFile = "TestDrive:\missing-files.md" - $markdownContent = @" -![Existing image](test-image1.png) -![Missing image](does-not-exist.jpg) -![Another existing](subfolder/test-image2.jpg) -"@ + $markdownContent = @( + "" + "![Existing image](test-image1.png)" + "![Missing image](does-not-exist.jpg)" + "![Another existing](subfolder/test-image2.jpg)" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 2 $result[0].FileName | Should -Be "test-image1.png" $result[1].FileName | Should -Be "test-image2.jpg" } It "Should handle empty alt text" { + # arrange $markdownFile = "TestDrive:\empty-alt.md" - $markdownContent = @" -![](test-image1.png) -"@ + $markdownContent = @( + "" + "![](test-image1.png)" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].AltText | Should -Be "" $result[0].FileName | Should -Be "test-image1.png" @@ -196,66 +229,74 @@ Third image: Context "Edge cases and special characters" { It "Should handle images with spaces in filenames" { + # arrange $imageWithSpaces = "TestDrive:\image with spaces.png" Set-Content -Path $imageWithSpaces -Value "fake content" $markdownFile = "TestDrive:\spaces.md" - $markdownContent = @" -![Image with spaces](image with spaces.png) -"@ + $markdownContent = @( + "" + "![Image with spaces](image with spaces.png)" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].FileName | Should -Be "image with spaces.png" } It "Should handle special characters in alt text" { + # arrange $markdownFile = "TestDrive:\special-chars.md" - $markdownContent = @" -![Alt with "quotes" and 'apostrophes'](test-image1.png) -"@ + $markdownContent = @( + "" + "![Alt with `"quotes`" and 'apostrophes'](test-image1.png)" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].AltText | Should -Be "Alt with `"quotes`" and 'apostrophes'" } It "Should handle markdown files with no images" { + # arrange $markdownFile = "TestDrive:\no-images.md" - $markdownContent = @" -# Test Post - -This is just text content. - -Some more paragraphs. - -- List item 1 -- List item 2 - -No images here! -"@ + $markdownContent = @( + "# Test Post" + "" + "This markdown file has no images." + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 0 } } Context "Return object structure" { It "Should return objects with all expected properties" { + # arrange $markdownFile = "TestDrive:\properties.md" - $markdownContent = @" -![Test Alt](test-image1.png "Test Title") -"@ + $markdownContent = @( + "" + "![Test Alt](test-image1.png `"Test Title`")" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $image = $result[0] @@ -315,13 +356,13 @@ No images here! }, @{ Name = "Multiple Obsidian images" - Content = @" -![[test-image1.png|First image]] -Some text here -![[subfolder/test-image2.jpg]] -More text -![[test-image1.png|Duplicate image]] -"@ + Content = @( + "![[test-image1.png|First image]]", + "Some text here", + "![[subfolder/test-image2.jpg]]", + "More text", + "![[test-image1.png|Duplicate image]]" + ) -join [Environment]::NewLine ExpectedCount = 3 ExpectedAltText = @("First image", "", "Duplicate image") ExpectedFileName = @("test-image1.png", "test-image2.jpg", "test-image1.png") @@ -331,12 +372,14 @@ More text It "Should handle " -TestCases $obsidianTestCases { param($Name, $Content, $ExpectedCount, $ExpectedAltText, $ExpectedFileName, $ExpectedOriginal) - + # arrange $markdownFile = "TestDrive:\obsidian-test.md" Set-MarkdownFile $markdownFile $Content + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be $ExpectedCount if ($ExpectedCount -eq 1) { @@ -357,31 +400,37 @@ More text } It "Should skip Obsidian images with HTTP URLs" { + # arrange $markdownFile = "TestDrive:\obsidian-urls.md" - $markdownContent = @" -![[test-image1.png|Local image]] -![[http://example.com/image.jpg|HTTP image]] -![[https://example.com/image.png|HTTPS image]] -"@ + $markdownContent = @( + "![[test-image1.png|Local image]]", + "![[http://example.com/image.jpg|HTTP image]]", + "![[https://example.com/image.png|HTTPS image]]" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 1 $result[0].FileName | Should -Be "test-image1.png" } It "Should skip non-existent Obsidian images" { + # arrange $markdownFile = "TestDrive:\obsidian-missing.md" - $markdownContent = @" -![[test-image1.png|Existing image]] -![[does-not-exist.jpg|Missing image]] -![[subfolder/test-image2.jpg|Another existing]] -"@ + $markdownContent = @( + "![[test-image1.png|Existing image]]", + "![[does-not-exist.jpg|Missing image]]", + "![[subfolder/test-image2.jpg|Another existing]]" + ) -join [Environment]::NewLine Set-MarkdownFile $markdownFile $markdownContent + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be 2 $result[0].FileName | Should -Be "test-image1.png" $result[1].FileName | Should -Be "test-image2.jpg" @@ -407,32 +456,41 @@ More text $mixedFormatTestCases = @( @{ Name = "Both standard and Obsidian in same file" - Content = @" -![Standard image](test-image1.png "Standard title") -![[test-image1.png|Obsidian image]] -![Another standard](subfolder/test-image2.jpg) -![[subfolder/test-image2.jpg]] -"@ + Content = @( + "# Mixed Format Test", + "", + "Here are some images:", + "![Standard image](test-image1.png `"Standard title`")", + "![[test-image1.png|Obsidian image]]", + "![Another standard](subfolder/test-image2.jpg)", + "![[subfolder/test-image2.jpg]]" + ) -join [Environment]::NewLine ExpectedCount = 4 ExpectedAltTexts = @("Standard image", "Obsidian image", "Another standard", "") ExpectedTitles = @("Standard title", "", "", "") }, @{ Name = "Standard format only" - Content = @" -![Image 1](test-image1.png) -![Image 2](subfolder/test-image2.jpg "With title") -"@ + Content = @( + "# Standard Format Test", + "", + "Here are some images:", + "![Image 1](test-image1.png)", + "![Image 2](subfolder/test-image2.jpg `"With title`")" + ) -join [Environment]::NewLine ExpectedCount = 2 ExpectedAltTexts = @("Image 1", "Image 2") ExpectedTitles = @("", "With title") }, @{ Name = "Obsidian format only" - Content = @" -![[test-image1.png|Alt 1]] -![[subfolder/test-image2.jpg]] -"@ + Content = @( + "# Obsidian Format Test", + "", + "Here are some images:", + "![[test-image1.png|Alt 1]]", + "![[subfolder/test-image2.jpg]]" + ) -join [Environment]::NewLine ExpectedCount = 2 ExpectedAltTexts = @("Alt 1", "") ExpectedTitles = @("", "") @@ -442,12 +500,14 @@ More text It "Should handle " -TestCases $mixedFormatTestCases { param($Name, $Content, $ExpectedCount, $ExpectedAltTexts, $ExpectedTitles) - + # arrange $markdownFile = "TestDrive:\mixed-format-test.md" Set-MarkdownFile $markdownFile $Content + # act $result = Find-MarkdownImages -File $markdownFile + # assert $result.Count | Should -Be $ExpectedCount for ($i = 0; $i -lt $ExpectedCount; $i++) { From d0db19ebd98d4ee4e20b58d5abdb57aa12a2ed3e Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Sat, 26 Jul 2025 18:32:25 -0400 Subject: [PATCH 2/6] Added -AttachmentsDirectory to Find-MarkdownImages --- src/public/Find-MarkdownImages.ps1 | 86 ++++++++++++++++++++----- src/tests/Find-MarkdownImages.Tests.ps1 | 78 ++++++++++++++++++---- src/tests/_TestHelpers.ps1 | 20 ++++++ 3 files changed, 157 insertions(+), 27 deletions(-) diff --git a/src/public/Find-MarkdownImages.ps1 b/src/public/Find-MarkdownImages.ps1 index d4e4f8f..a8b8fec 100644 --- a/src/public/Find-MarkdownImages.ps1 +++ b/src/public/Find-MarkdownImages.ps1 @@ -1,25 +1,36 @@ <# .SYNOPSIS - Finds all image references in a markdown file. + Finds all image references in a markdown file. .DESCRIPTION - Parses a markdown file and extracts all image references including: - - Standard markdown format: ![alt text](image_path "optional title") - - Obsidian format: ![[image_path|alt text]] - Returns information about each image including the original markdown syntax, image path, alt text, and title. + Parses a markdown file and extracts all image references including: + - Standard markdown format: ![alt text](image_path "optional title") + - Obsidian format: ![[image_path|alt text]] + Returns information about each image including the original markdown syntax, image path, alt text, and title. .PARAMETER File - The path to the markdown file to analyze. + The path to the markdown file to analyze. + +.PARAMETER AttachmentsDirectory + The directory where attachments are stored. If specified, it will be used to resolve relative paths for images. .EXAMPLE Find-MarkdownImages -File "post.md" + +.EXAMPLE + Find-MarkdownImages -File "post.md" -AttachmentsDirectory "C:\Attachments" + + This command will find all images in the specified markdown file and resolve their paths against the provided attachments directory. #> function Find-MarkdownImages { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] - [string]$File + [string]$File, + + [Parameter(Mandatory = $false)] + [string]$AttachmentsDirectory ) $content = Get-Content -Path $File -Raw @@ -31,6 +42,11 @@ function Find-MarkdownImages { $fileDirectory = "." } + if ([string]::IsNullOrEmpty($AttachmentsDirectory)) { + # If no attachments directory is specified, use the file's directory + $AttachmentsDirectory = $fileDirectory + } + # Regex pattern for standard markdown images: ![alt text](image_path "optional title") $standardPattern = '!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]*)")?\)' @@ -85,14 +101,12 @@ function Find-MarkdownImages { continue } - # Resolve relative paths - if (-not [System.IO.Path]::IsPathRooted($imagePath)) { - $resolvedPath = Join-Path -Path $fileDirectory -ChildPath $imagePath - } - else { - $resolvedPath = $imagePath - } - + # Resolve the image file path + $resolvedPath = Resolve-ImageFilePath ` + -FilePath $imagePath ` + -BaseDirectory $fileDirectory ` + -AttachmentsDirectory $AttachmentsDirectory + # Check if the file exists if (Test-Path -Path $resolvedPath -PathType Leaf) { $images += New-MarkdownImage ` @@ -126,4 +140,46 @@ Function New-MarkdownImage { FileName = $FileName NewUrl = $null # This will be set after uploading to Google Drive } +} + +Function Resolve-ImageFilePath +{ + param( + [string]$FilePath, + [string]$BaseDirectory, + [string]$AttachmentsDirectory + ) + + # Test if the file path is relative to the base directory + if (-not [System.IO.Path]::IsPathRooted($FilePath)) { + # If the path is relative, resolve it against the base directory + $resolvedPath = Join-Path -Path $BaseDirectory -ChildPath $FilePath + } else { + # If the path is absolute, use it as is + $resolvedPath = $FilePath + } + if (Test-Path $resolvedPath) { + Write-Verbose "Found image at base directory: $resolvedPath" + return $resolvedPath + } + + #Test if the file path is relative to the attachments directory + $resolvedPath = Join-Path -Path $AttachmentsDirectory -ChildPath $FilePath + if (Test-Path $resolvedPath) { + Write-Verbose "Found image at attachments directory: $resolvedPath" + return $resolvedPath + } + + # # If not found, recursively search the attachments directory + $files = @(Get-ChildItem -Path $AttachmentsDirectory -Recurse -File -Filter $FilePath) + if ($files.Count -gt 0) { + if ($files.Count -gt 1) { + Write-Warning "Multiple files found matching '$FilePath' in '$AttachmentsDirectory'. Returning the first match." + } + Write-Verbose "Found image in attachments directory: $($files[0].FullName)" + return $files[0].FullName + } + + # If still not found, return the original file path + return $FilePath } \ No newline at end of file diff --git a/src/tests/Find-MarkdownImages.Tests.ps1 b/src/tests/Find-MarkdownImages.Tests.ps1 index 41843ef..51fcb83 100644 --- a/src/tests/Find-MarkdownImages.Tests.ps1 +++ b/src/tests/Find-MarkdownImages.Tests.ps1 @@ -4,17 +4,9 @@ Describe "Find-MarkdownImages" { Import-Module $PSScriptRoot\_TestHelpers.ps1 -Force # Create test images in TestDrive - $testImage1 = "TestDrive:\test-image1.png" - $testImage2 = "TestDrive:\subfolder\test-image2.jpg" - $testImage3 = "TestDrive:\absolute-image.gif" - - # Create directory structure - New-Item -Path "TestDrive:\subfolder" -ItemType Directory -Force - - # Create dummy image files - Set-Content -Path $testImage1 -Value "fake png content" - Set-Content -Path $testImage2 -Value "fake jpg content" - Set-Content -Path $testImage3 -Value "fake gif content" + New-TestImage "TestDrive:\test-image1.png" + New-TestImage "TestDrive:\subfolder\test-image2.jpg" + New-TestImage "TestDrive:\absolute-image.gif" } Context "Basic image detection" { @@ -107,7 +99,7 @@ Describe "Find-MarkdownImages" { } } - Context "Path resolution" { + Context "Image Path resolution" { It "Should resolve relative paths correctly" { # arrange $markdownFile = "TestDrive:\relative.md" @@ -168,6 +160,68 @@ Describe "Find-MarkdownImages" { } } + Context "Path resolution with attachments directory" { + BeforeEach{ + # create images in the attachments directory + New-TestImage "TestDrive:\attachments\test-attachment1.png" + New-TestImage "TestDrive:\attachments\subfolder\test-attachment2.jpg" + } + + It "Should resolve absolute image in the attachments directory" { + # arrange + $markdownFile = "TestDrive:\attachments.md" + $markdownContent = @( + "" + "![Image in attachments](test-attachment1.png)" + ) -join [Environment]::NewLine + Set-MarkdownFile $markdownFile $markdownContent + + # act + $result = Find-MarkdownImages -File $markdownFile -AttachmentsDirectory "TestDrive:\attachments" + + # assert + $result.Count | Should -Be 1 + $result[0].FileName | Should -Be "test-attachment1.png" + $result[0].LocalPath | Should -BeLike "*attachments\test-attachment1.png" + } + + It "Should resolve absolute image with subfolder relative to the attachments directory" { + # arrange + $markdownFile = "TestDrive:\attachments-subfolder.md" + $markdownContent = @( + "" + "![Image in subfolder](subfolder/test-attachment2.jpg)" + ) -join [Environment]::NewLine + Set-MarkdownFile $markdownFile $markdownContent + + # act + $result = Find-MarkdownImages -File $markdownFile -AttachmentsDirectory "TestDrive:\attachments" + + # assert + $result.Count | Should -Be 1 + $result[0].FileName | Should -Be "test-attachment2.jpg" + $result[0].LocalPath | Should -BeLike "*attachments\subfolder\test-attachment2.jpg" + } + + It "Should find images in subfolders of the attachments directory when markdown does not specify a subfolder" { + # arrange + $markdownFile = "TestDrive:\attachments-subfolder.md" + $markdownContent = @( + "" + "![Image in subfolder](test-attachment2.jpg)" # this is in the subfolder + ) -join [Environment]::NewLine + Set-MarkdownFile $markdownFile $markdownContent + + # act + $result = Find-MarkdownImages -File $markdownFile -AttachmentsDirectory "TestDrive:\attachments" + + # assert + $result.Count | Should -Be 1 + $result[0].FileName | Should -Be "test-attachment2.jpg" + $result[0].LocalPath | Should -BeLike "*attachments\subfolder\test-attachment2.jpg" + } + } + Context "Filtering and validation" { It "Should skip images that are already web hosted (HTTP/HTTPS)" { # arrange diff --git a/src/tests/_TestHelpers.ps1 b/src/tests/_TestHelpers.ps1 index b4f1943..2f8a058 100644 --- a/src/tests/_TestHelpers.ps1 +++ b/src/tests/_TestHelpers.ps1 @@ -40,4 +40,24 @@ Function New-MarkdownImage FileName = $FileName NewUrl = $NewUrl } +} + +Function New-TestImage +{ + param( + [string]$FilePath + ) + + # resolve path + $FilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath) + $Folder = [System.IO.Path]::GetDirectoryName($FilePath) + if (-not (Test-Path -Path $Folder)) { + New-Item -ItemType Directory -Path $Folder -Force | Out-Null + } + + # create a test image + $TestImage = New-Item -ItemType File -Path $FilePath -Force + Set-Content -Path $TestImage -Value "fake image content" + + return $FilePath } \ No newline at end of file From 9d387a1441b4c48535decb98abc918f23ea67637 Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Sat, 26 Jul 2025 19:25:10 -0400 Subject: [PATCH 3/6] Expanded -AttachmentsDirectory to Publish-MarkdownBloggerPost and Publish-MarkdownDriveImages --- src/public/Publish-MarkdownBloggerPost.ps1 | 5 +- src/public/Publish-MarkdownDriveImages.ps1 | 9 ++- .../Publish-MarkdownBloggerPost.Tests.ps1 | 68 ++++++++++++++++--- .../Publish-MarkdownDriveImages.Tests.ps1 | 46 ++++++++++--- 4 files changed, 105 insertions(+), 23 deletions(-) diff --git a/src/public/Publish-MarkdownBloggerPost.ps1 b/src/public/Publish-MarkdownBloggerPost.ps1 index ff6454b..9620798 100644 --- a/src/public/Publish-MarkdownBloggerPost.ps1 +++ b/src/public/Publish-MarkdownBloggerPost.ps1 @@ -62,6 +62,9 @@ Function Publish-MarkdownBloggerPost [Parameter(Mandatory=$false)] [array]$ExcludeLabels = @(), + [Parameter(Mandatory=$false)] + [string]$AttachmentsDirectory, + [Parameter(Mandatory=$false)] [switch]$Force, @@ -86,7 +89,7 @@ Function Publish-MarkdownBloggerPost $postInfo = Get-MarkdownFrontMatter -File $File # Process images: detect, upload to Google Drive, and update markdown - $imageMappings = Publish-MarkdownDriveImages -File $File -Force:$Force + $imageMappings = Publish-MarkdownDriveImages -File $File -AttachmentsDirectory $AttachmentsDirectory -Force:$Force # convert from markdown to html file $content = ConvertTo-HtmlFromMarkdown -File $File diff --git a/src/public/Publish-MarkdownDriveImages.ps1 b/src/public/Publish-MarkdownDriveImages.ps1 index 27589aa..f42c20f 100644 --- a/src/public/Publish-MarkdownDriveImages.ps1 +++ b/src/public/Publish-MarkdownDriveImages.ps1 @@ -9,6 +9,10 @@ .PARAMETER File The path to the markdown file containing image references. +.PARAMETER AttachmentsDirectory + Optional. The directory where images are stored. If not specified, the function will look for + images in the same directory as the markdown file. + .PARAMETER Force If specified, will overwrite existing files in Google Drive with the same name. @@ -26,13 +30,16 @@ Function Publish-MarkdownDriveImages [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] [string]$File, + [Parameter(Mandatory=$false)] + [string]$AttachmentsDirectory, + [Parameter(Mandatory=$false)] [switch]$Force ) # Process images: detect, upload to Google Drive, and update markdown $imageMappings = @() - $images = Find-MarkdownImages -File $File + $images = Find-MarkdownImages -File $File -AttachmentsDirectory $AttachmentsDirectory if ($images -and $images.Count -gt 0) { Write-Verbose "Found $($images.Count) local images to upload to Google Drive" diff --git a/src/tests/Publish-MarkdownBloggerPost.Tests.ps1 b/src/tests/Publish-MarkdownBloggerPost.Tests.ps1 index 04f666d..29670a8 100644 --- a/src/tests/Publish-MarkdownBloggerPost.Tests.ps1 +++ b/src/tests/Publish-MarkdownBloggerPost.Tests.ps1 @@ -248,6 +248,17 @@ postId: "123456" Context "Upload Images to Google Drive" { BeforeEach { + InModuleScope "PSBlogger" { + Mock Publish-BloggerPost { @{ id = "1234"} } + } + + New-TestImage "TestDrive:\image.png" + $script:testFile = "TestDrive:\testfile.md" + Set-MarkdownFile $script:testFile "# hello world$([Environment]::NewLine)Your image ![image](image.png)" + } + + It "Should upload images and update markdown in place" { + # arrange InModuleScope "PSBlogger" { Mock Add-GoogleDriveFile -Verifiable { @@ -257,27 +268,62 @@ postId: "123456" } } Mock Set-GoogleDriveFilePermission -Verifiable {} + } - Mock Publish-BloggerPost { @{ id = "1234"} } + # act + $actualFile = Resolve-Path $script:testFile + Publish-MarkdownBloggerPost -File $actualFile -BlogId 1234 + + # assert + Should -InvokeVerifiable + $updatedContent = Get-Content -Path $script:testFile -Raw + $escapedRegex = [regex]::Escape("![image](https://drive.google.com/12345)") + $updatedContent | Should -Match $escapedRegex + } + + It "Should upload images from supplied attachments directory" { + # arrange + InModuleScope PSBlogger { + Mock Publish-MarkdownDriveImages -ParameterFilter { $AttachmentsDirectory -eq "TestDrive:\attachments" } { + return @() + } -Verifiable } + + # act + Publish-MarkdownBloggerPost -File $script:testFile -BlogId 1234 -AttachmentsDirectory "TestDrive:\attachments" + + # assert + Should -InvokeVerifiable } - It "Should upload images and update markdown in place" { + It "Should upload images from default attachments directory if not specified" { # arrange - $testFile = "TestDrive:\testfile.md" - $imagePath = "TestDrive:\image.png" - Set-Content -Path $testFile -Value "# hello world$([Environment]::NewLine)Your image ![image](image.png)" - Set-Content -Path $imagePath -Value "dummy image content" + InModuleScope PSBlogger { + Mock Publish-MarkdownDriveImages -ParameterFilter { $AttachmentsDirectory -eq "" } { + return @() + } -Verifiable + } # act - $actualFile = Resolve-Path $testFile - Publish-MarkdownBloggerPost -File $actualFile -BlogId 1234 + Publish-MarkdownBloggerPost -File $validFile -BlogId 1234 + + # assert + Should -InvokeVerifiable + } + + It "Should overwrite existing images in google drive if Force is specified" { + # arrange + InModuleScope PSBlogger { + Mock Publish-MarkdownDriveImages -ParameterFilter { $Force -eq $true } { + return @() + } -Verifiable + } + + # act + Publish-MarkdownBloggerPost -File $script:testFile -BlogId 1234 -Force # assert Should -InvokeVerifiable - $updatedContent = Get-Content -Path $testFile -Raw - $escapedRegex = [regex]::Escape("![image](https://drive.google.com/12345)") - $updatedContent | Should -Match $escapedRegex } } diff --git a/src/tests/Publish-MarkdownDriveImages.Tests.ps1 b/src/tests/Publish-MarkdownDriveImages.Tests.ps1 index c677ae3..f94b4f6 100644 --- a/src/tests/Publish-MarkdownDriveImages.Tests.ps1 +++ b/src/tests/Publish-MarkdownDriveImages.Tests.ps1 @@ -4,15 +4,8 @@ Describe "Publish-MarkdownDriveImages" { Import-Module $PSScriptRoot\_TestHelpers.ps1 -Force # Create test images in TestDrive - $testImage1 = "TestDrive:\test-image1.png" - $testImage2 = "TestDrive:\subfolder\test-image2.jpg" - - # Create directory structure - New-Item -Path "TestDrive:\subfolder" -ItemType Directory -Force - - # Create dummy image files - Set-Content -Path $testImage1 -Value "fake png content" - Set-Content -Path $testImage2 -Value "fake jpg content" + $testImage1 = New-TestImage "TestDrive:\test-image1.png" + $testImage2 = New-TestImage "TestDrive:\subfolder\test-image2.jpg" $fileWithSingleImage = "TestDrive:\single-image.md" $fileWithSingleImageMarkdown = @( @@ -20,7 +13,7 @@ Describe "Publish-MarkdownDriveImages" { "", "![Test Image](test-image1.png)" ) -join "`n" - Set-Content -Path $fileWithSingleImage -Value $fileWithSingleImageMarkdown + Set-MarkdownFile $fileWithSingleImage $fileWithSingleImageMarkdown # Prevent actual API calls during tests InModuleScope PSBlogger { @@ -483,4 +476,37 @@ Describe "Publish-MarkdownDriveImages" { $script:result.Count | Should -Be 2 } } + + Context "Attachments Directory" { + + It "Should find images in specified attachments directory" { + # arrange + InModuleScope PSBlogger { + Mock Find-MarkdownImages -ParameterFilter { $AttachmentsDirectory -eq "TestDrive:\attachments" } { + return @() + } -Verifiable + } + + # act + Publish-MarkdownDriveImages -File $fileWithSingleImage -AttachmentsDirectory "TestDrive:\attachments" + + # assert + Should -InvokeVerifiable + } + + It "Should allow no attachments directory to be specified and use default behavior" { + # arrange + InModuleScope PSBlogger { + Mock Find-MarkdownImages -ParameterFilter { $AttachmentsDirectory -eq ""} { + return @() + } -Verifiable + } + + # act + Publish-MarkdownDriveImages -File $fileWithSingleImage + + # assert + Should -InvokeVerifiable + } + } } From d8e43631af66093234f5065dcc78ad957cf7355d Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Sat, 26 Jul 2025 19:33:27 -0400 Subject: [PATCH 4/6] Added AttachmentsDirectory configuration setting --- src/private/Get-BloggerSession.ps1 | 1 + src/public/Set-BloggerConfig.ps1 | 3 ++- src/tests/Set-BloggerConfig.Tests.ps1 | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/private/Get-BloggerSession.ps1 b/src/private/Get-BloggerSession.ps1 index e3212bb..3b4ff0d 100644 --- a/src/private/Get-BloggerSession.ps1 +++ b/src/private/Get-BloggerSession.ps1 @@ -12,6 +12,7 @@ Function Get-BloggerSession PandocAdditionalArgs = "--html-q-tags --ascii" BlogId = $null ExcludeLabels = @() + AttachmentsDirectory = $null } if (Test-Path $session.UserPreferences) diff --git a/src/public/Set-BloggerConfig.ps1 b/src/public/Set-BloggerConfig.ps1 index 8355602..b387d82 100644 --- a/src/public/Set-BloggerConfig.ps1 +++ b/src/public/Set-BloggerConfig.ps1 @@ -12,6 +12,7 @@ - PandocHtmlFormat: The HTML format to use when converting Markdown to HTML. - PandocMarkdownFormat: The Markdown format to use when converting HTML to Markdown. - ExcludeLabels: Labels to exclude when publishing to Blogger. + - AttachmentsDirectory: Folder path to attachments .PARAMETER Value The value to set for the specified user preference. Specify an empty string to remove the preference. @@ -21,7 +22,7 @@ Function Set-BloggerConfig [CmdletBinding()] Param( [Parameter(Mandatory=$true)] - [ValidateSet("BlogId","PandocAdditionalArgs","PandocHtmlFormat","PandocMarkdownFormat","ExcludeLabels")] + [ValidateSet("BlogId","PandocAdditionalArgs","PandocHtmlFormat","PandocMarkdownFormat","ExcludeLabels","AttachmentsDirectory")] [string]$Name, [Parameter(Mandatory=$true)] diff --git a/src/tests/Set-BloggerConfig.Tests.ps1 b/src/tests/Set-BloggerConfig.Tests.ps1 index f7ae6a8..ccdf805 100644 --- a/src/tests/Set-BloggerConfig.Tests.ps1 +++ b/src/tests/Set-BloggerConfig.Tests.ps1 @@ -15,6 +15,7 @@ Describe "Set-BloggerConfig" { BlogId = $null ExcludeLabels = @() UserPreferences = "TestDrive:\UserPreferences.json" + AttachmentsDirectory = $null } # Set it as a script-scoped variable (same as the module does) @@ -25,6 +26,7 @@ Describe "Set-BloggerConfig" { It "Should persist new value to to BloggerSession.UserPreferences" -TestCases @( @{ UserPreference="BlogId"; UserPreferenceValue="12345" } @{ UserPreference="ExcludeLabels"; UserPreferenceValue=@("personal/blog-post") } + @{ UserPreference="AttachmentsDirectory"; UserPreferenceValue="c:\attachments"} ) { # act @@ -40,6 +42,7 @@ Describe "Set-BloggerConfig" { It "Should persist new value to to empty BloggerSession.UserPreferences file" -TestCases @( @{ UserPreference="BlogId"; UserPreferenceValue="12345" } @{ UserPreference="ExcludeLabels"; UserPreferenceValue=@("personal/blog-post") } + @{ UserPreference="AttachmentsDirectory"; UserPreferenceValue="c:\attachments"} ) { # arrange: empty file Set-Content TestDrive:\UserPreferences.json -Value "{}" From ade8906dc1dba105c1026e3cf88385166a873c3e Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Sat, 26 Jul 2025 20:16:43 -0400 Subject: [PATCH 5/6] Find-MarkdownImages uses $BloggerSession.AttachmentsDirectory --- src/public/Find-MarkdownImages.ps1 | 10 ++++-- src/tests/Find-MarkdownImages.Tests.ps1 | 42 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/public/Find-MarkdownImages.ps1 b/src/public/Find-MarkdownImages.ps1 index a8b8fec..1601493 100644 --- a/src/public/Find-MarkdownImages.ps1 +++ b/src/public/Find-MarkdownImages.ps1 @@ -43,8 +43,14 @@ function Find-MarkdownImages { } if ([string]::IsNullOrEmpty($AttachmentsDirectory)) { - # If no attachments directory is specified, use the file's directory - $AttachmentsDirectory = $fileDirectory + if ($BloggerSession.AttachmentsDirectory) { + # Use the Blogger session's attachments directory if available + Write-Verbose "Using PSBlogger config for attachments directory: $($BloggerSession.AttachmentsDirectory)" + $AttachmentsDirectory = $BloggerSession.AttachmentsDirectory + } else { + # Default to the file's directory if no attachments directory is specified + $AttachmentsDirectory = $fileDirectory + } } # Regex pattern for standard markdown images: ![alt text](image_path "optional title") diff --git a/src/tests/Find-MarkdownImages.Tests.ps1 b/src/tests/Find-MarkdownImages.Tests.ps1 index 51fcb83..254f0b6 100644 --- a/src/tests/Find-MarkdownImages.Tests.ps1 +++ b/src/tests/Find-MarkdownImages.Tests.ps1 @@ -220,6 +220,48 @@ Describe "Find-MarkdownImages" { $result[0].FileName | Should -Be "test-attachment2.jpg" $result[0].LocalPath | Should -BeLike "*attachments\subfolder\test-attachment2.jpg" } + + It "Should use folder of file if attachments directory is not specified" { + # arrange + $markdownFile = "TestDrive:\relative-path.md" + $markdownContent = @( + "" + "![Image with relative path](test-attachment1.png)" + ) -join [Environment]::NewLine + Set-MarkdownFile $markdownFile $markdownContent + + # act + $result = Find-MarkdownImages -File $markdownFile + + # assert + $result.Count | Should -Be 1 + $result[0].FileName | Should -Be "test-attachment1.png" + $result[0].LocalPath | Should -BeLike "*attachments\test-attachment1.png" + } + + It "Should use attachments directory user preference if available" { + # arrange + InModuleScope PSBlogger { + $BloggerSession.AttachmentsDirectory = "TestDrive:\attachments" + } + + # use a markdown file that is a sibling to attachments directory to ensure + # attachments are not in a subfolder of the markdown file + $markdownFile = "TestDrive:\subfolder\attachments-preference.md" + $markdownContent = @( + "" + "![Image in attachments directory](test-attachment1.png)" + ) -join [Environment]::NewLine + Set-MarkdownFile $markdownFile $markdownContent + + # act + $result = Find-MarkdownImages -File $markdownFile + + # assert + $result.Count | Should -Be 1 + $result[0].FileName | Should -Be "test-attachment1.png" + $result[0].LocalPath | Should -BeLike "*attachments\test-attachment1.png" + } } Context "Filtering and validation" { From 41a797a104a5e2e450906f2e261016590b20d9af Mon Sep 17 00:00:00 2001 From: bryan cook <3217452+bryanbcook@users.noreply.github.com> Date: Sat, 26 Jul 2025 20:17:23 -0400 Subject: [PATCH 6/6] Validation logic for BloggerSession values to ensure AttachmentsDirectory is a valid path and stored as non-relative value --- src/public/Get-BloggerConfig.ps1 | 1 + src/public/Set-BloggerConfig.ps1 | 11 +++++ src/tests/Set-BloggerConfig.Tests.ps1 | 62 ++++++++++++++++++++++----- src/tests/_TestHelpers.ps1 | 6 +++ 4 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/public/Get-BloggerConfig.ps1 b/src/public/Get-BloggerConfig.ps1 index 0eefe29..a472003 100644 --- a/src/public/Get-BloggerConfig.ps1 +++ b/src/public/Get-BloggerConfig.ps1 @@ -12,5 +12,6 @@ Function Get-BloggerConfig PandocAdditionalArgs = $BloggerSession.PandocAdditionalArgs BlogId = $BloggerSession.BlogId ExcludeLabels = $BloggerSession.ExcludeLabels + AttachmentsDirectory = $BloggerSession.AttachmentsDirectory } } \ No newline at end of file diff --git a/src/public/Set-BloggerConfig.ps1 b/src/public/Set-BloggerConfig.ps1 index b387d82..92c6ae1 100644 --- a/src/public/Set-BloggerConfig.ps1 +++ b/src/public/Set-BloggerConfig.ps1 @@ -38,6 +38,17 @@ Function Set-BloggerConfig $userPreferences | Out-String | Write-Verbose } + if ($Value) { + switch ($Name) { + "AttachmentsDirectory" { + if (-not (Test-Path -Path $Value -PathType Container)) { + throw "AttachmentsDirectory must be a valid directory path." + } + $Value = (Resolve-Path -Path $Value).Path + } + } + } + if (@($userPreferences.PsObject.Properties).Count -eq 0 -or $Name -notin $userPreferences.PsObject.Properties.Name) { Write-Verbose "Set-BloggerConfig: Adding Property $Name" diff --git a/src/tests/Set-BloggerConfig.Tests.ps1 b/src/tests/Set-BloggerConfig.Tests.ps1 index ccdf805..38e6261 100644 --- a/src/tests/Set-BloggerConfig.Tests.ps1 +++ b/src/tests/Set-BloggerConfig.Tests.ps1 @@ -1,4 +1,10 @@ +$script:UserPreferences = @( + @{ UserPreference="BlogId"; UserPreferenceValue="12345" } + @{ UserPreference="ExcludeLabels"; UserPreferenceValue=@("personal/blog-post") } + @{ UserPreference="AttachmentsDirectory"; UserPreferenceValue="TestDrive:\Attachments"} +) + Describe "Set-BloggerConfig" { BeforeEach { Import-Module $PSScriptRoot\..\PSBlogger.psm1 -Force @@ -21,14 +27,11 @@ Describe "Set-BloggerConfig" { # Set it as a script-scoped variable (same as the module does) New-Variable -Name BloggerSession -Value $BloggerSession -Scope Script -Force } - } - It "Should persist new value to to BloggerSession.UserPreferences" -TestCases @( - @{ UserPreference="BlogId"; UserPreferenceValue="12345" } - @{ UserPreference="ExcludeLabels"; UserPreferenceValue=@("personal/blog-post") } - @{ UserPreference="AttachmentsDirectory"; UserPreferenceValue="c:\attachments"} - ) { + New-Item -Path TestDrive:\Attachments -ItemType Directory -Force | Out-Null + } + It "Should persist new value to to BloggerSession.UserPreferences" -TestCases $script:UserPreferences { # act Set-BloggerConfig -Name $UserPreference -Value $UserPreferenceValue -ErrorAction Stop @@ -39,14 +42,11 @@ Describe "Set-BloggerConfig" { } -Parameters @{ UserPreference=$UserPreference; UserPreferenceValue=$UserPreferenceValue } } - It "Should persist new value to to empty BloggerSession.UserPreferences file" -TestCases @( - @{ UserPreference="BlogId"; UserPreferenceValue="12345" } - @{ UserPreference="ExcludeLabels"; UserPreferenceValue=@("personal/blog-post") } - @{ UserPreference="AttachmentsDirectory"; UserPreferenceValue="c:\attachments"} - ) { + It "Should persist new value to to empty BloggerSession.UserPreferences file" -TestCases $script:UserPreferences { # arrange: empty file Set-Content TestDrive:\UserPreferences.json -Value "{}" + # act Set-BloggerConfig -Name $UserPreference -Value $UserPreferenceValue @@ -56,4 +56,44 @@ Describe "Set-BloggerConfig" { $userPreferences.$UserPreference | Should -Be $UserPreferenceValue } -Parameters @{ UserPreference=$UserPreference; UserPreferenceValue=$UserPreferenceValue } } + + It "Should clear if set to empty string" -TestCases $script:UserPreferences { + # act + Set-BloggerConfig -Name $UserPreference -Value "" + + # assert + InModuleScope "PSBlogger" { + $userPreferences = Get-Content $BloggerSession.UserPreferences -Raw | ConvertFrom-Json + $userPreferences.$UserPreference | Should -Be $UserPreferenceValue + } -Parameters @{ UserPreference=$UserPreference; UserPreferenceValue="" } + } + + It "Should enforce the AttachmentsDirectory is a valid path" { + # act + { + Set-BloggerConfig -Name AttachmentsDirectory -Value ".\InvalidPath" + } | Should -Throw + } + + It "Should store AttachmentsDirectory as an absolute path" { + + try { + # arrange + $relativePath = ".\Attachments" + Push-Location TestDrive:\ + + # act + Set-BloggerConfig -Name AttachmentsDirectory -Value $relativePath + + # assert + InModuleScope "PSBlogger" { + $expectedPath = (Resolve-Path -Path "TestDrive:\Attachments").Path + $userPreferences = Get-Content $BloggerSession.UserPreferences -Raw | ConvertFrom-Json + $userPreferences.AttachmentsDirectory | Should -Be $expectedPath + } + } + finally { + Pop-Location + } + } } \ No newline at end of file diff --git a/src/tests/_TestHelpers.ps1 b/src/tests/_TestHelpers.ps1 index 2f8a058..ece7dc9 100644 --- a/src/tests/_TestHelpers.ps1 +++ b/src/tests/_TestHelpers.ps1 @@ -3,6 +3,12 @@ function Set-MarkdownFile($path, $content) { .SYNOPSIS Set the content of a markdown file #> + # resolve path + $path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path) + $Folder = [System.IO.Path]::GetDirectoryName($path) + if (-not (Test-Path -Path $Folder)) { + New-Item -ItemType Directory -Path $Folder -Force | Out-Null + } Set-Content -Path $path -Value $content }