docs: remove Phase 2 completion and executive summary files
This commit is contained in:
@@ -0,0 +1,794 @@
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
interface TestResult {
|
||||
endpoint: string;
|
||||
method: string;
|
||||
status: 'pending' | 'running' | 'success' | 'error';
|
||||
duration?: number;
|
||||
response?: any;
|
||||
error?: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-tests-panel',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
template: `
|
||||
<div class="tests-panel">
|
||||
<div class="panel-header">
|
||||
<h2>🧪 API Tests Panel</h2>
|
||||
<p class="panel-description">
|
||||
Test all ObsiViewer APIs and validate functionality. Each test shows execution time and response details.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="panel-content">
|
||||
<!-- Test Sections -->
|
||||
<div class="test-sections">
|
||||
<!-- Health Check -->
|
||||
<div class="test-section">
|
||||
<h3>🏥 Health Check</h3>
|
||||
<button type="button" class="test-btn test-btn--primary" (click)="runHealthCheck()" [disabled]="isRunning()">
|
||||
Run Health Check
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Vault APIs -->
|
||||
<div class="test-section">
|
||||
<h3>📁 Vault APIs</h3>
|
||||
<div class="test-group">
|
||||
<button type="button" class="test-btn test-btn--secondary" (click)="runVaultMetadata()" [disabled]="isRunning()">
|
||||
GET /api/vault/metadata
|
||||
</button>
|
||||
<button type="button" class="test-btn test-btn--secondary" (click)="runVaultPaginated()" [disabled]="isRunning()">
|
||||
GET /api/vault/metadata/paginated
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Files APIs -->
|
||||
<div class="test-section">
|
||||
<h3>📄 Files APIs</h3>
|
||||
<div class="file-test-form">
|
||||
<div class="form-row">
|
||||
<label for="filePath">File Path:</label>
|
||||
<input
|
||||
id="filePath"
|
||||
type="text"
|
||||
[(ngModel)]="testFilePath"
|
||||
placeholder="e.g., vault/home.md"
|
||||
class="form-input"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="fileContent">Content:</label>
|
||||
<textarea
|
||||
id="fileContent"
|
||||
[(ngModel)]="testFileContent"
|
||||
placeholder="Test content for file operations..."
|
||||
rows="3"
|
||||
class="form-textarea"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="test-group">
|
||||
<button type="button" class="test-btn test-btn--info" (click)="runFileRead()" [disabled]="isRunning() || !testFilePath().trim()">
|
||||
GET /api/files (Read)
|
||||
</button>
|
||||
<button type="button" class="test-btn test-btn--warning" (click)="runFileWrite()" [disabled]="isRunning() || !testFilePath().trim() || !testFileContent().trim()">
|
||||
PUT /api/files (Write)
|
||||
</button>
|
||||
<button type="button" class="test-btn test-btn--danger" (click)="runFileDelete()" [disabled]="isRunning() || !testFilePath().trim()">
|
||||
DELETE /api/files (Delete)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- State Management -->
|
||||
<div class="test-section">
|
||||
<h3>🏷️ State Management</h3>
|
||||
<div class="form-row">
|
||||
<label for="stateNoteId">Note ID:</label>
|
||||
<input
|
||||
id="stateNoteId"
|
||||
type="text"
|
||||
[(ngModel)]="testNoteId"
|
||||
placeholder="e.g., home"
|
||||
class="form-input"
|
||||
/>
|
||||
</div>
|
||||
<div class="test-group">
|
||||
<button type="button" class="test-btn test-btn--accent" (click)="runTogglePublish()" [disabled]="isRunning() || !testNoteId().trim()">
|
||||
Toggle Publish
|
||||
</button>
|
||||
<button type="button" class="test-btn test-btn--accent" (click)="runToggleFavorite()" [disabled]="isRunning() || !testNoteId().trim()">
|
||||
Toggle Favorite
|
||||
</button>
|
||||
<button type="button" class="test-btn test-btn--accent" (click)="runToggleDraft()" [disabled]="isRunning() || !testNoteId().trim()">
|
||||
Toggle Draft
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SSE Events -->
|
||||
<div class="test-section">
|
||||
<h3>📡 SSE Events</h3>
|
||||
<div class="test-group">
|
||||
<button type="button" class="test-btn test-btn--success" (click)="runSSETest()" [disabled]="isRunning()">
|
||||
Test SSE Connection
|
||||
</button>
|
||||
<button type="button" class="test-btn test-btn--info" (click)="runSimulateEvent()" [disabled]="isRunning()">
|
||||
Simulate Event
|
||||
</button>
|
||||
</div>
|
||||
<div class="sse-events" *ngIf="sseEvents().length > 0">
|
||||
<h4>Recent Events:</h4>
|
||||
<div class="events-list">
|
||||
<div *ngFor="let event of sseEvents()" class="event-item">
|
||||
<span class="event-type">{{ event.type }}</span>
|
||||
<span class="event-data">{{ event.data | json }}</span>
|
||||
<span class="event-time">{{ event.timestamp | date:'HH:mm:ss' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logging -->
|
||||
<div class="test-section">
|
||||
<h3>📝 Logging</h3>
|
||||
<button type="button" class="test-btn test-btn--neutral" (click)="runLogTest()" [disabled]="isRunning()">
|
||||
Send Test Logs
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Batch Operations -->
|
||||
<div class="test-section">
|
||||
<h3>🔄 Batch Operations</h3>
|
||||
<button type="button" class="test-btn test-btn--primary" (click)="runBatchTest()" [disabled]="isRunning()">
|
||||
Run Full Test Suite
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Results Panel -->
|
||||
<div class="results-panel">
|
||||
<div class="results-header">
|
||||
<h3>Test Results</h3>
|
||||
<button type="button" class="clear-btn" (click)="clearResults()" [disabled]="results().length === 0">
|
||||
Clear Results
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="results-list" *ngIf="results().length > 0; else noResults">
|
||||
<div *ngFor="let result of results()" class="result-item" [ngClass]="getResultClass(result)">
|
||||
<div class="result-header">
|
||||
<span class="result-method">{{ result.method }}</span>
|
||||
<span class="result-endpoint">{{ result.endpoint }}</span>
|
||||
<span class="result-status">{{ result.status.toUpperCase() }}</span>
|
||||
<span class="result-duration" *ngIf="result.duration">({{ result.duration }}ms)</span>
|
||||
</div>
|
||||
<div class="result-details" *ngIf="result.response || result.error">
|
||||
<pre class="result-json">{{ result.response ? (result.response | json) : result.error }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #noResults>
|
||||
<div class="no-results">
|
||||
<p>No tests run yet. Click a button above to start testing!</p>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
styles: [`
|
||||
.tests-panel {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
font-family: 'Monaco', 'Menlo', monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.panel-header h2 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-description {
|
||||
margin: 0;
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
display: grid;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.test-sections {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.test-section h3 {
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.test-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.test-btn {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.test-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.test-btn--primary {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.test-btn--primary:hover:not(:disabled) {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
.test-btn--secondary {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.test-btn--secondary:hover:not(:disabled) {
|
||||
background: #545b62;
|
||||
}
|
||||
|
||||
.test-btn--info {
|
||||
background: #17a2b8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.test-btn--info:hover:not(:disabled) {
|
||||
background: #138496;
|
||||
}
|
||||
|
||||
.test-btn--warning {
|
||||
background: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.test-btn--warning:hover:not(:disabled) {
|
||||
background: #e0a800;
|
||||
}
|
||||
|
||||
.test-btn--danger {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.test-btn--danger:hover:not(:disabled) {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
.test-btn--accent {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.test-btn--accent:hover:not(:disabled) {
|
||||
background: #218838;
|
||||
}
|
||||
|
||||
.test-btn--neutral {
|
||||
background: #6f42c1;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.test-btn--neutral:hover:not(:disabled) {
|
||||
background: #5a32a3;
|
||||
}
|
||||
|
||||
.file-test-form {
|
||||
display: grid;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.form-row label {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-textarea {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
||||
}
|
||||
|
||||
.results-panel {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.results-header h3 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
padding: 6px 12px;
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.clear-btn:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
.clear-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.result-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.result-item--success {
|
||||
background: #d4edda;
|
||||
border-left: 4px solid #28a745;
|
||||
}
|
||||
|
||||
.result-item--error {
|
||||
background: #f8d7da;
|
||||
border-left: 4px solid #dc3545;
|
||||
}
|
||||
|
||||
.result-item--running {
|
||||
background: #fff3cd;
|
||||
border-left: 4px solid #ffc107;
|
||||
}
|
||||
|
||||
.result-item--pending {
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid #6c757d;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.result-method {
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
background: #e7f3ff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.result-endpoint {
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.result-status {
|
||||
font-weight: bold;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.result-item--success .result-status {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.result-item--error .result-status {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.result-item--running .result-status {
|
||||
background: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.result-item--pending .result-status {
|
||||
background: #f8f9fa;
|
||||
color: #383d41;
|
||||
}
|
||||
|
||||
.result-duration {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.result-details {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.result-json {
|
||||
background: #f8f9fa;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.no-results p {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.sse-events {
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.sse-events h4 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.events-list {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.event-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 5px 0;
|
||||
font-size: 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.event-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.event-type {
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.event-data {
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.event-time {
|
||||
color: #666;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class TestsPanelComponent {
|
||||
private http = inject(HttpClient);
|
||||
|
||||
// Test form data
|
||||
testFilePath = signal('');
|
||||
testFileContent = signal('');
|
||||
testNoteId = signal('');
|
||||
|
||||
// Test state
|
||||
results = signal<TestResult[]>([]);
|
||||
sseEvents = signal<Array<{type: string, data: any, timestamp: Date}>>([]);
|
||||
isRunning = signal(false);
|
||||
|
||||
// SSE connection
|
||||
private sseConnection: EventSource | null = null;
|
||||
|
||||
async runHealthCheck(): Promise<void> {
|
||||
await this.runTest('GET', '/api/health', async () => {
|
||||
return this.http.get('/api/health').toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runVaultMetadata(): Promise<void> {
|
||||
await this.runTest('GET', '/api/vault/metadata', async () => {
|
||||
return this.http.get('/api/vault/metadata').toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runVaultPaginated(): Promise<void> {
|
||||
await this.runTest('GET', '/api/vault/metadata/paginated', async () => {
|
||||
return this.http.get('/api/vault/metadata/paginated').toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runFileRead(): Promise<void> {
|
||||
const path = this.testFilePath().trim();
|
||||
if (!path) return;
|
||||
|
||||
await this.runTest('GET', `/api/files?path=${encodeURIComponent(path)}`, async () => {
|
||||
return this.http.get(`/api/files?path=${encodeURIComponent(path)}`, { responseType: 'text' }).toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runFileWrite(): Promise<void> {
|
||||
const path = this.testFilePath().trim();
|
||||
const content = this.testFileContent().trim();
|
||||
if (!path || !content) return;
|
||||
|
||||
await this.runTest('PUT', `/api/files?path=${encodeURIComponent(path)}`, async () => {
|
||||
return this.http.put(`/api/files?path=${encodeURIComponent(path)}`, content, {
|
||||
headers: { 'Content-Type': 'text/markdown; charset=utf-8' }
|
||||
}).toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runFileDelete(): Promise<void> {
|
||||
const path = this.testFilePath().trim();
|
||||
if (!path) return;
|
||||
|
||||
await this.runTest('DELETE', `/api/files?path=${encodeURIComponent(path)}`, async () => {
|
||||
return this.http.delete(`/api/files?path=${encodeURIComponent(path)}`).toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runTogglePublish(): Promise<void> {
|
||||
const noteId = this.testNoteId().trim();
|
||||
if (!noteId) return;
|
||||
|
||||
await this.runTest('PUT', `/api/notes/${encodeURIComponent(noteId)}/states/publish`, async () => {
|
||||
return this.http.put(`/api/notes/${encodeURIComponent(noteId)}/states/publish`, { value: true }).toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runToggleFavorite(): Promise<void> {
|
||||
const noteId = this.testNoteId().trim();
|
||||
if (!noteId) return;
|
||||
|
||||
await this.runTest('PUT', `/api/notes/${encodeURIComponent(noteId)}/states/favoris`, async () => {
|
||||
return this.http.put(`/api/notes/${encodeURIComponent(noteId)}/states/favoris`, { value: true }).toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runToggleDraft(): Promise<void> {
|
||||
const noteId = this.testNoteId().trim();
|
||||
if (!noteId) return;
|
||||
|
||||
await this.runTest('PUT', `/api/notes/${encodeURIComponent(noteId)}/states/draft`, async () => {
|
||||
return this.http.put(`/api/notes/${encodeURIComponent(noteId)}/states/draft`, { value: true }).toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
runSSETest(): void {
|
||||
if (this.sseConnection) {
|
||||
this.sseConnection.close();
|
||||
this.sseConnection = null;
|
||||
}
|
||||
|
||||
this.sseConnection = new EventSource('/api/vault/events');
|
||||
this.sseEvents.set([]);
|
||||
|
||||
this.sseConnection.onopen = () => {
|
||||
this.addResult({
|
||||
endpoint: '/api/vault/events',
|
||||
method: 'SSE',
|
||||
status: 'success',
|
||||
response: 'SSE connection established',
|
||||
timestamp: Date.now()
|
||||
});
|
||||
};
|
||||
|
||||
this.sseConnection.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
this.sseEvents.update(events => [{
|
||||
type: data.event || 'message',
|
||||
data,
|
||||
timestamp: new Date()
|
||||
}, ...events.slice(0, 9)]); // Keep last 10 events
|
||||
} catch (e) {
|
||||
// Ignore parse errors
|
||||
}
|
||||
};
|
||||
|
||||
this.sseConnection.onerror = (error) => {
|
||||
this.addResult({
|
||||
endpoint: '/api/vault/events',
|
||||
method: 'SSE',
|
||||
status: 'error',
|
||||
error: 'SSE connection failed',
|
||||
timestamp: Date.now()
|
||||
});
|
||||
this.sseConnection?.close();
|
||||
this.sseConnection = null;
|
||||
};
|
||||
}
|
||||
|
||||
async runSimulateEvent(): Promise<void> {
|
||||
await this.runTest('POST', '/api/simulate-event', async () => {
|
||||
return this.http.post('/api/simulate-event', { event: 'test', data: { message: 'Test event' } }).toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runLogTest(): Promise<void> {
|
||||
const testLogs = [
|
||||
{ event: 'test_api_call', level: 'info', context: { route: '/api/test' }, data: { result: 'success' } },
|
||||
{ event: 'test_error', level: 'error', context: { route: '/api/test' }, data: { error: 'Test error' } }
|
||||
];
|
||||
|
||||
await this.runTest('POST', '/api/log', async () => {
|
||||
return this.http.post('/api/log', testLogs[0]).toPromise();
|
||||
});
|
||||
|
||||
await this.runTest('POST', '/api/logs', async () => {
|
||||
return this.http.post('/api/logs', testLogs[1]).toPromise();
|
||||
});
|
||||
}
|
||||
|
||||
async runBatchTest(): Promise<void> {
|
||||
this.isRunning.set(true);
|
||||
|
||||
try {
|
||||
// Health check
|
||||
await this.runHealthCheck();
|
||||
|
||||
// Vault APIs
|
||||
await this.runVaultMetadata();
|
||||
await this.runVaultPaginated();
|
||||
|
||||
// File operations (using a test file)
|
||||
this.testFilePath.set('test-file.md');
|
||||
this.testFileContent.set('# Test File\n\nThis is a test file for API testing.');
|
||||
await this.runFileWrite();
|
||||
await this.runFileRead();
|
||||
|
||||
// State operations (using home note)
|
||||
this.testNoteId.set('home');
|
||||
await this.runTogglePublish();
|
||||
await this.runToggleFavorite();
|
||||
await this.runToggleDraft();
|
||||
|
||||
// SSE
|
||||
this.runSSETest();
|
||||
|
||||
// Logging
|
||||
await this.runLogTest();
|
||||
|
||||
// Clean up
|
||||
await this.runFileDelete();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Batch test error:', error);
|
||||
} finally {
|
||||
this.isRunning.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async runTest(method: string, endpoint: string, operation: () => Promise<any>): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
const result: TestResult = {
|
||||
endpoint,
|
||||
method,
|
||||
status: 'running',
|
||||
timestamp: startTime
|
||||
};
|
||||
|
||||
this.results.update(results => [result, ...results]);
|
||||
|
||||
try {
|
||||
const response = await operation();
|
||||
result.status = 'success';
|
||||
result.response = response;
|
||||
} catch (error: any) {
|
||||
result.status = 'error';
|
||||
result.error = error.message || error.toString();
|
||||
}
|
||||
|
||||
result.duration = Date.now() - startTime;
|
||||
this.results.update(results => results.map(r => r === result ? { ...result } : r));
|
||||
}
|
||||
|
||||
private addResult(result: TestResult): void {
|
||||
this.results.update(results => [result, ...results]);
|
||||
}
|
||||
|
||||
clearResults(): void {
|
||||
this.results.set([]);
|
||||
this.sseEvents.set([]);
|
||||
if (this.sseConnection) {
|
||||
this.sseConnection.close();
|
||||
this.sseConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
getResultClass(result: TestResult): string {
|
||||
return `result-item--${result.status}`;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { MarkdownPlaygroundComponent } from './markdown-playground/markdown-playground.component';
|
||||
import { TestsPanelComponent } from './tests-panel.component';
|
||||
|
||||
export const TESTS_ROUTES: Routes = [
|
||||
{
|
||||
path: 'markdown',
|
||||
component: MarkdownPlaygroundComponent
|
||||
},
|
||||
{
|
||||
path: 'panel',
|
||||
component: TestsPanelComponent
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
redirectTo: 'markdown'
|
||||
redirectTo: 'panel'
|
||||
}
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user