162 lines
5.7 KiB
PHP
162 lines
5.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Resources\BackupScheduleResource;
|
|
use App\Filament\Resources\ProviderConnectionResource;
|
|
use App\Models\BackupSchedule;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
pest()->browser()->timeout(60_000);
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
function spec402BrowserLoginUrl(User $user, ManagedEnvironment $tenant, string $redirect): string
|
|
{
|
|
return route('admin.local.smoke-login', [
|
|
'email' => $user->email,
|
|
'tenant' => $tenant->external_id,
|
|
'workspace' => $tenant->workspace->slug,
|
|
'redirect' => $redirect,
|
|
]);
|
|
}
|
|
|
|
function spec402BrowserPath(string $url): string
|
|
{
|
|
$parts = parse_url($url);
|
|
|
|
return ($parts['path'] ?? '/admin').(isset($parts['query']) ? '?'.$parts['query'] : '');
|
|
}
|
|
|
|
function spec402BrowserVisibleActionsDisabledScript(array $labels): string
|
|
{
|
|
$encodedLabels = json_encode(array_values($labels), JSON_THROW_ON_ERROR);
|
|
|
|
return <<<JS
|
|
(() => {
|
|
const labels = {$encodedLabels};
|
|
const candidates = Array.from(document.querySelectorAll('button, a, [role="button"], [role="menuitem"]'));
|
|
|
|
const visible = (element) => {
|
|
const style = window.getComputedStyle(element);
|
|
|
|
return style.display !== 'none'
|
|
&& style.visibility !== 'hidden'
|
|
&& ! element.hidden
|
|
&& Boolean(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
|
};
|
|
|
|
const normalizedText = (element) => [
|
|
element.textContent || '',
|
|
element.getAttribute('aria-label') || '',
|
|
element.getAttribute('title') || '',
|
|
].join(' ').replace(/\\s+/g, ' ').trim();
|
|
|
|
const disabled = (element) => {
|
|
const disabledOwner = element.closest('[disabled], [aria-disabled="true"]');
|
|
const href = element.tagName === 'A' ? element.getAttribute('href') : null;
|
|
|
|
return Boolean(disabledOwner)
|
|
|| element.disabled === true
|
|
|| element.getAttribute('aria-disabled') === 'true'
|
|
|| (element.tagName === 'A' && (href === null || href === '' || href === '#'));
|
|
};
|
|
|
|
return labels.every((label) => candidates.some((element) => (
|
|
visible(element)
|
|
&& normalizedText(element).includes(label)
|
|
&& disabled(element)
|
|
)));
|
|
})()
|
|
JS;
|
|
}
|
|
|
|
it('Spec402 smokes provider resource authorization boundaries in the browser', function (): void {
|
|
[$owner, $tenant] = createUserWithTenant(role: 'owner', workspaceRole: 'owner');
|
|
|
|
$connection = ProviderConnection::factory()
|
|
->platform()
|
|
->verifiedHealthy()
|
|
->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'provider' => 'microsoft',
|
|
'display_name' => 'Spec 402 Browser Provider',
|
|
'is_enabled' => true,
|
|
]);
|
|
|
|
$backupSchedule = BackupSchedule::query()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'name' => 'Spec 402 Browser Backup Schedule',
|
|
'is_enabled' => true,
|
|
'timezone' => 'UTC',
|
|
'frequency' => 'daily',
|
|
'time_of_day' => '01:00:00',
|
|
'days_of_week' => null,
|
|
'policy_types' => ['deviceConfiguration'],
|
|
'include_foundations' => true,
|
|
'retention_keep_last' => 30,
|
|
]);
|
|
|
|
$providerPath = spec402BrowserPath(ProviderConnectionResource::getUrl('view', [
|
|
'record' => $connection,
|
|
'environment_id' => (int) $tenant->getKey(),
|
|
], panel: 'admin'));
|
|
$backupPath = spec402BrowserPath(BackupScheduleResource::getUrl('index', panel: 'admin', tenant: $tenant));
|
|
|
|
visit(spec402BrowserLoginUrl($owner, $tenant, $providerPath))
|
|
->resize(1440, 1000)
|
|
->waitForText('Spec 402 Browser Provider')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
|
|
[$readonly] = createUserWithTenant(tenant: $tenant, role: 'readonly', workspaceRole: 'readonly');
|
|
|
|
$readonlyPage = visit(spec402BrowserLoginUrl($readonly, $tenant, $backupPath))
|
|
->resize(1440, 1000)
|
|
->waitForText($backupSchedule->name)
|
|
->assertDontSee('Run now')
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
|
|
expect(\App\Models\OperationRun::query()
|
|
->where('managed_environment_id', (int) $tenant->getKey())
|
|
->where('type', 'backup.schedule.execute')
|
|
->count())->toBe(0);
|
|
|
|
visit(spec402BrowserLoginUrl($readonly, $tenant, $providerPath))
|
|
->resize(1440, 1000)
|
|
->waitForText('Spec 402 Browser Provider')
|
|
->assertSee('Run provider verification')
|
|
->click('[aria-label="More"]')
|
|
->wait(1)
|
|
->assertSee('Edit')
|
|
->assertSee('Inventory sync')
|
|
->assertSee('Compliance snapshot')
|
|
->assertScript(spec402BrowserVisibleActionsDisabledScript([
|
|
'Run provider verification',
|
|
'Edit',
|
|
'Inventory sync',
|
|
'Compliance snapshot',
|
|
]), true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
|
|
[$otherUser, $otherTenant] = createUserWithTenant(role: 'owner', workspaceRole: 'owner');
|
|
|
|
visit(spec402BrowserLoginUrl($otherUser, $otherTenant, $backupPath))
|
|
->resize(1440, 1000)
|
|
->assertScript('document.body.innerText.includes("404") || document.body.innerText.includes("Not Found") || document.body.innerText.includes("No access")', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
|
|
visit('/system')
|
|
->resize(1440, 1000)
|
|
->assertScript('document.body.innerText.includes("404") || document.body.innerText.includes("Not Found") || document.body.innerText.includes("No access")', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
});
|