mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-14 00:33:44 -04:00
168 lines
7.5 KiB
PHP
168 lines
7.5 KiB
PHP
<?php
|
|
|
|
use App\Models\GithubApp;
|
|
use App\Models\S3Storage;
|
|
use App\Models\ScheduledDatabaseBackup;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
$this->stack = seedBrowserResourceStack();
|
|
$this->application = createBrowserApplication($this->stack, [
|
|
'uuid' => 'app-palette-settings',
|
|
'name' => 'Palette App',
|
|
]);
|
|
});
|
|
|
|
it('shows the settings of the open resource in the command palette and switches scope with Tab', function () {
|
|
loginAndSkipBoarding();
|
|
|
|
$page = visit(applicationConfigurationUrl($this->stack['project'], $this->stack['environment'], $this->application));
|
|
$page->assertSee('Palette App')
|
|
->assertMissing('[aria-label="Filter settings"]')
|
|
->assertVisible('button:visible[title^="Search this page"]');
|
|
|
|
$page->script("window.dispatchEvent(new Event('open-global-search'))");
|
|
|
|
$page->assertVisible('.command-palette-input')
|
|
->assertAttribute('.command-palette-scope-option:has-text("Global")', 'aria-pressed', 'true')
|
|
->assertAttribute('.command-palette-scope-option:has-text("This page")', 'aria-pressed', 'false')
|
|
->type('.command-palette-input', 'runtime')
|
|
->assertSeeIn('.command-palette-body', 'This page')
|
|
->assertSeeIn('.command-palette-body', 'Runtime Logs')
|
|
->assertSeeIn('.command-palette-body', 'Settings · General')
|
|
->keys('.command-palette-input', 'Tab')
|
|
->assertAttribute('.command-palette-scope-option:has-text("This page")', 'aria-pressed', 'true')
|
|
->assertAttribute('.command-palette-scope-option:has-text("Global")', 'aria-pressed', 'false')
|
|
->assertScript("document.activeElement === document.querySelector('.command-palette-input')")
|
|
->type('.command-palette-input', 'zzz-nothing')
|
|
->assertSeeIn('.command-palette-body', 'Nothing on this page')
|
|
->type('.command-palette-input', 'runtime logs')
|
|
->screenshot(filename: 'command-palette-this-page-scope')
|
|
->click('.command-palette-body a.search-result-item')
|
|
->assertPathEndsWith('/logs')
|
|
->screenshot(filename: 'command-palette-opened-runtime-logs');
|
|
});
|
|
|
|
it('does not show the scope switch on pages without a settings sidebar', function () {
|
|
loginAndSkipBoarding();
|
|
|
|
$page = visit('/dashboard');
|
|
$page->assertMissing('button[title^="Search this page"]');
|
|
$page->script("window.dispatchEvent(new Event('open-global-search'))");
|
|
|
|
$page->assertVisible('.command-palette-input')
|
|
->assertMissing('.command-palette-scope')
|
|
->screenshot(filename: 'command-palette-without-scope');
|
|
});
|
|
|
|
it('focuses the search input when the palette is opened from the mobile menu', function () {
|
|
loginAndSkipBoarding();
|
|
|
|
$page = visit(applicationConfigurationUrl($this->stack['project'], $this->stack['environment'], $this->application))
|
|
->on()->mobile();
|
|
|
|
$page->assertSee('Palette App')
|
|
->click('button:visible:has-text("Open sidebar")')
|
|
->assertVisible('button:visible[title^="Search this page"]')
|
|
->screenshot(filename: 'command-palette-mobile-menu')
|
|
->click('button:visible:has-text("Search")')
|
|
->assertVisible('.command-palette-input')
|
|
->wait(0.3)
|
|
->assertScript("document.activeElement === document.querySelector('.command-palette-input')")
|
|
->assertVisible('.command-palette-scope')
|
|
->screenshot(filename: 'command-palette-mobile');
|
|
});
|
|
|
|
function commandPaletteSettingsPageUrl(string $page, array $stack): string
|
|
{
|
|
return match ($page) {
|
|
'instance settings' => '/settings/oauth',
|
|
'team' => '/team',
|
|
'keys and tokens' => '/security/private-key',
|
|
'notifications' => '/notifications/email',
|
|
'shared variables' => '/shared-variables',
|
|
'destination' => '/destination/'.$stack['destination']->uuid,
|
|
's3 storage' => '/storages/'.S3Storage::create([
|
|
'team_id' => 0, 'name' => 'Palette S3', 'region' => 'us-east-1', 'key' => 'key', 'secret' => 'secret',
|
|
'bucket' => 'bucket', 'endpoint' => 'https://s3.example.com',
|
|
])->uuid,
|
|
'github app' => '/source/github/'.GithubApp::create([
|
|
'name' => 'palette-app', 'organization' => 'acme', 'api_url' => 'https://api.github.com',
|
|
'html_url' => 'https://github.com', 'custom_user' => 'git', 'custom_port' => 22, 'app_id' => 1234,
|
|
'installation_id' => 5678, 'webhook_secret' => 'secret', 'private_key_id' => $stack['privateKey']->id,
|
|
'team_id' => 0, 'is_system_wide' => false,
|
|
])->uuid,
|
|
'database backup' => (function () use ($stack): string {
|
|
$database = createBrowserPostgresql($stack, ['uuid' => 'db-palette-backup']);
|
|
$backup = ScheduledDatabaseBackup::create([
|
|
'team_id' => 0, 'frequency' => '0 0 * * *', 'database_type' => $database->getMorphClass(),
|
|
'database_id' => $database->id,
|
|
]);
|
|
|
|
return "/project/{$stack['project']->uuid}/environment/{$stack['environment']->uuid}/database/{$database->uuid}/backups/{$backup->uuid}";
|
|
})(),
|
|
};
|
|
}
|
|
|
|
it('searches the sidebar pages of other settings pages', function (string $pageType, string $expectedLabel) {
|
|
$url = commandPaletteSettingsPageUrl($pageType, $this->stack);
|
|
|
|
loginAndSkipBoarding();
|
|
|
|
$page = visit($url);
|
|
$page->assertVisible('button:visible[title^="Search this page"]');
|
|
$page->script("window.dispatchEvent(new Event('open-global-search'))");
|
|
|
|
$page->assertVisible('.command-palette-scope')
|
|
->keys('.command-palette-input', 'Tab')
|
|
->assertSeeIn('.command-palette-body', $expectedLabel)
|
|
->screenshot(filename: 'command-palette-page-'.str($pageType)->slug());
|
|
})->with([
|
|
['instance settings', 'Updates'],
|
|
['team', 'Members'],
|
|
['keys and tokens', 'API Tokens'],
|
|
['notifications', 'Telegram'],
|
|
['shared variables', 'Environments'],
|
|
['destination', 'Danger Zone'],
|
|
['s3 storage', 'Resources'],
|
|
['github app', 'Permissions'],
|
|
['database backup', 'Retention'],
|
|
]);
|
|
|
|
it('searches the sections of a page without a settings sidebar and scrolls to them', function () {
|
|
loginAndSkipBoarding();
|
|
|
|
$page = visit('/profile');
|
|
$page->assertVisible('button:visible[title^="Search this page"]');
|
|
$page->script("window.dispatchEvent(new Event('open-global-search'))");
|
|
|
|
$page->assertVisible('.command-palette-scope')
|
|
->keys('.command-palette-input', 'Tab')
|
|
->type('.command-palette-input', 'two-factor')
|
|
->assertSeeIn('.command-palette-body', 'Two-factor authentication')
|
|
->click('.command-palette-body a.search-result-item')
|
|
->assertMissing('.command-palette-input')
|
|
->wait(1)
|
|
->assertScript(<<<'JS'
|
|
() => {
|
|
const heading = [...document.querySelectorAll('.application-settings-section h2')]
|
|
.find(el => el.textContent.includes('Two-factor authentication'));
|
|
const top = heading.getBoundingClientRect().top;
|
|
|
|
return top >= 0 && top < window.innerHeight;
|
|
}
|
|
JS)
|
|
->screenshot(filename: 'command-palette-profile-section');
|
|
});
|
|
|
|
it('lists a section once when the sidebar already links to it', function () {
|
|
loginAndSkipBoarding();
|
|
|
|
$page = visit(applicationConfigurationUrl($this->stack['project'], $this->stack['environment'], $this->application));
|
|
$page->assertSee('Palette App');
|
|
|
|
expect($page->script("window.currentPageSearchItems().filter(item => item.label === 'Container labels').length"))->toBe(1);
|
|
});
|