Skip to content
Open
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
15 changes: 15 additions & 0 deletions .docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,18 @@ tracy:
bar:
- @swaggerPanel
```

The panel inlines its own copy of Swagger UI, so it renders the versions that copy
understands. Swagger UI renders OpenAPI 3.1 from 5.0.0 on.

To move the copy to another release:

```bash
php tools/update-swagger-ui.php 5.32.12
```

The script stores the scripts as distributed and confines every selector of the
stylesheet to `#tracy-debug`, because the panel renders inside a host page it must not
style. It aborts on a selector it cannot place rather than letting Swagger UI reach the
page, so a release that styles the page in a new way stops the update instead of
surprising an application.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ README.md export-ignore
phpstan.neon export-ignore
ruleset.xml export-ignore
tests export-ignore
tools export-ignore
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ qa: phpstan cs
.PHONY: cs
cs:
ifdef GITHUB_ACTION
vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp -q --report=checkstyle src tests | cs2pr
vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp -q --report=checkstyle src tests tools | cs2pr
else
vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp src tests
vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp src tests tools
endif

.PHONY: csf
csf:
vendor/bin/phpcbf --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp src tests
vendor/bin/phpcbf --standard=ruleset.xml --encoding=utf-8 --extensions="php,phpt" --colors -nsp src tests tools

.PHONY: phpstan
phpstan:
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ parameters:

paths:
- src
- tools
- .docs

ignoreErrors:
136 changes: 2 additions & 134 deletions src/Tracy/templates/assets/swagger-ui-bundle.js

Large diffs are not rendered by default.

24 changes: 2 additions & 22 deletions src/Tracy/templates/assets/swagger-ui-standalone-preset.js

Large diffs are not rendered by default.

10,905 changes: 2 additions & 10,903 deletions src/Tracy/templates/assets/swagger-ui.css

Large diffs are not rendered by default.

128 changes: 128 additions & 0 deletions tests/Cases/Tracy/SwaggerUiAssetsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php declare(strict_types = 1);

namespace Tests\Cases\Tracy;

use Tester\Assert;
use Tester\TestCase;

require_once __DIR__ . '/../../bootstrap.php';

/**
* The panel inlines Swagger UI into a host page, so its stylesheet has to stay
* confined to the debug bar. These tests guard the outcome of
* tools/update-swagger-ui.php, which is where the confinement is produced.
*/
class SwaggerUiAssetsTest extends TestCase
{

private const ASSETS = __DIR__ . '/../../../src/Tracy/templates/assets';

private const SCOPE = '#tracy-debug';

/**
* The oldest Swagger UI that renders OpenAPI 3.1, which the library supports.
*/
private const MINIMAL_VERSION = 5;

public function testBundleRendersTheVersionsTheLibrarySupports(): void
{
$bundle = $this->read('swagger-ui-bundle.js');

Assert::match(
'#PACKAGE_VERSION:"(\d+)\.#',
$bundle,
'The bundle states no version the way 5.x does. An older Swagger UI states it differently.',
);

preg_match('#PACKAGE_VERSION:"(\d+)\.#', $bundle, $matches);

Assert::true(
(int) $matches[1] >= self::MINIMAL_VERSION,
sprintf('Swagger UI %s renders OpenAPI 3.0 only; 3.1 needs 5.x.', $matches[1]),
);
}

public function testEverySelectorIsConfinedToTheDebugBar(): void
{
$unconfined = [];

foreach ($this->selectors() as $selector) {
if (!str_contains($selector, self::SCOPE)) {
$unconfined[] = $selector;
}
}

Assert::same([], $unconfined, 'These selectors would style the host page.');
}

/**
* Scoping a selector must not move it out of the descendant chain Swagger UI
* relies on, so the scope always sits directly in front of .swagger-ui.
*/
public function testTheScopeSitsInFrontOfTheSwaggerUiClass(): void
{
$misplaced = [];

foreach ($this->selectors() as $selector) {
if (!str_contains($selector, self::SCOPE . ' .swagger-ui')) {
$misplaced[] = $selector;
}
}

Assert::same([], $misplaced);
}

private function read(string $file): string
{
$path = self::ASSETS . '/' . $file;

Assert::true(is_file($path), sprintf('%s is missing.', $file));

return (string) file_get_contents($path);
}

/**
* Selectors of the stylesheet, excluding keyframe positions, which name steps
* of an animation rather than elements.
*
* @return list<string>
*/
private function selectors(): array
{
$css = $this->read('swagger-ui.css');

// Drop keyframe blocks wholesale: their positions ("from", "50%") are not
// selectors, and the rules they contain are reached through the animation.
$css = (string) preg_replace('#@keyframes[^{]*\{(?:[^{}]*\{[^{}]*\})*[^{}]*\}#', '', $css);
$css = (string) preg_replace('#/\*.*?\*/#s', '', $css);

preg_match_all('#(?:^|[{}])([^{}]+)\{#', $css, $matches);

$selectors = [];

foreach ($matches[1] as $prelude) {
$prelude = trim($prelude);

// Conditional groups carry a condition, not a selector; their contents
// are matched separately by the same pass.
if (str_starts_with($prelude, '@')) {
continue;
}

foreach (explode(',', $prelude) as $selector) {
$selector = trim($selector);

if ($selector !== '') {
$selectors[] = $selector;
}
}
}

Assert::notSame([], $selectors, 'No selectors were found, so these tests prove nothing.');

return $selectors;
}

}

(new SwaggerUiAssetsTest())->run();
Loading