assertJsonStringEqualsJsonString($expected, Json::encode($data, ['prettyPrint' => true]));
+ }
+
+ public function testEncodeEmptyArrayWithForceObjectOption(): void
+ {
+ $data = [];
+
+ $this->assertJsonStringEqualsJsonString('{}', Json::encode($data, ['forceObject' => true]));
+ }
+}
diff --git a/tests/Parsers/MarkdownTest.php b/tests/Parsers/MarkdownTest.php
new file mode 100644
index 000000000..bf7ddfa60
--- /dev/null
+++ b/tests/Parsers/MarkdownTest.php
@@ -0,0 +1,88 @@
+Hello, World!\nThis is a bold statement.
\n";
+
+ $this->assertSame($expectedHtml, Markdown::parse($markdown));
+ }
+
+ public function testParseWithSiteUri(): void
+ {
+ $site = $this->createStub(Site::class);
+
+ $site->method('uri')
+ ->willReturnArgument(0);
+
+ $markdown = "\n\n[Link text](https://example.com)";
+ $expectedHtml = "
\nLink text
\n";
+
+ $this->assertSame($expectedHtml, Markdown::parse($markdown, ['site' => $site]));
+ }
+
+ public function testParseReturnsHeadingsIdsWhenOptionEnabled(): void
+ {
+ $markdown = "## Section One\n\n## Section Two";
+ $expectedHtml = "Section One
\nSection Two
\n";
+
+ $this->assertSame($expectedHtml, Markdown::parse($markdown, ['addHeadingIds' => true]));
+ }
+
+ public function testParseWithCommonMarkExtensions(): void
+ {
+ $options = [
+ 'commonmarkExtensions' => [
+ CommonMarkExtensionFixture::class => [
+ 'enabled' => true,
+ ],
+ ],
+ ];
+
+ $this->expectNotToPerformAssertions();
+ Markdown::parse('', $options);
+ }
+
+ public function testParseWithCommonMarkExtensionsDoesNotAddEnvironmentExtensions(): void
+ {
+ $options = [
+ 'commonmarkExtensions' => [
+ CommonMarkCoreExtension::class => [
+ 'enabled' => true,
+ ],
+ ],
+ ];
+
+ $this->expectNotToPerformAssertions();
+ Markdown::parse('', $options);
+ }
+
+ public function testParseThrowsUnexpectedValueExceptionOnInvalidCommonMarkExtension(): void
+ {
+ $options = [
+ 'commonmarkExtensions' => [
+ stdClass::class => [
+ 'enabled' => true,
+ ],
+ ],
+ ];
+
+ $this->expectException(UnexpectedValueException::class);
+ $this->expectExceptionMessage('Invalid CommonMark extension "stdClass"');
+ Markdown::parse('', $options);
+ }
+}
diff --git a/tests/Parsers/PhpTest.php b/tests/Parsers/PhpTest.php
new file mode 100644
index 000000000..8f431b2e7
--- /dev/null
+++ b/tests/Parsers/PhpTest.php
@@ -0,0 +1,147 @@
+tearDownTempDirectory();
+ }
+
+ public function testParseAlwaysThrowsException(): void
+ {
+ $this->expectException(LogicException::class);
+ $this->expectExceptionMessage('Parsing a string of Php code is not allowed');
+ Php::parse('assertSame([
+ 'title' => 'Test',
+ 'description' => 'This is a test.',
+ 'tags' => ['php', 'testing', 'formwork'],
+ ], Php::parseFile($filePath));
+ }
+
+ public function testEncodeExportsDataAsPhpString(): void
+ {
+ $enumFixtureClass = EnumFixture::class;
+
+ $arraySerializable = $this->createStub(ArraySerializable::class);
+ $arraySerializable->method('toArray')->willReturn([
+ 'key' => 'value',
+ ]);
+ $arraySerializableClass = $arraySerializable::class;
+
+ $setStateImplementingClass = SetStateImplementingClassFixture::class;
+
+ $serializable = new SerializableFixture('value');
+ $serialized = addcslashes(serialize($serializable), '\\');
+
+ $data = [
+ 'string' => 'Test',
+ 'int' => 3,
+ 'float' => 3.14,
+ 'boolean' => false,
+ 'null' => null,
+ 'array' => ['test', 29, 2.71, true, null, []],
+ 'object' => (object) ['key' => 'value'],
+ 'enum' => EnumFixture::Alpha,
+ 'arraySerializable' => $arraySerializable,
+ 'setStateImplementing' => new SetStateImplementingClassFixture('value'),
+ 'serializable' => $serializable,
+ ];
+
+ $expected = << 'Test',
+ 'int' => 3,
+ 'float' => 3.14,
+ 'boolean' => false,
+ 'null' => null,
+ 'array' => [
+ 'test',
+ 29,
+ 2.71,
+ true,
+ null,
+ []
+ ],
+ 'object' => (object) [
+ 'key' => 'value'
+ ],
+ 'enum' => \\$enumFixtureClass::Alpha,
+ 'arraySerializable' => \\$arraySerializableClass::fromArray([
+ 'key' => 'value'
+ ]),
+ 'setStateImplementing' => \\$setStateImplementingClass::__set_state([
+ 'key' => 'value'
+ ]),
+ 'serializable' => unserialize('$serialized')
+ ]
+ PHP;
+
+ $this->assertSame($expected, Php::encode($data));
+ }
+
+ public function testEncodeThrowsExceptionWithUnencodableClasses(): void
+ {
+ $data = [
+ 'closure' => fn() => 'Unencodable',
+ ];
+
+ $this->expectException(UnexpectedValueException::class);
+ $this->expectExceptionMessage('Objects of class "Closure" cannot be encoded');
+ Php::encode($data);
+ }
+
+ public function testEncodeThrowsExceptionWithResources(): void
+ {
+ $stream = fopen('php://temp', 'r');
+
+ $this->expectException(UnexpectedValueException::class);
+ $this->expectExceptionMessage('Data of type "resource" cannot be encoded');
+
+ try {
+ Php::encode(['resource' => $stream]);
+ } finally {
+ fclose($stream);
+ }
+ }
+
+ public function testEncodeToFile(): void
+ {
+ $data = [
+ 'title' => 'Test',
+ 'description' => 'This is a test.',
+ 'tags' => ['php', 'testing', 'formwork'],
+ ];
+
+ $filePath = TESTS_TMP_PATH . '/output.php';
+
+ Php::encodeToFile($data, $filePath);
+
+ $this->assertSame($data, include $filePath);
+ }
+}
diff --git a/tests/Parsers/YamlTest.php b/tests/Parsers/YamlTest.php
new file mode 100644
index 000000000..fbaf3d7d2
--- /dev/null
+++ b/tests/Parsers/YamlTest.php
@@ -0,0 +1,97 @@
+tearDownTempDirectory();
+ }
+
+ public function testParse(): void
+ {
+ $yamlString = << 'Test',
+ 'description' => 'This is a test.',
+ 'tags' => ['php', 'yaml', 'parser'],
+ ];
+
+ $this->assertSame($expected, Yaml::parse($yamlString));
+ }
+
+ public function testParseFile(): void
+ {
+ $yamlFilePath = TESTS_TMP_PATH . '/test.yaml';
+
+ $expected = [
+ 'title' => 'Test',
+ 'description' => 'This is a test.',
+ 'tags' => ['php', 'yaml', 'parser'],
+ ];
+
+ $this->assertSame($expected, Yaml::parseFile($yamlFilePath));
+ }
+
+ public function testEncode(): void
+ {
+ $data = [
+ 'title' => 'Test',
+ 'description' => 'This is a test.',
+ 'tags' => ['php', 'yaml', 'parser'],
+ ];
+
+ $expectedYamlString = <<assertSame($expectedYamlString, Yaml::encode($data));
+ }
+
+ public function testEncodeToFile(): void
+ {
+ $data = [
+ 'title' => 'Test',
+ 'description' => 'This is a test.',
+ 'tags' => ['php', 'yaml', 'parser'],
+ ];
+
+ $yamlFilePath = TESTS_TMP_PATH . '/output.yaml';
+
+ Yaml::encodeToFile($data, $yamlFilePath);
+
+ $this->assertFileExists($yamlFilePath);
+ $this->assertSame(Yaml::encode($data), FileSystem::read($yamlFilePath));
+ }
+
+ public function testEncodeReturnsEmptyStringForEmptyData(): void
+ {
+ $this->assertSame('', Yaml::encode([]));
+ }
+}