From 5c286c3334820beef894438eab12299e88cd19e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sat, 8 Feb 2025 15:53:55 +0100 Subject: [PATCH 1/5] Implement preview branding --- app/controllers/general.php | 18 ++- app/http.php | 2 + public/scripts/preview.js | 129 ++++++++++++++++++ .../Modules/Sites/Transformers/Preview.php | 58 ++++++++ tests/e2e/Services/Sites/SitesBase.php | 23 ++++ .../Services/Sites/SitesCustomServerTest.php | 59 ++++++++ 6 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 public/scripts/preview.js create mode 100644 src/Appwrite/Platform/Modules/Sites/Transformers/Preview.php diff --git a/app/controllers/general.php b/app/controllers/general.php index 6764cbae40..18dc81a102 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -11,6 +11,7 @@ use Appwrite\Event\Usage; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Network\Validator\Origin; use Appwrite\Platform\Appwrite; +use Appwrite\Platform\Modules\Sites\Transformers\Preview; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -130,7 +131,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw }; } - if ($type === 'function' || $type === 'site') { + if ($type === 'function' || $type === 'site' || $type === 'deployment') { $method = $utopia->getRoute()?->getLabel('sdk', null); if (empty($method)) { @@ -446,6 +447,21 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw requestTimeout: 30 ); + + // Branded banner for previews + if ($type === 'deployment' && Preview::isValid($executionResponse['headers'])) { + $transformer = new Preview($executionResponse['body']); + $transformer->transform(); + $executionResponse['body'] = $transformer->getBody(); + + $execution->setAttribute('responseBody', $body); + foreach ($executionResponse['headers'] as $key => $value) { + if (\strtolower($key) === 'content-length') { + $executionResponse['headers'][$key] = \strlen($executionResponse['body']); + } + } + } + $headersFiltered = []; foreach ($executionResponse['headers'] as $key => $value) { if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) { diff --git a/app/http.php b/app/http.php index 608ac2ec12..611e373ffa 100644 --- a/app/http.php +++ b/app/http.php @@ -35,6 +35,8 @@ $domains = new Table(1_000_000); // 1 million rows $domains->column('value', Table::TYPE_INT, 1); $domains->create(); +Files::load(__DIR__ . '/../public'); + $http = new Server( host: "0.0.0.0", port: System::getEnv('PORT', 80), diff --git a/public/scripts/preview.js b/public/scripts/preview.js new file mode 100644 index 0000000000..66eac6153d --- /dev/null +++ b/public/scripts/preview.js @@ -0,0 +1,129 @@ +var banner = document.createElement('button'); +banner.id = 'appwrite-preview'; +banner.innerHTML = ` +

Preview by

+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; +banner.addEventListener("click", function() { + banner.style.opacity = 0; + setTimeout(() => { + banner.style.display = 'none'; + }, 350); +}); + +var css = document.createElement('style'); +css.innerHTML += ` +#appwrite-preview { + padding: 0; + margin: 0; + position: absolute; + right: 16px; + bottom: 16px; + z-index: 1; + border-radius: var(--border-radius-S, 8px); + border: var(--border-width-S, 1px) solid var(--color-border-neutral, #EDEDF0); + background: var(--color-bgColor-neutral-primary, #FFF); + box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.03), 0px 4px 4px 0px rgba(0, 0, 0, 0.04); + padding: var(--space-3, 6px) var(--space-4, 8px); + display: flex; + justify-content: center; + align-items: center; + gap: var(--gap-XXS, 4px); + cursor: pointer; + + transition: opacity 0.3s; +} + +#appwrite-preview-close { + position: absolute; + right: 0px; + bottom: 0px; + border-radius: var(--border-radius-S, 8px); + background: linear-gradient(270deg, #FFF 69.64%, rgba(255, 255, 255, 0.00) 114.29%); + height: 100%; + aspect-ratio: 1 / 1; + display: flex; + justify-content: center; + align-items: center; + opacity: 0; + transition: opacity 0.3s; +} + +#appwrite-preview-logo-dark { + display: none; +} + +#appwrite-preview:hover #appwrite-preview-close { + opacity: 1; +} + +#appwrite-preview-text { + padding: 0; + margin: 0; + color: var(--color-fgColor-neutral-secondary, #56565C); + font-family: var(--font-family-sansSerif, Inter); + font-size: var(--font-size-XS, 12px); + font-style: normal; + font-weight: 500; + line-height: 130%; + letter-spacing: -0.12px; +} + +@media (prefers-color-scheme: dark) { + #appwrite-preview { + border: var(--border-width-S, 1px) solid var(--color-border-neutral, #2D2D31); + background: var(--color-bgColor-neutral-primary, #1D1D21); + box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.03), 0px 4px 4px 0px rgba(0, 0, 0, 0.04); + } + + #appwrite-preview-text { + color: var(--color-fgColor-neutral-secondary, #C3C3C6); + font-family: var(--font-family-sansSerif, Inter); + font-size: var(--font-size-XS, 12px); + } + + #appwrite-preview-logo-dark { + display: block; + } + + #appwrite-preview-logo-light { + display: none; + } +} +`; + +document.head.appendChild(css); +document.body.appendChild(banner); \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Sites/Transformers/Preview.php b/src/Appwrite/Platform/Modules/Sites/Transformers/Preview.php new file mode 100644 index 0000000000..365ce70d18 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Sites/Transformers/Preview.php @@ -0,0 +1,58 @@ + $headers + */ + public static function isValid(array $headers): bool + { + $contentType = ''; + + foreach ($headers as $key => $value) { + if (\strtolower($key) === 'content-type') { + $contentType = $value; + break; + } + } + + if (\str_contains($contentType, 'text/html')) { + return true; + } + + return false; + } + + public function transform(): bool + { + $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; + $hostname = System::getEnv('_APP_DOMAIN'); + + // TODO: Temporary fix for development + if (App::isDevelopment()) { + $hostname = 'localhost'; + } + + $source = "{$protocol}://{$hostname}/scripts/preview.js"; + + $this->body .= ''; + + return true; + } + + public function getBody(): string + { + return $this->body; + } +} diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 277064ead5..88805317fd 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -238,4 +238,27 @@ trait SitesBase return $domain; } + + + protected function getDeploymentDomain(string $deploymentId): string + { + $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('resourceId', [$deploymentId])->toString(), + Query::equal('resourceType', ['deployment'])->toString(), + ], + ]); + + $this->assertEquals(200, $rules['headers']['status-code']); + $this->assertGreaterThanOrEqual(1, $rules['body']['total']); + $this->assertGreaterThanOrEqual(1, \count($rules['body']['rules'])); + $this->assertNotEmpty($rules['body']['rules'][0]['domain']); + + $domain = $rules['body']['rules'][0]['domain']; + + return $domain; + } } diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index ff3aa7b348..e471651ef6 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1088,5 +1088,64 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($site['body']['$id']); } + public function testSitePreviewBranding(): void + { + $siteId = $this->setupSite([ + 'siteId' => ID::unique(), + 'name' => 'A site', + 'framework' => 'other', + 'adapter' => 'static', + 'buildRuntime' => 'static-1', + 'outputDirectory' => './', + 'buildCommand' => '', + 'installCommand' => '', + 'fallbackFile' => '', + ]); + + $this->assertNotEmpty($siteId); + + $deploymentId = $this->setupDeployment($siteId, [ + 'code' => $this->packageSite('static'), + 'activate' => 'true' + ]); + + $this->assertNotEmpty($deploymentId); + + $domain = $this->getSiteDomain($siteId); + $previewDomain = $this->getDeploymentDomain($deploymentId); + + $this->assertNotEmpty($domain); + $this->assertNotEmpty($previewDomain); + + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertStringContainsString("Hello Appwrite", $response['body']); + $this->assertStringNotContainsString("setEndpoint('http://' . $previewDomain); + + $response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertStringContainsString("Hello Appwrite", $response['body']); + $this->assertStringContainsString("assertGreaterThan($contentLength, $response['headers']['content-length']); + + $this->cleanupSite($siteId); + } + // TODO: Add tests for deletion of resources when site is deleted } From 084b62116faf66d1f32e5402edb031e72f0b4a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 11 Feb 2025 14:26:49 +0100 Subject: [PATCH 2/5] Convert to library approach --- .github/workflows/static-analysis.yml | 16 ++++ app/controllers/general.php | 15 ++-- composer.json | 6 +- composer.lock | 73 +++++++++++++++++-- phpstan.neon | 8 ++ src/Appwrite/Transformation/Adapter.php | 25 +++++++ .../Adapter}/Preview.php | 29 +++----- .../Transformation/Transformation.php | 28 +++++++ .../Transformation/TransformationTest.php | 27 +++++++ 9 files changed, 193 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/static-analysis.yml create mode 100644 phpstan.neon create mode 100644 src/Appwrite/Transformation/Adapter.php rename src/Appwrite/{Platform/Modules/Sites/Transformers => Transformation/Adapter}/Preview.php (53%) create mode 100644 src/Appwrite/Transformation/Transformation.php create mode 100644 tests/unit/Transformation/TransformationTest.php diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000000..48b77eb4f8 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,16 @@ +name: "Static code analysis" + +on: [pull_request] +jobs: + lint: + name: CodeQL + runs-on: ubuntu-latest + + steps: + - name: Check out the repo + uses: actions/checkout@v2 + + - name: Run CodeQL + run: | + docker run --rm -v $PWD:/app composer:2.6 sh -c \ + "composer install --profile --ignore-platform-reqs && composer check" \ No newline at end of file diff --git a/app/controllers/general.php b/app/controllers/general.php index 18dc81a102..022e6ae1f7 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -11,11 +11,12 @@ use Appwrite\Event\Usage; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Network\Validator\Origin; use Appwrite\Platform\Appwrite; -use Appwrite\Platform\Modules\Sites\Transformers\Preview; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Transformation\Adapter\Preview; +use Appwrite\Transformation\Transformation; use Appwrite\Utopia\Request; use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; @@ -449,12 +450,14 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw // Branded banner for previews - if ($type === 'deployment' && Preview::isValid($executionResponse['headers'])) { - $transformer = new Preview($executionResponse['body']); - $transformer->transform(); - $executionResponse['body'] = $transformer->getBody(); + $transformation = new Transformation(new Preview($executionResponse['body'])); + if ($type === 'deployment' && $transformation->isValid($executionResponse['headers'])) { + $transformation->transform(); + $executionResponse['body'] = $transformation->getOutput(); + + + \var_dump($executionResponse['body']); - $execution->setAttribute('responseBody', $body); foreach ($executionResponse['headers'] as $key => $value) { if (\strtolower($key) === 'content-length') { $executionResponse['headers'][$key] = \strlen($executionResponse['body']); diff --git a/composer.json b/composer.json index 199229c105..7b94a3f22c 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "test": "vendor/bin/phpunit", "lint": "vendor/bin/pint --test", "format": "vendor/bin/pint", - "bench": "vendor/bin/phpbench run --report=benchmark" + "bench": "vendor/bin/phpbench run --report=benchmark", + "check": "./vendor/bin/phpstan analyse -c phpstan.neon" }, "autoload": { "psr-4": { @@ -89,7 +90,8 @@ "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.7", "laravel/pint": "^1.14", - "phpbench/phpbench": "^1.2" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "1.8.*" }, "provide": { "ext-phpiredis": "*" diff --git a/composer.lock b/composer.lock index 4f6d1a0bbe..526170ebde 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4da9bee4423753c2e592c081b19b8711", + "content-hash": "5473f6b65d54314228e69b6bed489d78", "packages": [ { "name": "adhocore/jwt", @@ -4490,16 +4490,16 @@ }, { "name": "utopia-php/queue", - "version": "0.8.2", + "version": "0.8.6", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "a6ec26a787e8292ca2d7b8f5a0ad179b46b2c4d0" + "reference": "b713b997285c29d120bbcbe3d6e93762d850f87c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/a6ec26a787e8292ca2d7b8f5a0ad179b46b2c4d0", - "reference": "a6ec26a787e8292ca2d7b8f5a0ad179b46b2c4d0", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/b713b997285c29d120bbcbe3d6e93762d850f87c", + "reference": "b713b997285c29d120bbcbe3d6e93762d850f87c", "shasum": "" }, "require": { @@ -4549,9 +4549,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.8.2" + "source": "https://github.com/utopia-php/queue/tree/0.8.6" }, - "time": "2025-02-06T11:01:15+00:00" + "time": "2025-02-10T03:35:00+00:00" }, { "name": "utopia-php/registry", @@ -6235,6 +6235,65 @@ }, "time": "2024-10-13T11:29:49+00:00" }, + { + "name": "phpstan/phpstan", + "version": "1.8.11", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "46e223dd68a620da18855c23046ddb00940b4014" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014", + "reference": "46e223dd68a620da18855c23046ddb00940b4014", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.8.11" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-10-24T15:45:13+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "9.2.32", diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000000..b18f3d6d58 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,8 @@ +parameters: + level: 8 + paths: + - src/Appwrite/Transformation + scanDirectories: + - vendor/swoole/ide-helper + excludePaths: + - tests/resources \ No newline at end of file diff --git a/src/Appwrite/Transformation/Adapter.php b/src/Appwrite/Transformation/Adapter.php new file mode 100644 index 0000000000..a80803b388 --- /dev/null +++ b/src/Appwrite/Transformation/Adapter.php @@ -0,0 +1,25 @@ + $traits + */ + abstract public function isValid(array $traits): bool; + + abstract public function transform(): bool; + + public function getOutput(): mixed + { + return $this->output; + } +} diff --git a/src/Appwrite/Platform/Modules/Sites/Transformers/Preview.php b/src/Appwrite/Transformation/Adapter/Preview.php similarity index 53% rename from src/Appwrite/Platform/Modules/Sites/Transformers/Preview.php rename to src/Appwrite/Transformation/Adapter/Preview.php index 365ce70d18..d95537c9d0 100644 --- a/src/Appwrite/Platform/Modules/Sites/Transformers/Preview.php +++ b/src/Appwrite/Transformation/Adapter/Preview.php @@ -1,26 +1,21 @@ $headers + * @param array $traits Proxied response headers */ - public static function isValid(array $headers): bool + public function isValid(array $traits): bool { $contentType = ''; - foreach ($headers as $key => $value) { + foreach ($traits as $key => $value) { if (\strtolower($key) === 'content-type') { $contentType = $value; break; @@ -39,20 +34,16 @@ class Preview $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; $hostname = System::getEnv('_APP_DOMAIN'); - // TODO: Temporary fix for development - if (App::isDevelopment()) { + // TODO: Find solution to this temporary fix + if (App::isDevelopment() && $hostname === 'traefik') { $hostname = 'localhost'; } $source = "{$protocol}://{$hostname}/scripts/preview.js"; - $this->body .= ''; + $this->output = $this->input; + $this->output .= ''; return true; } - - public function getBody(): string - { - return $this->body; - } } diff --git a/src/Appwrite/Transformation/Transformation.php b/src/Appwrite/Transformation/Transformation.php new file mode 100644 index 0000000000..db158f78fb --- /dev/null +++ b/src/Appwrite/Transformation/Transformation.php @@ -0,0 +1,28 @@ + $traits + */ + public function isValid(array $traits): bool + { + return $this->adapter->isValid($traits); + } + + public function transform(): bool + { + return $this->adapter->transform(); + } + + public function getOutput(): mixed + { + return $this->adapter->getOutput(); + } +} diff --git a/tests/unit/Transformation/TransformationTest.php b/tests/unit/Transformation/TransformationTest.php new file mode 100644 index 0000000000..6e12b1dc01 --- /dev/null +++ b/tests/unit/Transformation/TransformationTest.php @@ -0,0 +1,27 @@ +assertFalse($transformer->isValid([])); + $this->assertFalse($transformer->isValid(['content-type' => 'text/plain'])); + $this->assertFalse($transformer->isValid(['content-type' => 'tExT/HtML'])); + $this->assertTrue($transformer->isValid(['content-type' => 'text/html'])); + $this->assertTrue($transformer->isValid(['content-TYPE' => 'text/html'])); + $this->assertTrue($transformer->isValid(['content-TYPE' => 'text/plain, text/html; charset=utf-8'])); + + $this->assertTrue($transformer->transform()); + + $this->assertStringContainsString("Hello world", $transformer->getOutput()); + $this->assertStringContainsString("'; + + $banner = << + #appwrite-preview { + padding: 0; + margin: 0; + position: fixed; + right: 16px; + bottom: 16px; + z-index: 1; + border-radius: var(--border-radius-S, 8px); + border: var(--border-width-S, 1px) solid var(--color-border-neutral, #EDEDF0); + background: var(--color-bgColor-neutral-primary, #FFF); + box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.03), 0px 4px 4px 0px rgba(0, 0, 0, 0.04); + padding: var(--space-3, 6px) var(--space-4, 8px); + display: flex; + justify-content: center; + align-items: center; + gap: var(--gap-XXS, 4px); + cursor: pointer; + transition: opacity 0.3s; + } + + #appwrite-preview-close { + position: absolute; + right: 0px; + bottom: 0px; + border-radius: var(--border-radius-S, 8px); + background: linear-gradient(270deg, #FFF 69.64%, rgba(255, 255, 255, 0.00) 114.29%); + height: 100%; + aspect-ratio: 1 / 1; + display: flex; + justify-content: center; + align-items: center; + opacity: 0; + transition: opacity 0.3s; + } + + #appwrite-preview-logo-dark { + display: none; + } + + #appwrite-preview:hover #appwrite-preview-close { + opacity: 1; + } + + #appwrite-preview-text { + padding: 0; + margin: 0; + color: var(--color-fgColor-neutral-secondary, #56565C); + font-family: var(--font-family-sansSerif, Inter); + font-size: var(--font-size-XS, 12px); + font-style: normal; + font-weight: 500; + line-height: 130%; + letter-spacing: -0.12px; + } + + #appwrite-preview-close-text { + opacity: 0; + transition: opacity 0.3s; + position: absolute; + bottom: calc(15px + 4px); + display: flex; + padding: var(--space-1, 2px) var(--space-2, 4px); + color: var(--color-fgColor-neutral-secondary, #56565C); + text-align: center; + font-family: var(--font-family-sansSerif, Inter); + font-size: var(--font-size-XS, 12px); + font-style: normal; + font-weight: 400; + line-height: 130%; + letter-spacing: -0.12px; + border-radius: var(--border-radius-XS, 6px); + background: #EDEDF0; + } + + #appwrite-preview-close:hover #appwrite-preview-close-text { + opacity: 1; + } + + @media (prefers-color-scheme: dark) { + #appwrite-preview { + border: var(--border-width-S, 1px) solid var(--color-border-neutral, #2D2D31); + background: var(--color-bgColor-neutral-primary, #1D1D21); + box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.03), 0px 4px 4px 0px rgba(0, 0, 0, 0.04); + } + + #appwrite-preview-text { + color: var(--color-fgColor-neutral-secondary, #C3C3C6); + font-family: var(--font-family-sansSerif, Inter); + font-size: var(--font-size-XS, 12px); + } + + #appwrite-preview-logo-dark { + display: block; + } + + #appwrite-preview-logo-light { + display: none; + } + + #appwrite-preview-close { + background: linear-gradient(270deg, #1D1D21 69.64%, rgba(29, 29, 33, 0.00) 114.29%); + } + + #appwrite-preview-close-text { + background: #2D2D31; + color: var(--color-fgColor-neutral-secondary, #C3C3C6); + } + } + + + + + + EOT; + + $this->output .= $banner; return true; } diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index e471651ef6..9c25a772ea 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1127,7 +1127,7 @@ class SitesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertStringContainsString("Hello Appwrite", $response['body']); - $this->assertStringNotContainsString("assertStringNotContainsString("Preview by", $response['body']); $contentLength = $response['headers']['content-length']; @@ -1141,7 +1141,7 @@ class SitesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertStringContainsString("Hello Appwrite", $response['body']); - $this->assertStringContainsString("assertStringContainsString("Preview by", $response['body']); $this->assertGreaterThan($contentLength, $response['headers']['content-length']); $this->cleanupSite($siteId); diff --git a/tests/unit/Transformation/TransformationTest.php b/tests/unit/Transformation/TransformationTest.php index 6e12b1dc01..08f40fc5c7 100644 --- a/tests/unit/Transformation/TransformationTest.php +++ b/tests/unit/Transformation/TransformationTest.php @@ -22,6 +22,6 @@ class TransformationTest extends TestCase $this->assertTrue($transformer->transform()); $this->assertStringContainsString("Hello world", $transformer->getOutput()); - $this->assertStringContainsString("