chore(web): prune dead ui cleanup artifacts
|
Before Width: | Height: | Size: 285 KiB |
@@ -1,101 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const session = {
|
||||
subjectId: 'user-author',
|
||||
tenant: 'tenant-default',
|
||||
scopes: [
|
||||
'ui.read',
|
||||
'policy:read',
|
||||
'policy:author',
|
||||
'policy:simulate',
|
||||
'advisory-ai:view',
|
||||
'advisory-ai:operate',
|
||||
'findings:read',
|
||||
'vex:read',
|
||||
'admin',
|
||||
],
|
||||
};
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({
|
||||
headless: true,
|
||||
args: ['--no-sandbox', '--no-proxy-server'],
|
||||
});
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
|
||||
const navHistory = [];
|
||||
const httpErrors = [];
|
||||
const failures = [];
|
||||
let currentUrl = '';
|
||||
|
||||
page.on('framenavigated', (frame) => {
|
||||
if (frame !== page.mainFrame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = `${new Date().toISOString()} ${frame.url()}`;
|
||||
navHistory.push(entry);
|
||||
console.log('[nav]', entry);
|
||||
});
|
||||
|
||||
page.on('response', (response) => {
|
||||
if (response.status() < 400) {
|
||||
return;
|
||||
}
|
||||
|
||||
const request = response.request();
|
||||
const entry = `${response.status()} ${request.method()} ${response.url()}`;
|
||||
httpErrors.push(entry);
|
||||
console.log('[http-error]', entry);
|
||||
});
|
||||
|
||||
page.on('requestfailed', (request) => {
|
||||
const entry = `${request.method()} ${request.url()} :: ${request.failure()?.errorText ?? 'failed'}`;
|
||||
failures.push(entry);
|
||||
console.log('[requestfailed]', entry);
|
||||
});
|
||||
|
||||
page.on('console', (msg) => {
|
||||
if (msg.type() === 'error') {
|
||||
console.log('[console-error]', msg.text());
|
||||
}
|
||||
});
|
||||
|
||||
await page.addInitScript((stubSession) => {
|
||||
window.__stellaopsTestSession = stubSession;
|
||||
}, session);
|
||||
|
||||
const target = process.argv[2] ?? 'https://stella-ops.local/';
|
||||
console.log('[goto]', target);
|
||||
|
||||
try {
|
||||
await page.goto(target, { waitUntil: 'commit', timeout: 20000 });
|
||||
} catch (error) {
|
||||
console.log('[goto-error]', error.message);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 20; i += 1) {
|
||||
const url = page.url();
|
||||
if (url !== currentUrl) {
|
||||
currentUrl = url;
|
||||
console.log('[url-change]', url);
|
||||
}
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
const searchInputCount = await page
|
||||
.evaluate(() => document.querySelectorAll('app-global-search input[type="text"]').length)
|
||||
.catch(() => -1);
|
||||
|
||||
console.log('[final-url]', page.url());
|
||||
console.log('[title]', await page.title().catch(() => '<title unavailable>'));
|
||||
console.log('[search-input-count]', searchInputCount);
|
||||
console.log('[nav-count]', navHistory.length);
|
||||
console.log('[http-error-count]', httpErrors.length);
|
||||
console.log('[failed-request-count]', failures.length);
|
||||
|
||||
await page.screenshot({ path: 'output/playwright/stella-ops-local-load-check-viewport.png' });
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -1,66 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const session = {
|
||||
subjectId: 'user-author',
|
||||
tenant: 'tenant-default',
|
||||
scopes: ['ui.read', 'policy:read', 'policy:author', 'policy:simulate', 'advisory:search', 'advisory:read', 'search:read', 'findings:read', 'vex:read', 'admin'],
|
||||
};
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
|
||||
page.on('requestfailed', (request) => {
|
||||
const url = request.url();
|
||||
if (url.includes('/search')) {
|
||||
console.log('[requestfailed]', request.method(), url, request.failure()?.errorText);
|
||||
}
|
||||
});
|
||||
|
||||
page.on('response', (response) => {
|
||||
const url = response.url();
|
||||
if (
|
||||
url.includes('/api/v1/search/query') ||
|
||||
url.includes('/api/v1/advisory-ai/search') ||
|
||||
url.includes('/api/v1/advisory-ai/search/analytics')
|
||||
) {
|
||||
const req = response.request();
|
||||
console.log('[response]', req.method(), response.status(), url);
|
||||
}
|
||||
});
|
||||
|
||||
await page.addInitScript((stubSession) => {
|
||||
window.__stellaopsTestSession = stubSession;
|
||||
}, session);
|
||||
|
||||
const url = process.argv[2] || 'https://stella-ops.local/';
|
||||
console.log('[goto]', url);
|
||||
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const count = await page.evaluate(() => document.querySelectorAll('app-global-search input[type="text"]').length);
|
||||
console.log('[search-input-count]', count);
|
||||
|
||||
if (count === 0) {
|
||||
console.log('[page-url]', page.url());
|
||||
console.log('[title]', await page.title());
|
||||
await page.screenshot({ path: 'output/playwright/header-search-repro-no-input.png', fullPage: true });
|
||||
await browser.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await page.click('app-global-search input[type="text"]', { timeout: 15000 });
|
||||
await page.fill('app-global-search input[type="text"]', 'critical findings', { timeout: 15000 });
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
const results = await page.evaluate(() => document.querySelectorAll('app-entity-card').length);
|
||||
const emptyText = await page.locator('.search__empty').allTextContents();
|
||||
const degradedVisible = await page.locator('.search__degraded-banner').isVisible().catch(() => false);
|
||||
console.log('[entity-cards]', results);
|
||||
console.log('[empty-text]', emptyText.join(' | '));
|
||||
console.log('[degraded-banner]', degradedVisible);
|
||||
|
||||
await page.screenshot({ path: 'output/playwright/header-search-repro-live.png', fullPage: true });
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -1,66 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const session = {
|
||||
subjectId: 'user-author',
|
||||
tenant: 'tenant-default',
|
||||
scopes: ['ui.read','policy:read','policy:author','policy:simulate','advisory:search','advisory:read','search:read','findings:read','vex:read','admin']
|
||||
};
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
|
||||
page.on('requestfailed', (request) => {
|
||||
const url = request.url();
|
||||
if (url.includes('/search')) {
|
||||
console.log('[requestfailed]', request.method(), url, request.failure()?.errorText);
|
||||
}
|
||||
});
|
||||
|
||||
page.on('response', (response) => {
|
||||
const url = response.url();
|
||||
if (
|
||||
url.includes('/api/v1/search/query') ||
|
||||
url.includes('/api/v1/advisory-ai/search') ||
|
||||
url.includes('/api/v1/advisory-ai/search/analytics')
|
||||
) {
|
||||
const req = response.request();
|
||||
console.log('[response]', req.method(), response.status(), url);
|
||||
}
|
||||
});
|
||||
|
||||
await page.addInitScript((stubSession) => {
|
||||
window.__stellaopsTestSession = stubSession;
|
||||
}, session);
|
||||
|
||||
const url = process.argv[2] || 'https://stella-ops.local:10000/';
|
||||
console.log('[goto]', url);
|
||||
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const count = await page.evaluate(() => document.querySelectorAll('app-global-search input[type="text"]').length);
|
||||
console.log('[search-input-count]', count);
|
||||
|
||||
if (count === 0) {
|
||||
console.log('[page-url]', page.url());
|
||||
console.log('[title]', await page.title());
|
||||
await page.screenshot({ path: 'output/playwright/header-search-repro-no-input.png', fullPage: true });
|
||||
await browser.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await page.click('app-global-search input[type="text"]', { timeout: 15000 });
|
||||
await page.fill('app-global-search input[type="text"]', 'critical findings', { timeout: 15000 });
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
const results = await page.evaluate(() => document.querySelectorAll('app-entity-card').length);
|
||||
const emptyText = await page.locator('.search__empty').allTextContents();
|
||||
const degradedVisible = await page.locator('.search__degraded-banner').isVisible().catch(() => false);
|
||||
console.log('[entity-cards]', results);
|
||||
console.log('[empty-text]', emptyText.join(' | '));
|
||||
console.log('[degraded-banner]', degradedVisible);
|
||||
|
||||
await page.screenshot({ path: 'output/playwright/header-search-repro.png', fullPage: true });
|
||||
await browser.close();
|
||||
})();
|
||||
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 79 KiB |
@@ -1,82 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-release-control-governance-hub',
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<section class="governance-hub">
|
||||
<header class="header">
|
||||
<h1>Governance</h1>
|
||||
<p>Policy and exception controls anchored under Release Control.</p>
|
||||
</header>
|
||||
|
||||
<div class="cards">
|
||||
<a routerLink="/ops/policy/baselines" class="card">
|
||||
<h2>Policy Baselines</h2>
|
||||
<p>Environment-scoped baseline definitions and lock rules.</p>
|
||||
</a>
|
||||
<a routerLink="/ops/policy/gates" class="card">
|
||||
<h2>Governance Rules</h2>
|
||||
<p>Rule catalog for release control gate enforcement.</p>
|
||||
</a>
|
||||
<a routerLink="/ops/policy/simulation" class="card">
|
||||
<h2>Policy Simulation</h2>
|
||||
<p>Dry-run policy evaluations before production rollout.</p>
|
||||
</a>
|
||||
<a routerLink="/ops/policy/waivers" class="card">
|
||||
<h2>Exception Workflow</h2>
|
||||
<p>Exception requests, approvals, and expiry management.</p>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.governance-hub {
|
||||
display: grid;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0 0 0.2rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
.cards {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid var(--color-border-primary);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface-primary);
|
||||
padding: 0.72rem 0.8rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin: 0 0 0.25rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class ReleaseControlGovernanceHubComponent {}
|
||||
@@ -1,59 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-release-control-governance-section',
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<section class="governance-section">
|
||||
<header>
|
||||
<h1>{{ sectionTitle() }}</h1>
|
||||
<p>This governance area is scaffolded and ready for backend contract binding.</p>
|
||||
</header>
|
||||
|
||||
<p class="note">Canonical location: Release Control > Governance.</p>
|
||||
<a routerLink="/ops/policy">Back to Governance Hub</a>
|
||||
</section>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.governance-section {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 0.2rem;
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 0.82rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.note {
|
||||
border: 1px solid var(--color-border-primary);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface-primary);
|
||||
padding: 0.55rem 0.65rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-brand-primary);
|
||||
text-decoration: none;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class ReleaseControlGovernanceSectionComponent {
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
|
||||
readonly sectionTitle = signal(
|
||||
(this.route.snapshot.data['sectionTitle'] as string | undefined) ?? 'Governance'
|
||||
);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export const RELEASE_CONTROL_GOVERNANCE_ROUTES: Routes = [
|
||||
{
|
||||
path: '',
|
||||
title: 'Governance',
|
||||
data: { breadcrumb: 'Governance' },
|
||||
loadComponent: () =>
|
||||
import('./release-control-governance-hub.component').then(
|
||||
(m) => m.ReleaseControlGovernanceHubComponent
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'baselines',
|
||||
title: 'Policy Baselines',
|
||||
data: { breadcrumb: 'Policy Baselines', sectionTitle: 'Policy Baselines' },
|
||||
loadComponent: () =>
|
||||
import('./release-control-governance-section.component').then(
|
||||
(m) => m.ReleaseControlGovernanceSectionComponent
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'rules',
|
||||
title: 'Governance Rules',
|
||||
data: { breadcrumb: 'Governance Rules', sectionTitle: 'Governance Rules' },
|
||||
loadComponent: () =>
|
||||
import('./release-control-governance-section.component').then(
|
||||
(m) => m.ReleaseControlGovernanceSectionComponent
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'simulation',
|
||||
title: 'Policy Simulation',
|
||||
data: { breadcrumb: 'Policy Simulation', sectionTitle: 'Policy Simulation' },
|
||||
loadComponent: () =>
|
||||
import('./release-control-governance-section.component').then(
|
||||
(m) => m.ReleaseControlGovernanceSectionComponent
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'exceptions',
|
||||
title: 'Exception Workflow',
|
||||
data: { breadcrumb: 'Exception Workflow', sectionTitle: 'Exception Workflow' },
|
||||
loadComponent: () =>
|
||||
import('./release-control-governance-section.component').then(
|
||||
(m) => m.ReleaseControlGovernanceSectionComponent
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -1,129 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
|
||||
interface EnvironmentNode {
|
||||
id: string;
|
||||
stage: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-region-detail',
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<section class="region-detail">
|
||||
<header class="header">
|
||||
<h1>{{ regionLabel() }} Region</h1>
|
||||
<p>Pipeline posture by environment with promotion flow context.</p>
|
||||
</header>
|
||||
|
||||
<section class="summary">
|
||||
<article>
|
||||
<span>Total environments</span>
|
||||
<strong>{{ environments.length }}</strong>
|
||||
</article>
|
||||
<article>
|
||||
<span>Overall health</span>
|
||||
<strong>DEGRADED</strong>
|
||||
</article>
|
||||
<article>
|
||||
<span>SBOM posture</span>
|
||||
<strong>WARN</strong>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="pipeline">
|
||||
@for (env of environments; track env.id) {
|
||||
<a class="pipeline-node" [routerLink]="['/releases/environments', regionLabel(), 'environments', env.id]">
|
||||
<h2>{{ env.id }}</h2>
|
||||
<p>{{ env.stage }}</p>
|
||||
<p>Status: {{ env.status }}</p>
|
||||
</a>
|
||||
}
|
||||
</section>
|
||||
</section>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.region-detail {
|
||||
display: grid;
|
||||
gap: 0.9rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0 0 0.2rem;
|
||||
font-size: 1.42rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
.summary {
|
||||
display: grid;
|
||||
gap: 0.65rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
|
||||
}
|
||||
|
||||
.summary article {
|
||||
border: 1px solid var(--color-border-primary);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface-primary);
|
||||
padding: 0.6rem 0.7rem;
|
||||
}
|
||||
|
||||
.summary span {
|
||||
display: block;
|
||||
font-size: 0.72rem;
|
||||
color: var(--color-text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.summary strong {
|
||||
font-size: 1.08rem;
|
||||
}
|
||||
|
||||
.pipeline {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
|
||||
}
|
||||
|
||||
.pipeline-node {
|
||||
border: 1px solid var(--color-border-primary);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface-primary);
|
||||
padding: 0.65rem 0.75rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.pipeline-node h2 {
|
||||
margin: 0 0 0.2rem;
|
||||
font-size: 0.98rem;
|
||||
}
|
||||
|
||||
.pipeline-node p {
|
||||
margin: 0.14rem 0;
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class RegionDetailComponent {
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
|
||||
readonly regionLabel = signal(this.route.snapshot.paramMap.get('region') ?? 'global');
|
||||
|
||||
readonly environments: EnvironmentNode[] = [
|
||||
{ id: 'dev', stage: 'Development', status: 'HEALTHY' },
|
||||
{ id: 'stage', stage: 'Staging', status: 'HEALTHY' },
|
||||
{ id: 'prod', stage: 'Production', status: 'DEGRADED' },
|
||||
];
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
interface RegionCard {
|
||||
id: string;
|
||||
name: string;
|
||||
envCount: number;
|
||||
health: string;
|
||||
sbomPosture: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-regions-overview',
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<section class="regions-overview">
|
||||
<header class="header">
|
||||
<h1>Regions & Environments</h1>
|
||||
<p>Region-first release control posture with environment health and SBOM coverage context.</p>
|
||||
</header>
|
||||
|
||||
<div class="cards">
|
||||
@for (region of regions; track region.id) {
|
||||
<a class="card" [routerLink]="['/releases/environments', region.id]">
|
||||
<h2>{{ region.name }}</h2>
|
||||
<p>Environments: {{ region.envCount }}</p>
|
||||
<p>Health: {{ region.health }}</p>
|
||||
<p>SBOM posture: {{ region.sbomPosture }}</p>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.regions-overview {
|
||||
display: grid;
|
||||
gap: 0.9rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0 0 0.2rem;
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
.cards {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid var(--color-border-primary);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface-primary);
|
||||
padding: 0.75rem 0.85rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin: 0 0 0.35rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0.15rem 0;
|
||||
font-size: 0.82rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class RegionsOverviewComponent {
|
||||
readonly regions: RegionCard[] = [
|
||||
{
|
||||
id: 'global',
|
||||
name: 'Global',
|
||||
envCount: 4,
|
||||
health: 'DEGRADED',
|
||||
sbomPosture: 'WARN',
|
||||
},
|
||||
{
|
||||
id: 'eu-west',
|
||||
name: 'EU West',
|
||||
envCount: 3,
|
||||
health: 'HEALTHY',
|
||||
sbomPosture: 'OK',
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
interface SetupArea {
|
||||
title: string;
|
||||
description: string;
|
||||
route: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-release-control-setup-home',
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<section class="setup-home">
|
||||
<header class="header">
|
||||
<h1>Release Control Setup</h1>
|
||||
<p>
|
||||
Canonical setup hub for environments, promotion paths, targets, agents, workflows, and
|
||||
bundle templates.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<p class="state-banner">
|
||||
Read-only structural mode: setup contracts are shown with deterministic placeholders until
|
||||
backend setup APIs are wired.
|
||||
</p>
|
||||
|
||||
<section class="areas" aria-label="Setup areas">
|
||||
@for (area of areas; track area.route) {
|
||||
<a class="card" [routerLink]="area.route">
|
||||
<h2>{{ area.title }}</h2>
|
||||
<p>{{ area.description }}</p>
|
||||
</a>
|
||||
}
|
||||
</section>
|
||||
|
||||
<section class="legacy-map" aria-label="Legacy setup aliases">
|
||||
<h2>Legacy Setup Aliases</h2>
|
||||
<ul>
|
||||
<li><code>/settings/release-control</code> redirects to <code>/release-control/setup</code></li>
|
||||
<li>
|
||||
<code>/settings/release-control/environments</code> redirects to
|
||||
<code>/release-control/setup/environments-paths</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>/settings/release-control/targets</code> and <code>/settings/release-control/agents</code>
|
||||
redirect to <code>/release-control/setup/targets-agents</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>/settings/release-control/workflows</code> redirects to
|
||||
<code>/release-control/setup/workflows</code>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
`,
|
||||
styles: [`
|
||||
.setup-home {
|
||||
padding: 1.5rem;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0 0 0.25rem;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.state-banner {
|
||||
margin: 0;
|
||||
border: 1px solid var(--color-status-warning-border, #facc15);
|
||||
background: var(--color-status-warning-bg, #fffbeb);
|
||||
color: var(--color-status-warning-text, #854d0e);
|
||||
border-radius: var(--radius-md, 8px);
|
||||
padding: 0.7rem 0.85rem;
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
|
||||
.areas {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: block;
|
||||
border: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
background: var(--color-surface-primary, #fff);
|
||||
border-radius: var(--radius-md, 8px);
|
||||
padding: 0.9rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
border-color: var(--color-brand-primary, #2563eb);
|
||||
box-shadow: 0 3px 10px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin: 0 0 0.25rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.legacy-map {
|
||||
border: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
border-radius: var(--radius-md, 8px);
|
||||
padding: 0.9rem;
|
||||
background: var(--color-surface-primary, #fff);
|
||||
}
|
||||
|
||||
.legacy-map h2 {
|
||||
margin: 0 0 0.6rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.legacy-map ul {
|
||||
margin: 0;
|
||||
padding-left: 1.1rem;
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
font-size: 0.83rem;
|
||||
}
|
||||
`],
|
||||
})
|
||||
export class ReleaseControlSetupHomeComponent {
|
||||
readonly areas: SetupArea[] = [
|
||||
{
|
||||
title: 'Environments and Promotion Paths',
|
||||
description: 'Define environment hierarchy and promotion routes (Dev -> Stage -> Prod).',
|
||||
route: '/release-control/setup/environments-paths',
|
||||
},
|
||||
{
|
||||
title: 'Targets and Agents',
|
||||
description: 'Track runtime targets and execution agents used by release deployments.',
|
||||
route: '/release-control/setup/targets-agents',
|
||||
},
|
||||
{
|
||||
title: 'Workflows',
|
||||
description: 'Review workflow templates and promotion execution steps before activation.',
|
||||
route: '/release-control/setup/workflows',
|
||||
},
|
||||
{
|
||||
title: 'Bundle Templates',
|
||||
description: 'Manage default bundle composition templates and validation requirements.',
|
||||
route: '/release-control/setup/bundle-templates',
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-setup-bundle-templates',
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<section class="page">
|
||||
<header class="header">
|
||||
<a routerLink="/ops/platform-setup" class="back-link">Back to Setup</a>
|
||||
<h1>Bundle Templates</h1>
|
||||
<p>Template presets for bundle composition, validation gates, and release metadata policy.</p>
|
||||
</header>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Template Catalog</h2>
|
||||
<table aria-label="Bundle templates">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Template</th>
|
||||
<th>Required Sections</th>
|
||||
<th>Validation Profile</th>
|
||||
<th>Default Use</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>service-platform</td><td>digest, config, changelog, evidence</td><td>strict</td><td>platform releases</td></tr>
|
||||
<tr><td>edge-hotfix</td><td>digest, changelog, evidence</td><td>fast-track</td><td>hotfix bundle</td></tr>
|
||||
<tr><td>regional-rollout</td><td>digest, config, promotion path, evidence</td><td>risk-aware</td><td>multi-region rollout</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Template Rules</h2>
|
||||
<ul>
|
||||
<li>Template controls required builder sections before bundle version materialization.</li>
|
||||
<li>Validation profile maps to policy and advisory confidence requirements.</li>
|
||||
<li>Template changes apply only to newly created bundle versions (immutability preserved).</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="panel links">
|
||||
<h2>Related Surfaces</h2>
|
||||
<a routerLink="/releases/bundles/create">Open Bundle Builder</a>
|
||||
<a routerLink="/releases/bundles">Open Bundle Catalog</a>
|
||||
</section>
|
||||
</section>
|
||||
`,
|
||||
styles: [`
|
||||
.page {
|
||||
padding: 1.5rem;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
font-size: 0.84rem;
|
||||
color: var(--color-brand-primary, #2563eb);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0.25rem 0 0.2rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
border-radius: var(--radius-md, 8px);
|
||||
padding: 0.85rem;
|
||||
background: var(--color-surface-primary, #fff);
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
margin: 0 0 0.6rem;
|
||||
font-size: 0.96rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
padding: 0.45rem 0.35rem;
|
||||
border-top: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
}
|
||||
|
||||
th {
|
||||
border-top: 0;
|
||||
font-size: 0.73rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 1.1rem;
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.links a {
|
||||
color: var(--color-brand-primary, #2563eb);
|
||||
text-decoration: none;
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
`],
|
||||
})
|
||||
export class SetupBundleTemplatesComponent {}
|
||||
@@ -1,133 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-setup-environments-paths',
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<section class="page">
|
||||
<header class="header">
|
||||
<a routerLink="/ops/platform-setup" class="back-link">Back to Setup</a>
|
||||
<h1>Environments and Promotion Paths</h1>
|
||||
<p>Release Control-owned environment graph and allowed promotion flows.</p>
|
||||
</header>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Environment Inventory</h2>
|
||||
<table aria-label="Environment inventory">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Environment</th>
|
||||
<th>Region</th>
|
||||
<th>Risk Tier</th>
|
||||
<th>Promotion Entry</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>dev-us-east</td><td>us-east</td><td>low</td><td>yes</td></tr>
|
||||
<tr><td>stage-eu-west</td><td>eu-west</td><td>medium</td><td>yes</td></tr>
|
||||
<tr><td>prod-eu-west</td><td>eu-west</td><td>high</td><td>yes</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Promotion Path Rules</h2>
|
||||
<ul>
|
||||
<li><code>dev-*</code> can promote to <code>stage-*</code> with approval gates.</li>
|
||||
<li><code>stage-*</code> can promote to <code>prod-*</code> only with policy + ops gate pass.</li>
|
||||
<li>Cross-region promotion requires an explicit path definition and target parity checks.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="panel links">
|
||||
<h2>Related Surfaces</h2>
|
||||
<a routerLink="/releases/environments">Open Regions and Environments</a>
|
||||
<a routerLink="/releases/approvals">Open Promotions</a>
|
||||
</section>
|
||||
</section>
|
||||
`,
|
||||
styles: [`
|
||||
.page {
|
||||
padding: 1.5rem;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
font-size: 0.84rem;
|
||||
color: var(--color-brand-primary, #2563eb);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0.25rem 0 0.2rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
border-radius: var(--radius-md, 8px);
|
||||
padding: 0.85rem;
|
||||
background: var(--color-surface-primary, #fff);
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
margin: 0 0 0.6rem;
|
||||
font-size: 0.96rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
padding: 0.45rem 0.35rem;
|
||||
border-top: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
}
|
||||
|
||||
th {
|
||||
border-top: 0;
|
||||
font-size: 0.73rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 1.1rem;
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.links a {
|
||||
color: var(--color-brand-primary, #2563eb);
|
||||
text-decoration: none;
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
`],
|
||||
})
|
||||
export class SetupEnvironmentsPathsComponent {}
|
||||
@@ -1,136 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-setup-targets-agents',
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<section class="page">
|
||||
<header class="header">
|
||||
<a routerLink="/ops/platform-setup" class="back-link">Back to Setup</a>
|
||||
<h1>Targets and Agents</h1>
|
||||
<p>Release Control deployment execution topology with ownership split to Integrations.</p>
|
||||
</header>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Deployment Targets</h2>
|
||||
<table aria-label="Deployment targets">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Target</th>
|
||||
<th>Runtime</th>
|
||||
<th>Region</th>
|
||||
<th>Agent Group</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>edge-gateway-prod</td><td>vm</td><td>eu-west</td><td>agent-eu</td><td>ready</td></tr>
|
||||
<tr><td>payments-core-stage</td><td>nomad</td><td>us-east</td><td>agent-us</td><td>ready</td></tr>
|
||||
<tr><td>billing-svc-prod</td><td>ecs</td><td>eu-west</td><td>agent-eu</td><td>degraded</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Agent Coverage</h2>
|
||||
<ul>
|
||||
<li><strong>agent-eu</strong>: 42 targets, heartbeat every 20s, upgrade window Fri 23:00 UTC.</li>
|
||||
<li><strong>agent-us</strong>: 35 targets, heartbeat every 20s, upgrade window Sat 01:00 UTC.</li>
|
||||
<li><strong>agent-apac</strong>: 18 targets, on-call watch enabled, runtime drift checks active.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="panel links">
|
||||
<h2>Ownership Links</h2>
|
||||
<a routerLink="/integrations/hosts">
|
||||
Connector connectivity and credentials are managed in Integrations > Targets / Runtimes
|
||||
</a>
|
||||
<a routerLink="/platform-ops/agents">Operational status and diagnostics are managed in Platform Ops > Agents</a>
|
||||
</section>
|
||||
</section>
|
||||
`,
|
||||
styles: [`
|
||||
.page {
|
||||
padding: 1.5rem;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
font-size: 0.84rem;
|
||||
color: var(--color-brand-primary, #2563eb);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0.25rem 0 0.2rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
border-radius: var(--radius-md, 8px);
|
||||
padding: 0.85rem;
|
||||
background: var(--color-surface-primary, #fff);
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
margin: 0 0 0.6rem;
|
||||
font-size: 0.96rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
padding: 0.45rem 0.35rem;
|
||||
border-top: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
}
|
||||
|
||||
th {
|
||||
border-top: 0;
|
||||
font-size: 0.73rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 1.1rem;
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.links a {
|
||||
color: var(--color-brand-primary, #2563eb);
|
||||
text-decoration: none;
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
`],
|
||||
})
|
||||
export class SetupTargetsAgentsComponent {}
|
||||
@@ -1,133 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-setup-workflows',
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<section class="page">
|
||||
<header class="header">
|
||||
<a routerLink="/ops/platform-setup" class="back-link">Back to Setup</a>
|
||||
<h1>Workflows</h1>
|
||||
<p>Release Control workflow definitions for promotion orchestration and approval sequencing.</p>
|
||||
</header>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Workflow Catalog</h2>
|
||||
<table aria-label="Workflow catalog">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Workflow</th>
|
||||
<th>Path</th>
|
||||
<th>Gate Profile</th>
|
||||
<th>Rollback</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>standard-blue-green</td><td>dev -> stage -> prod</td><td>strict-prod</td><td>auto</td></tr>
|
||||
<tr><td>canary-regional</td><td>stage -> prod-canary -> prod</td><td>risk-aware</td><td>manual</td></tr>
|
||||
<tr><td>hotfix-fast-track</td><td>stage -> prod</td><td>expedited</td><td>manual</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Execution Constraints</h2>
|
||||
<ul>
|
||||
<li>All workflows require a bundle version digest and resolved inputs before promotion launch.</li>
|
||||
<li>Approval checkpoints inherit policy gates from Administration policy governance baseline.</li>
|
||||
<li>Run timeline evidence checkpoints are mandatory for promotion completion.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="panel links">
|
||||
<h2>Related Surfaces</h2>
|
||||
<a routerLink="/administration/workflows">Open legacy workflow editor surface</a>
|
||||
<a routerLink="/releases/runs">Open Run Timeline</a>
|
||||
</section>
|
||||
</section>
|
||||
`,
|
||||
styles: [`
|
||||
.page {
|
||||
padding: 1.5rem;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
font-size: 0.84rem;
|
||||
color: var(--color-brand-primary, #2563eb);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0.25rem 0 0.2rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
border-radius: var(--radius-md, 8px);
|
||||
padding: 0.85rem;
|
||||
background: var(--color-surface-primary, #fff);
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
margin: 0 0 0.6rem;
|
||||
font-size: 0.96rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
padding: 0.45rem 0.35rem;
|
||||
border-top: 1px solid var(--color-border-primary, #e4e7ec);
|
||||
}
|
||||
|
||||
th {
|
||||
border-top: 0;
|
||||
font-size: 0.73rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 1.1rem;
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
color: var(--color-text-secondary, #667085);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.links a {
|
||||
color: var(--color-brand-primary, #2563eb);
|
||||
text-decoration: none;
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
`],
|
||||
})
|
||||
export class SetupWorkflowsComponent {}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) Stella Ops. All rights reserved. SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export const workflowVisualizationRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
loadComponent: () =>
|
||||
import('./components/workflow-visualizer/workflow-visualizer.component').then(
|
||||
(m) => m.WorkflowVisualizerComponent
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -1,72 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { ReleaseControlSetupHomeComponent } from '../../app/features/release-control/setup/release-control-setup-home.component';
|
||||
import { SetupBundleTemplatesComponent } from '../../app/features/release-control/setup/setup-bundle-templates.component';
|
||||
import { SetupEnvironmentsPathsComponent } from '../../app/features/release-control/setup/setup-environments-paths.component';
|
||||
import { SetupTargetsAgentsComponent } from '../../app/features/release-control/setup/setup-targets-agents.component';
|
||||
import { SetupWorkflowsComponent } from '../../app/features/release-control/setup/setup-workflows.component';
|
||||
|
||||
describe('Release Control setup components (release-control)', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ReleaseControlSetupHomeComponent,
|
||||
SetupEnvironmentsPathsComponent,
|
||||
SetupTargetsAgentsComponent,
|
||||
SetupWorkflowsComponent,
|
||||
SetupBundleTemplatesComponent,
|
||||
],
|
||||
providers: [provideRouter([])],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('setup home renders required setup areas', () => {
|
||||
const fixture = TestBed.createComponent(ReleaseControlSetupHomeComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const text = fixture.nativeElement.textContent as string;
|
||||
expect(text).toContain('Release Control Setup');
|
||||
expect(text).toContain('Environments and Promotion Paths');
|
||||
expect(text).toContain('Targets and Agents');
|
||||
expect(text).toContain('Workflows');
|
||||
expect(text).toContain('Bundle Templates');
|
||||
});
|
||||
|
||||
it('environments and paths page renders inventory and path rules', () => {
|
||||
const fixture = TestBed.createComponent(SetupEnvironmentsPathsComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const text = fixture.nativeElement.textContent as string;
|
||||
expect(text).toContain('Environment Inventory');
|
||||
expect(text).toContain('Promotion Path Rules');
|
||||
});
|
||||
|
||||
it('targets and agents page renders ownership links', () => {
|
||||
const fixture = TestBed.createComponent(SetupTargetsAgentsComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const text = fixture.nativeElement.textContent as string;
|
||||
expect(text).toContain('Targets and Agents');
|
||||
expect(text).toContain('Integrations > Targets / Runtimes');
|
||||
expect(text).toContain('Platform Ops > Agents');
|
||||
});
|
||||
|
||||
it('workflows page renders workflow catalog and run timeline link', () => {
|
||||
const fixture = TestBed.createComponent(SetupWorkflowsComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const text = fixture.nativeElement.textContent as string;
|
||||
expect(text).toContain('Workflow Catalog');
|
||||
expect(text).toContain('Open Run Timeline');
|
||||
});
|
||||
|
||||
it('bundle templates page renders template catalog and builder link', () => {
|
||||
const fixture = TestBed.createComponent(SetupBundleTemplatesComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const text = fixture.nativeElement.textContent as string;
|
||||
expect(text).toContain('Template Catalog');
|
||||
expect(text).toContain('Open Bundle Builder');
|
||||
});
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svgjs="http://svgjs.com/svgjs" xmlns:xlink="http://www.w3.org/1999/xlink" width="164" height="164" version="1.1"><svg xmlns="http://www.w3.org/2000/svg" width="164" height="164" fill="none" viewBox="0 0 164 164"><path fill="#FF4785" d="M22.467 147.762 17.5 15.402a8.062 8.062 0 0 1 7.553-8.35L137.637.016a8.061 8.061 0 0 1 8.565 8.047v144.23a8.063 8.063 0 0 1-8.424 8.054l-107.615-4.833a8.062 8.062 0 0 1-7.695-7.752Z"/><path fill="#fff" fill-rule="evenodd" d="m128.785.57-15.495.968-.755 18.172a1.203 1.203 0 0 0 1.928 1.008l7.06-5.354 5.962 4.697a1.202 1.202 0 0 0 1.946-.987L128.785.569Zm-12.059 60.856c-2.836 2.203-23.965 3.707-23.965.57.447-11.969-4.912-12.494-7.889-12.494-2.828 0-7.59.855-7.59 7.267 0 6.534 6.96 10.223 15.13 14.553 11.607 6.15 25.654 13.594 25.654 32.326 0 17.953-14.588 27.871-33.194 27.871-19.201 0-35.981-7.769-34.086-34.702.744-3.163 25.156-2.411 25.156 0-.298 11.114 2.232 14.383 8.633 14.383 4.912 0 7.144-2.708 7.144-7.267 0-6.9-7.252-10.973-15.595-15.657C64.827 81.933 51.53 74.468 51.53 57.34c0-17.098 11.76-28.497 32.747-28.497 20.988 0 32.449 11.224 32.449 32.584Z" clip-rule="evenodd"/></svg><style>@media (prefers-color-scheme:light){:root{filter:none}}</style></svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,177 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<title>@storybook/angular - Storybook</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
|
||||
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('./sb-common-assets/nunito-sans-regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('./sb-common-assets/nunito-sans-italic.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('./sb-common-assets/nunito-sans-bold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nunito Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('./sb-common-assets/nunito-sans-bold-italic.woff2') format('woff2');
|
||||
}
|
||||
</style>
|
||||
|
||||
<link href="./sb-manager/runtime.js" rel="modulepreload" />
|
||||
|
||||
|
||||
<link href="./sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/essentials-controls-1/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/essentials-actions-2/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/essentials-docs-3/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/essentials-backgrounds-4/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/essentials-viewport-5/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/essentials-toolbars-6/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/essentials-measure-7/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/essentials-outline-8/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/a11y-9/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
<link href="./sb-addons/interactions-10/manager-bundle.js" rel="modulepreload" />
|
||||
|
||||
|
||||
<style>
|
||||
#storybook-root[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
window['FEATURES'] = {
|
||||
"argTypeTargetsV7": true,
|
||||
"legacyDecoratorFileOrder": false,
|
||||
"disallowImplicitActionsInRenderV8": true
|
||||
};
|
||||
|
||||
|
||||
|
||||
window['REFS'] = {};
|
||||
|
||||
|
||||
|
||||
window['LOGLEVEL'] = "info";
|
||||
|
||||
|
||||
|
||||
window['DOCS_OPTIONS'] = {
|
||||
"defaultName": "Docs",
|
||||
"autodocs": "tag"
|
||||
};
|
||||
|
||||
|
||||
|
||||
window['CONFIG_TYPE'] = "PRODUCTION";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
window['TAGS_OPTIONS'] = {
|
||||
"dev-only": {
|
||||
"excludeFromDocsStories": true
|
||||
},
|
||||
"docs-only": {
|
||||
"excludeFromSidebar": true
|
||||
},
|
||||
"test-only": {
|
||||
"excludeFromSidebar": true,
|
||||
"excludeFromDocsStories": true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
window['STORYBOOK_RENDERER'] = "angular";
|
||||
|
||||
|
||||
|
||||
window['STORYBOOK_BUILDER'] = "@storybook/builder-webpack5";
|
||||
|
||||
|
||||
|
||||
window['STORYBOOK_FRAMEWORK'] = "@storybook/angular";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<script type="module">
|
||||
import './sb-manager/globals-runtime.js';
|
||||
|
||||
|
||||
import './sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js';
|
||||
|
||||
import './sb-addons/essentials-controls-1/manager-bundle.js';
|
||||
|
||||
import './sb-addons/essentials-actions-2/manager-bundle.js';
|
||||
|
||||
import './sb-addons/essentials-docs-3/manager-bundle.js';
|
||||
|
||||
import './sb-addons/essentials-backgrounds-4/manager-bundle.js';
|
||||
|
||||
import './sb-addons/essentials-viewport-5/manager-bundle.js';
|
||||
|
||||
import './sb-addons/essentials-toolbars-6/manager-bundle.js';
|
||||
|
||||
import './sb-addons/essentials-measure-7/manager-bundle.js';
|
||||
|
||||
import './sb-addons/essentials-outline-8/manager-bundle.js';
|
||||
|
||||
import './sb-addons/a11y-9/manager-bundle.js';
|
||||
|
||||
import './sb-addons/interactions-10/manager-bundle.js';
|
||||
|
||||
|
||||
import './sb-manager/runtime.js';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
{"generatedAt":1770045794032,"userSince":1770045051724,"hasCustomBabel":false,"hasCustomWebpack":false,"hasStaticDirs":false,"hasStorybookEslint":false,"refCount":0,"testPackages":{"@axe-core/playwright":"4.8.4","@playwright/test":"1.56.1","@types/jasmine":"5.1.12","jasmine-core":"5.1.2","karma":"6.4.4","karma-chrome-launcher":"3.2.0","karma-coverage":"2.2.1","karma-jasmine":"5.1.0","karma-jasmine-html-reporter":"2.1.0"},"hasRouterPackage":true,"packageManager":{"type":"npm","agent":"npm"},"preview":{"usesGlobals":true},"framework":{"name":"@storybook/angular","options":{}},"builder":"@storybook/builder-webpack5","renderer":"@storybook/angular","portableStoriesFileCount":0,"applicationFileCount":58,"storybookVersion":"8.6.14","storybookVersionSpecifier":"^8.6.14","language":"typescript","storybookPackages":{"@chromatic-com/storybook":{"version":"5.0.0"},"@storybook/angular":{"version":"8.6.14"},"@storybook/test":{"version":"8.6.14"},"storybook":{"version":"8.6.14"}},"addons":{"@storybook/addon-essentials":{"version":"8.6.14"},"@storybook/addon-a11y":{"version":"8.6.14"},"@storybook/addon-interactions":{"version":"8.6.14"}}}
|
||||
@@ -1,3 +0,0 @@
|
||||
try{
|
||||
(()=>{var T=__STORYBOOK_API__,{ActiveTabs:h,Consumer:g,ManagerContext:f,Provider:v,RequestResponseError:A,addons:n,combineParameters:x,controlOrMetaKey:P,controlOrMetaSymbol:k,eventMatchesShortcut:M,eventToShortcut:R,experimental_MockUniversalStore:C,experimental_UniversalStore:U,experimental_requestResponse:w,experimental_useUniversalStore:B,isMacLike:E,isShortcutTaken:I,keyToSymbol:K,merge:N,mockChannel:G,optionOrAltSymbol:L,shortcutMatchesShortcut:Y,shortcutToHumanString:q,types:D,useAddonState:F,useArgTypes:H,useArgs:j,useChannel:V,useGlobalTypes:z,useGlobals:J,useParameter:Q,useSharedState:W,useStoryPrepared:X,useStorybookApi:Z,useStorybookState:$}=__STORYBOOK_API__;var S=(()=>{let e;return typeof window<"u"?e=window:typeof globalThis<"u"?e=globalThis:typeof window<"u"?e=window:typeof self<"u"?e=self:e={},e})(),c="tag-filters",p="static-filter";n.register(c,e=>{let u=Object.entries(S.TAGS_OPTIONS??{}).reduce((t,r)=>{let[o,i]=r;return i.excludeFromSidebar&&(t[o]=!0),t},{});e.experimental_setFilter(p,t=>{let r=t.tags??[];return(r.includes("dev")||t.type==="docs")&&r.filter(o=>u[o]).length===0})});})();
|
||||
}catch(e){ console.error("[Storybook] One of your manager-entries failed: " + import.meta.url, e); }
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svgjs="http://svgjs.com/svgjs" xmlns:xlink="http://www.w3.org/1999/xlink" width="164" height="164" version="1.1"><svg xmlns="http://www.w3.org/2000/svg" width="164" height="164" fill="none" viewBox="0 0 164 164"><path fill="#FF4785" d="M22.467 147.762 17.5 15.402a8.062 8.062 0 0 1 7.553-8.35L137.637.016a8.061 8.061 0 0 1 8.565 8.047v144.23a8.063 8.063 0 0 1-8.424 8.054l-107.615-4.833a8.062 8.062 0 0 1-7.695-7.752Z"/><path fill="#fff" fill-rule="evenodd" d="m128.785.57-15.495.968-.755 18.172a1.203 1.203 0 0 0 1.928 1.008l7.06-5.354 5.962 4.697a1.202 1.202 0 0 0 1.946-.987L128.785.569Zm-12.059 60.856c-2.836 2.203-23.965 3.707-23.965.57.447-11.969-4.912-12.494-7.889-12.494-2.828 0-7.59.855-7.59 7.267 0 6.534 6.96 10.223 15.13 14.553 11.607 6.15 25.654 13.594 25.654 32.326 0 17.953-14.588 27.871-33.194 27.871-19.201 0-35.981-7.769-34.086-34.702.744-3.163 25.156-2.411 25.156 0-.298 11.114 2.232 14.383 8.633 14.383 4.912 0 7.144-2.708 7.144-7.267 0-6.9-7.252-10.973-15.595-15.657C64.827 81.933 51.53 74.468 51.53 57.34c0-17.098 11.76-28.497 32.747-28.497 20.988 0 32.449 11.224 32.449 32.584Z" clip-rule="evenodd"/></svg><style>@media (prefers-color-scheme:light){:root{filter:none}}</style></svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,48 +0,0 @@
|
||||
import ESM_COMPAT_Module from "node:module";
|
||||
import { fileURLToPath as ESM_COMPAT_fileURLToPath } from 'node:url';
|
||||
import { dirname as ESM_COMPAT_dirname } from 'node:path';
|
||||
const __filename = ESM_COMPAT_fileURLToPath(import.meta.url);
|
||||
const __dirname = ESM_COMPAT_dirname(__filename);
|
||||
const require = ESM_COMPAT_Module.createRequire(import.meta.url);
|
||||
|
||||
// src/manager/globals/globals.ts
|
||||
var _ = {
|
||||
react: "__REACT__",
|
||||
"react-dom": "__REACT_DOM__",
|
||||
"react-dom/client": "__REACT_DOM_CLIENT__",
|
||||
"@storybook/icons": "__STORYBOOK_ICONS__",
|
||||
"storybook/internal/manager-api": "__STORYBOOK_API__",
|
||||
"@storybook/manager-api": "__STORYBOOK_API__",
|
||||
"@storybook/core/manager-api": "__STORYBOOK_API__",
|
||||
"storybook/internal/components": "__STORYBOOK_COMPONENTS__",
|
||||
"@storybook/components": "__STORYBOOK_COMPONENTS__",
|
||||
"@storybook/core/components": "__STORYBOOK_COMPONENTS__",
|
||||
"storybook/internal/channels": "__STORYBOOK_CHANNELS__",
|
||||
"@storybook/channels": "__STORYBOOK_CHANNELS__",
|
||||
"@storybook/core/channels": "__STORYBOOK_CHANNELS__",
|
||||
"storybook/internal/core-errors": "__STORYBOOK_CORE_EVENTS__",
|
||||
"@storybook/core-events": "__STORYBOOK_CORE_EVENTS__",
|
||||
"@storybook/core/core-events": "__STORYBOOK_CORE_EVENTS__",
|
||||
"storybook/internal/manager-errors": "__STORYBOOK_CORE_EVENTS_MANAGER_ERRORS__",
|
||||
"@storybook/core-events/manager-errors": "__STORYBOOK_CORE_EVENTS_MANAGER_ERRORS__",
|
||||
"@storybook/core/manager-errors": "__STORYBOOK_CORE_EVENTS_MANAGER_ERRORS__",
|
||||
"storybook/internal/router": "__STORYBOOK_ROUTER__",
|
||||
"@storybook/router": "__STORYBOOK_ROUTER__",
|
||||
"@storybook/core/router": "__STORYBOOK_ROUTER__",
|
||||
"storybook/internal/theming": "__STORYBOOK_THEMING__",
|
||||
"@storybook/theming": "__STORYBOOK_THEMING__",
|
||||
"@storybook/core/theming": "__STORYBOOK_THEMING__",
|
||||
"storybook/internal/theming/create": "__STORYBOOK_THEMING_CREATE__",
|
||||
"@storybook/theming/create": "__STORYBOOK_THEMING_CREATE__",
|
||||
"@storybook/core/theming/create": "__STORYBOOK_THEMING_CREATE__",
|
||||
"storybook/internal/client-logger": "__STORYBOOK_CLIENT_LOGGER__",
|
||||
"@storybook/client-logger": "__STORYBOOK_CLIENT_LOGGER__",
|
||||
"@storybook/core/client-logger": "__STORYBOOK_CLIENT_LOGGER__",
|
||||
"storybook/internal/types": "__STORYBOOK_TYPES__",
|
||||
"@storybook/types": "__STORYBOOK_TYPES__",
|
||||
"@storybook/core/types": "__STORYBOOK_TYPES__"
|
||||
}, o = Object.keys(_);
|
||||
export {
|
||||
o as globalPackages,
|
||||
_ as globalsNameReferenceMap
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
import ESM_COMPAT_Module from "node:module";
|
||||
import { fileURLToPath as ESM_COMPAT_fileURLToPath } from 'node:url';
|
||||
import { dirname as ESM_COMPAT_dirname } from 'node:path';
|
||||
const __filename = ESM_COMPAT_fileURLToPath(import.meta.url);
|
||||
const __dirname = ESM_COMPAT_dirname(__filename);
|
||||
const require = ESM_COMPAT_Module.createRequire(import.meta.url);
|
||||
|
||||
// src/preview/globals/globals.ts
|
||||
var _ = {
|
||||
"@storybook/global": "__STORYBOOK_MODULE_GLOBAL__",
|
||||
"storybook/internal/channels": "__STORYBOOK_MODULE_CHANNELS__",
|
||||
"@storybook/channels": "__STORYBOOK_MODULE_CHANNELS__",
|
||||
"@storybook/core/channels": "__STORYBOOK_MODULE_CHANNELS__",
|
||||
"storybook/internal/client-logger": "__STORYBOOK_MODULE_CLIENT_LOGGER__",
|
||||
"@storybook/client-logger": "__STORYBOOK_MODULE_CLIENT_LOGGER__",
|
||||
"@storybook/core/client-logger": "__STORYBOOK_MODULE_CLIENT_LOGGER__",
|
||||
"storybook/internal/core-events": "__STORYBOOK_MODULE_CORE_EVENTS__",
|
||||
"@storybook/core-events": "__STORYBOOK_MODULE_CORE_EVENTS__",
|
||||
"@storybook/core/core-events": "__STORYBOOK_MODULE_CORE_EVENTS__",
|
||||
"storybook/internal/preview-errors": "__STORYBOOK_MODULE_CORE_EVENTS_PREVIEW_ERRORS__",
|
||||
"@storybook/core-events/preview-errors": "__STORYBOOK_MODULE_CORE_EVENTS_PREVIEW_ERRORS__",
|
||||
"@storybook/core/preview-errors": "__STORYBOOK_MODULE_CORE_EVENTS_PREVIEW_ERRORS__",
|
||||
"storybook/internal/preview-api": "__STORYBOOK_MODULE_PREVIEW_API__",
|
||||
"@storybook/preview-api": "__STORYBOOK_MODULE_PREVIEW_API__",
|
||||
"@storybook/core/preview-api": "__STORYBOOK_MODULE_PREVIEW_API__",
|
||||
"storybook/internal/types": "__STORYBOOK_MODULE_TYPES__",
|
||||
"@storybook/types": "__STORYBOOK_MODULE_TYPES__",
|
||||
"@storybook/core/types": "__STORYBOOK_MODULE_TYPES__"
|
||||
}, O = Object.keys(_);
|
||||
export {
|
||||
O as globalPackages,
|
||||
_ as globalsNameReferenceMap
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true, args: ['--disable-dev-shm-usage'] });
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
const events = [];
|
||||
|
||||
const push = (kind, payload) => events.push({ ts: new Date().toISOString(), kind, ...payload, page: page.url() });
|
||||
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') {
|
||||
push('console_error', { text: msg.text() });
|
||||
}
|
||||
});
|
||||
|
||||
page.on('requestfailed', request => {
|
||||
const url = request.url();
|
||||
if (/\.(css|js|map|png|jpg|jpeg|svg|woff2?)($|\?)/i.test(url)) return;
|
||||
push('request_failed', {
|
||||
method: request.method(),
|
||||
url,
|
||||
error: request.failure()?.errorText ?? 'unknown'
|
||||
});
|
||||
});
|
||||
|
||||
page.on('response', response => {
|
||||
const url = response.url();
|
||||
if (/\.(css|js|map|png|jpg|jpeg|svg|woff2?)($|\?)/i.test(url)) return;
|
||||
if (response.status() >= 400) {
|
||||
push('response_error', { status: response.status(), method: response.request().method(), url });
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('https://stella-ops.local/welcome', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(1200);
|
||||
const cta = page.locator('button.cta').first();
|
||||
if (await cta.count()) {
|
||||
await cta.click({ force: true, noWaitAfter: true });
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
if (page.url().includes('/connect/authorize')) {
|
||||
await page.locator('input[name="username"]').first().fill('admin');
|
||||
await page.locator('input[name="password"]').first().fill('Admin@Stella2026!');
|
||||
await page.locator('button[type="submit"], button:has-text("Sign In")').first().click();
|
||||
await page.waitForURL(url => !url.toString().includes('/connect/authorize'), { timeout: 20000 });
|
||||
await page.waitForTimeout(1200);
|
||||
}
|
||||
|
||||
for (const path of ['/evidence/proof-chains', '/policy/packs']) {
|
||||
await page.goto(`https://stella-ops.local${path}`, { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(6000);
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(events, null, 2));
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -1,49 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const BASE='https://stella-ops.local';
|
||||
const USER='admin';
|
||||
const PASS='Admin@Stella2026!';
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true, args:['--disable-dev-shm-usage'] });
|
||||
const ctx = await browser.newContext({ ignoreHTTPSErrors: true, viewport:{width:1511,height:864} });
|
||||
const page = await ctx.newPage();
|
||||
|
||||
const failed=[];
|
||||
const responses=[];
|
||||
page.on('requestfailed', req => {
|
||||
const url=req.url();
|
||||
if (/\.(css|js|map|png|jpg|jpeg|svg|woff2?)($|\?)/i.test(url)) return;
|
||||
failed.push({ url, method:req.method(), error:req.failure()?.errorText || 'unknown', page: page.url() });
|
||||
});
|
||||
page.on('response', res => {
|
||||
const url=res.url();
|
||||
if (/\.(css|js|map|png|jpg|jpeg|svg|woff2?)($|\?)/i.test(url)) return;
|
||||
if (res.status() >= 400) {
|
||||
responses.push({ status: res.status(), method: res.request().method(), url, page: page.url() });
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto(`${BASE}/welcome`, { waitUntil:'domcontentloaded' });
|
||||
await page.waitForTimeout(1200);
|
||||
const cta = page.locator('button.cta').first();
|
||||
if (await cta.count()) {
|
||||
await cta.click({ force:true, noWaitAfter:true });
|
||||
await page.waitForTimeout(1200);
|
||||
}
|
||||
if (page.url().includes('/connect/authorize')) {
|
||||
await page.locator('input[name="username"]').first().fill(USER);
|
||||
await page.locator('input[name="password"]').first().fill(PASS);
|
||||
await page.locator('button[type="submit"], button:has-text("Sign In")').first().click();
|
||||
await page.waitForURL(url => !url.toString().includes('/connect/authorize'), { timeout: 20000 });
|
||||
await page.waitForTimeout(1200);
|
||||
}
|
||||
|
||||
for (const p of ['/security/exceptions','/evidence/proof-chains']) {
|
||||
await page.goto(`${BASE}${p}`, { waitUntil:'domcontentloaded' });
|
||||
await page.waitForTimeout(2200);
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
console.log(JSON.stringify({ failed, responses }, null, 2));
|
||||
})();
|
||||
@@ -1,65 +0,0 @@
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true, args: ['--disable-dev-shm-usage'] });
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
|
||||
const failed = [];
|
||||
const websockets = [];
|
||||
|
||||
page.on('requestfailed', request => {
|
||||
const url = request.url();
|
||||
if (/\.(css|js|map|png|jpg|jpeg|svg|woff2?)($|\?)/i.test(url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
failed.push({
|
||||
url,
|
||||
method: request.method(),
|
||||
error: request.failure()?.errorText ?? 'unknown',
|
||||
page: page.url(),
|
||||
});
|
||||
});
|
||||
|
||||
page.on('websocket', socket => {
|
||||
const record = { url: socket.url(), events: [] };
|
||||
websockets.push(record);
|
||||
socket.on('framesent', () => record.events.push('sent'));
|
||||
socket.on('framereceived', () => record.events.push('recv'));
|
||||
socket.on('close', () => record.events.push('close'));
|
||||
});
|
||||
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') {
|
||||
console.log('console-error', msg.text(), '@', page.url());
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('https://stella-ops.local/welcome', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(1200);
|
||||
|
||||
const cta = page.locator('button.cta').first();
|
||||
if (await cta.count()) {
|
||||
await cta.click({ force: true, noWaitAfter: true });
|
||||
await page.waitForTimeout(1200);
|
||||
}
|
||||
|
||||
if (page.url().includes('/connect/authorize')) {
|
||||
await page.locator('input[name="username"]').first().fill('admin');
|
||||
await page.locator('input[name="password"]').first().fill('Admin@Stella2026!');
|
||||
await page.locator('button[type="submit"], button:has-text("Sign In")').first().click();
|
||||
await page.waitForURL(url => !url.toString().includes('/connect/authorize'), { timeout: 20000 });
|
||||
await page.waitForTimeout(1200);
|
||||
}
|
||||
|
||||
for (const path of ['/security/exceptions', '/evidence/proof-chains']) {
|
||||
await page.goto(`https://stella-ops.local${path}`, { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(3000);
|
||||
}
|
||||
|
||||
const filteredFailed = failed.filter(item => !item.url.includes('/connect/authorize?'));
|
||||
console.log(JSON.stringify({ filteredFailed, websockets }, null, 2));
|
||||
|
||||
await browser.close();
|
||||
})();
|
||||