Skip to content
Merged
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
23 changes: 22 additions & 1 deletion src/Services/PropertiesTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public function convertPropertiesToMarkdownTable(array $properties): string
continue;
}

$markdown .= "| {$name} | {$value} |\n";
// Escape special characters in property name and value
$escapedName = $this->escapeTableCellCharacters($name);
$escapedValue = $this->escapeTableCellCharacters($value);

$markdown .= "| {$escapedName} | {$escapedValue} |\n";
}

return $markdown."\n";
Expand Down Expand Up @@ -289,4 +293,21 @@ private function formatUser(?array $user): string

return $user['name'] ?? 'Unknown';
}

/**
* Escape special characters in markdown table cells
*
* @param string $text Text to escape
* @return string Escaped text
*/
private function escapeTableCellCharacters(string $text): string
{
// Escape pipe characters to prevent table structure corruption
$text = str_replace('|', '\|', $text);

// Replace newline characters with spaces to maintain table structure
$text = str_replace(["\r\n", "\r", "\n"], ' ', $text);

return $text;
}
}
320 changes: 320 additions & 0 deletions tests/Services/PropertiesTableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -489,3 +489,323 @@

expect($result)->toContain('| Description | This is a description |');
});

test('escapes pipe characters in property names', function () {
$properties = [
'Name | Title' => [
'id' => 'title',
'type' => 'title',
'title' => [
['plain_text' => 'Test Page'],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Name \| Title | Test Page |');
});

test('escapes pipe characters in property values', function () {
$properties = [
'Description' => [
'id' => 'rich',
'type' => 'rich_text',
'rich_text' => [
['plain_text' => 'Value with | pipe'],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Description | Value with \| pipe |');
});

test('escapes multiple pipe characters in property names and values', function () {
$properties = [
'A | B | C' => [
'id' => 'title',
'type' => 'title',
'title' => [
['plain_text' => 'X | Y | Z'],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| A \| B \| C | X \| Y \| Z |');
});

test('escapes pipe characters in select property values', function () {
$properties = [
'Type' => [
'id' => 'select',
'type' => 'select',
'select' => [
'id' => 'LOQu',
'name' => 'option-1 | option-2',
'color' => 'pink',
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Type | option-1 \| option-2 |');
});

test('escapes pipe characters in multi_select property values', function () {
$properties = [
'Tags' => [
'id' => 'multi_select',
'type' => 'multi_select',
'multi_select' => [
[
'id' => 'tag1',
'name' => 'tag | 1',
'color' => 'blue',
],
[
'id' => 'tag2',
'name' => 'tag | 2',
'color' => 'green',
],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Tags | tag \| 1, tag \| 2 |');
});

test('escapes pipe characters in people names', function () {
$properties = [
'Person' => [
'id' => 'people',
'type' => 'people',
'people' => [
['name' => 'John | Doe'],
['name' => 'Jane | Smith'],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Person | John \| Doe, Jane \| Smith |');
});

test('escapes pipe characters in file names', function () {
$properties = [
'Files' => [
'id' => 'files',
'type' => 'files',
'files' => [
[
'name' => 'file | name.pdf',
'type' => 'external',
'external' => [
'url' => 'https://example.com/file.pdf',
],
],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Files | [file \| name.pdf](https://example.com/file.pdf) |');
});

test('escapes pipe characters in user names', function () {
$properties = [
'Created By' => [
'id' => 'created_by',
'type' => 'created_by',
'created_by' => [
'id' => 'user-id',
'name' => 'John | Doe',
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Created By | John \| Doe |');
});

test('escapes pipe characters in formula string values', function () {
$properties = [
'Calculated' => [
'id' => 'formula',
'type' => 'formula',
'formula' => [
'type' => 'string',
'string' => 'Result | Text',
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Calculated | Result \| Text |');
});

test('replaces newline characters with spaces in property values', function () {
$properties = [
'Description' => [
'id' => 'rich',
'type' => 'rich_text',
'rich_text' => [
['plain_text' => "Line 1\nLine 2"],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Description | Line 1 Line 2 |');
});

test('replaces carriage return with spaces in property values', function () {
$properties = [
'Description' => [
'id' => 'rich',
'type' => 'rich_text',
'rich_text' => [
['plain_text' => "Line 1\rLine 2"],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Description | Line 1 Line 2 |');
});

test('replaces CRLF with spaces in property values', function () {
$properties = [
'Description' => [
'id' => 'rich',
'type' => 'rich_text',
'rich_text' => [
['plain_text' => "Line 1\r\nLine 2"],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Description | Line 1 Line 2 |');
});

test('replaces multiple newlines with spaces in property values', function () {
$properties = [
'Description' => [
'id' => 'rich',
'type' => 'rich_text',
'rich_text' => [
['plain_text' => "Line 1\n\nLine 2\n\nLine 3"],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Description | Line 1 Line 2 Line 3 |');
});

test('replaces newlines in property names', function () {
$properties = [
"Multi\nLine\nName" => [
'id' => 'title',
'type' => 'title',
'title' => [
['plain_text' => 'Test Value'],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Multi Line Name | Test Value |');
});

test('handles both newlines and pipe characters together', function () {
$properties = [
"Name | Title\nWith Newline" => [
'id' => 'rich',
'type' => 'rich_text',
'rich_text' => [
['plain_text' => "Value | with\npipe and\nnewlines"],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Name \| Title With Newline | Value \| with pipe and newlines |');
});

test('replaces newlines in select property values', function () {
$properties = [
'Type' => [
'id' => 'select',
'type' => 'select',
'select' => [
'id' => 'LOQu',
'name' => "Option\nWith\nNewlines",
'color' => 'pink',
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Type | Option With Newlines |');
});

test('replaces newlines in multi_select property values', function () {
$properties = [
'Tags' => [
'id' => 'multi_select',
'type' => 'multi_select',
'multi_select' => [
[
'id' => 'tag1',
'name' => "Tag\n1",
'color' => 'blue',
],
[
'id' => 'tag2',
'name' => "Tag\n2",
'color' => 'green',
],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Tags | Tag 1, Tag 2 |');
});

test('replaces newlines in people names', function () {
$properties = [
'Person' => [
'id' => 'people',
'type' => 'people',
'people' => [
['name' => "John\nDoe"],
['name' => "Jane\nSmith"],
],
],
];

$result = $this->propertiesTable->convertPropertiesToMarkdownTable($properties);

expect($result)->toContain('| Person | John Doe, Jane Smith |');
});
Loading