Added new tab in groups. Search client

oggui/commands
Manuel Aranda Rosales 2024-09-27 12:00:47 +02:00
commit ab303ba76e
23 changed files with 520 additions and 149 deletions

View File

@ -1,12 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { RouterTestingModule } from '@angular/router/testing'; // Asegúrate de que está importado
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
RouterTestingModule
],
declarations: [
AppComponent
@ -20,16 +20,4 @@ describe('AppComponent', () => {
expect(app).toBeTruthy();
});
it(`should have as title 'ogWebconsole'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('ogWebconsole');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, ogWebconsole');
});
});

View File

@ -17,7 +17,7 @@ import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatSidenavModule } from '@angular/material/sidenav';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AdminComponent } from './components/admin/admin.component';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
@ -157,7 +157,7 @@ import { ClientTabViewComponent } from './components/groups/client-tab-view/clie
MatIconModule,
MatButtonModule,
MatSidenavModule,
BrowserAnimationsModule,
NoopAnimationsModule,
MatCardModule,
MatCheckboxModule,
MatFormFieldModule,

View File

@ -1,23 +1,57 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AdminComponent } from './admin.component';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { By } from '@angular/platform-browser';
import { UsersComponent } from './admin.component';
describe('UsersComponent', () => {
let component: UsersComponent;
let fixture: ComponentFixture<UsersComponent>;
describe('AdminComponent', () => {
let component: AdminComponent;
let fixture: ComponentFixture<AdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [UsersComponent]
})
.compileComponents();
fixture = TestBed.createComponent(UsersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
declarations: [AdminComponent],
imports: [
RouterTestingModule, // Importa RouterTestingModule para manejar routerLink
MatButtonModule, // Importa MatButtonModule para botones
MatIconModule // Importa MatIconModule para iconos
]
}).compileComponents();
});
it('should create', () => {
beforeEach(() => {
fixture = TestBed.createComponent(AdminComponent);
component = fixture.componentInstance;
fixture.detectChanges(); // Detecta cambios para renderizar el componente
});
it('debería crear el componente', () => {
expect(component).toBeTruthy();
});
it('debería contener dos botones', () => {
const buttons = fixture.debugElement.queryAll(By.css('button'));
expect(buttons.length).toBe(2); // Verifica que hay dos botones
});
it('el primer botón debería tener el texto "Usuarios"', () => {
const firstButton = fixture.debugElement.query(By.css('.fab-button:first-child'));
expect(firstButton.nativeElement.textContent).toContain('Usuarios'); // Verifica que el texto sea "Usuarios"
});
it('el segundo botón debería tener el texto "Roles"', () => {
const secondButton = fixture.debugElement.query(By.css('.fab-button:last-child'));
expect(secondButton.nativeElement.textContent).toContain('Roles'); // Verifica que el texto sea "Roles"
});
it('el primer botón debería tener el routerLink correcto', () => {
const firstButton = fixture.debugElement.query(By.css('.fab-button:first-child'));
expect(firstButton.nativeElement.getAttribute('ng-reflect-router-link')).toBe('/users'); // Verifica el routerLink
});
it('el segundo botón debería tener el routerLink correcto', () => {
const secondButton = fixture.debugElement.query(By.css('.fab-button:last-child'));
expect(secondButton.nativeElement.getAttribute('ng-reflect-router-link')).toBe('/user-groups'); // Verifica el routerLink
});
});

View File

@ -1,13 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrl: './admin.component.css'
})
export class AdminComponent {
}
export class UsersComponent {
}

View File

@ -1,17 +1,34 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ReactiveFormsModule } from '@angular/forms';
import { EditUserModalComponent } from './edit-user-modal.component';
import { UserService } from '../users.service';
import { ToastrService } from 'ngx-toastr';
import { of } from 'rxjs';
describe('EditUserModalComponent', () => {
let component: EditUserModalComponent;
let fixture: ComponentFixture<EditUserModalComponent>;
let dialogRefSpy: jasmine.SpyObj<MatDialogRef<EditUserModalComponent>>;
let userServiceSpy: jasmine.SpyObj<UserService>;
let toastServiceSpy: jasmine.SpyObj<ToastrService>;
beforeEach(async () => {
dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['close']);
userServiceSpy = jasmine.createSpyObj('UserService', ['getUserGroups', 'getOrganizationalUnits']);
toastServiceSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']);
await TestBed.configureTestingModule({
declarations: [EditUserModalComponent]
})
.compileComponents();
declarations: [EditUserModalComponent],
imports: [ReactiveFormsModule],
providers: [
{ provide: MatDialogRef, useValue: dialogRefSpy },
{ provide: MAT_DIALOG_DATA, useValue: { user: { username: 'testUser', userGroups: [], allowedOrganizationalUnits: [] } } },
{ provide: UserService, useValue: userServiceSpy },
{ provide: ToastrService, useValue: toastServiceSpy }
]
}).compileComponents();
fixture = TestBed.createComponent(EditUserModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();

View File

@ -1,6 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UsersComponent } from './users.component';
import { MatTableModule } from '@angular/material/table';
import { MatDialogModule } from '@angular/material/dialog';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ToastrService } from 'ngx-toastr';
import { of } from 'rxjs';
import { UserService } from './users.service';
import { NO_ERRORS_SCHEMA } from '@angular/core'; // Add this import for schema
// Create a mock for UserService
class MockUserService {
getUsers() {
return of({
'hydra:member': [
{ id: 1, username: 'user1', allowedOrganizationalUnits: [], roles: ['admin'] },
{ id: 2, username: 'user2', allowedOrganizationalUnits: [], roles: ['user'] }
]
});
}
}
describe('UsersComponent', () => {
let component: UsersComponent;
@ -8,7 +26,17 @@ describe('UsersComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [UsersComponent]
declarations: [UsersComponent],
imports: [
MatTableModule,
MatDialogModule,
HttpClientTestingModule,
],
providers: [
{ provide: UserService, useClass: MockUserService },
{ provide: ToastrService, useValue: { success: () => {} } }, // Mock ToastrService
],
schemas: [NO_ERRORS_SCHEMA], // Use this to ignore unrecognized components in template
})
.compileComponents();
@ -20,4 +48,9 @@ describe('UsersComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
it('should load users on init', () => {
component.ngOnInit();
expect(component.dataSource.data.length).toBe(2); // Expect 2 mock users
});
});

View File

@ -13,6 +13,7 @@ import { ToastrService } from 'ngx-toastr';
templateUrl: './users.component.html',
styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
baseUrl: string = import.meta.env.NG_APP_BASE_API_URL;
dataSource = new MatTableDataSource<any>();

View File

@ -7,11 +7,6 @@
<input matInput [(ngModel)]="groupName" name="groupName" required />
</mat-form-field>
<mat-form-field>
<mat-label>Posición</mat-label>
<input matInput type="number" [(ngModel)]="position" name="position" required min="1" />
</mat-form-field>
<mat-slide-toggle [(ngModel)]="enabled" name="enabled">Habilitado</mat-slide-toggle>
<div class="command-selection">

View File

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { MatDialogRef } from '@angular/material/dialog';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
@Component({
@ -13,21 +13,28 @@ export class CreateCommandGroupComponent implements OnInit {
availableCommands: any[] = [];
selectedCommands: any[] = [];
groupName: string = '';
position: number = 1;
enabled: boolean = true;
editing: boolean = false;
private apiUrl = 'http://127.0.0.1:8001/commands';
constructor(
private http: HttpClient,
private dialogRef: MatDialogRef<CreateCommandGroupComponent>,
private toastService: ToastrService
private toastService: ToastrService,
@Inject(MAT_DIALOG_DATA) public data: any
) {}
ngOnInit(): void {
this.loadAvailableCommands();
if (this.data && this.data.group) {
this.editing = true;
this.loadGroupData(this.data.group);
}
}
loadAvailableCommands(): void {
this.http.get<any>(`${this.baseUrl}/commands`).subscribe(
this.http.get<any>(this.apiUrl).subscribe(
(data) => {
this.availableCommands = data['hydra:member'];
},
@ -37,6 +44,12 @@ export class CreateCommandGroupComponent implements OnInit {
);
}
loadGroupData(group: any): void {
this.groupName = group.name;
this.enabled = group.enabled;
this.selectedCommands = group.commands;
}
addCommand(command: any): void {
this.selectedCommands.push(command);
}
@ -52,20 +65,31 @@ export class CreateCommandGroupComponent implements OnInit {
const payload = {
name: this.groupName,
commands: this.selectedCommands.map(cmd => cmd['@id']),
position: this.position,
enabled: this.enabled
};
console.log('Payload', payload);
this.http.post(`${this.baseUrl}/command-groups`, payload).subscribe({
next: () => {
this.toastService.success('Grupo de comandos creado con éxito');
this.dialogRef.close();
},
error: (error) => {
console.error('Error creando el grupo de comandos', error);
}
});
if (this.editing) {
const groupId = this.data.group.uuid;
this.http.patch(`http://127.0.0.1:8001/command-groups/${groupId}`, payload).subscribe({
next: () => {
this.toastService.success('Grupo de comandos actualizado con éxito');
this.dialogRef.close();
},
error: (error) => {
console.error('Error actualizando el grupo de comandos', error);
}
});
} else {
this.http.post('http://127.0.0.1:8001/command-groups', payload).subscribe({
next: () => {
this.toastService.success('Grupo de comandos creado con éxito');
this.dialogRef.close();
},
error: (error) => {
console.error('Error creando el grupo de comandos', error);
}
});
}
}
close(): void {

View File

@ -8,20 +8,14 @@
<table mat-table [dataSource]="tasks" class="mat-elevation-z8">
<!-- Nombre de la tarea -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Nombre </th>
<td mat-cell *matCellDef="let task"> {{ task.name }} </td>
</ng-container>
<!-- Grupo de comandos -->
<ng-container matColumnDef="commandGroup">
<th mat-header-cell *matHeaderCellDef> Grupo de Comandos </th>
<td mat-cell *matCellDef="let task"> {{ task.commandGroup.name }} </td>
<th mat-header-cell *matHeaderCellDef> Creado por </th>
<td mat-cell *matCellDef="let task"> {{ task.createdBy }} </td>
</ng-container>
<!-- Fecha de ejecución -->
<ng-container matColumnDef="scheduledDate">
<th mat-header-cell *matHeaderCellDef> Fecha de Ejecución </th>
<td mat-cell *matCellDef="let task"> {{ task.scheduledDate | date:'short' }} </td>
<td mat-cell *matCellDef="let task"> {{ task.dateTime | date:'short' }} </td>
</ng-container>
<!-- Estado -->

View File

@ -19,8 +19,8 @@ export class CommandsTaskComponent implements OnInit {
itemsPerPage: number = 10;
page: number = 1;
pageSizeOptions: number[] = [5, 10, 20, 40, 100];
displayedColumns: string[] = ['name', 'commandGroup', 'scheduledDate', 'enabled', 'actions'];
private apiUrl = `${this.baseUrl}/tasks`;
displayedColumns: string[] = ['name', 'scheduledDate', 'enabled', 'actions']; // Actualizado con las nuevas columnas
private apiUrl = `${this.baseUrl}/command-tasks`;
constructor(private http: HttpClient, private dialog: MatDialog, private toastService: ToastrService) {}
@ -33,6 +33,7 @@ export class CommandsTaskComponent implements OnInit {
(data) => {
this.tasks = data['hydra:member'];
this.length = data['hydra:totalItems'];
console.log('Tasks:', this.tasks);
},
(error) => {
console.error('Error fetching tasks', error);
@ -63,7 +64,7 @@ export class CommandsTaskComponent implements OnInit {
deleteTask(task: any): void {
this.dialog.open(DeleteModalComponent, {
width: '300px',
data: { name: task.name },
data: { name: task.createdBy }, // Usamos 'createdBy' en lugar de 'name' ya que cambiaste la columna
}).afterClosed().subscribe((result) => {
if (result) {
this.http.delete(`${this.apiUrl}/${task.uuid}`).subscribe({
@ -80,7 +81,7 @@ export class CommandsTaskComponent implements OnInit {
}
onPageChange(event: any): void {
this.page = event.pageIndex;
this.page = event.pageIndex + 1; // pageIndex es cero basado, así que sumamos 1
this.itemsPerPage = event.pageSize;
this.loadTasks();
}

View File

@ -1,6 +1,7 @@
<h2 mat-dialog-title>Crear Tarea</h2>
<mat-dialog-content [formGroup]="taskForm">
<!-- Grupo de Comandos -->
<mat-form-field appearance="fill" class="full-width">
<mat-label>Selecciona Comandos</mat-label>
<mat-select formControlName="commandGroup" (selectionChange)="onCommandGroupChange()">
@ -20,6 +21,7 @@
<mat-error *ngIf="taskForm.get('commandGroup')?.invalid">Este campo es obligatorio</mat-error>
</mat-form-field>
<!-- Mostrar Comandos del Grupo Seleccionado -->
<div *ngIf="selectedGroupCommands.length > 0" class="commands-list">
<h3>Comandos del Grupo Seleccionado</h3>
<div *ngFor="let command of selectedGroupCommands" class="command-item">
@ -27,6 +29,17 @@
</div>
</div>
<!-- Comandos Extras -->
<mat-form-field appearance="fill" class="full-width">
<mat-label>Selecciona Comandos Individuales (Opcional)</mat-label>
<mat-select formControlName="extraCommands" multiple>
<mat-option *ngFor="let command of availableIndividualCommands" [value]="command.uuid">
{{ command.name }}
</mat-option>
</mat-select>
</mat-form-field>
<!-- Fecha de Ejecución -->
<mat-form-field appearance="fill" class="full-width">
<mat-label>Fecha de Ejecución</mat-label>
<input matInput [matDatepicker]="picker" formControlName="date" placeholder="Selecciona una fecha">
@ -35,12 +48,14 @@
<mat-error *ngIf="taskForm.get('date')?.invalid">Este campo es obligatorio</mat-error>
</mat-form-field>
<!-- Hora de Ejecución -->
<mat-form-field appearance="fill" class="full-width">
<mat-label>Hora de Ejecución</mat-label>
<input matInput type="time" formControlName="time" placeholder="Selecciona una hora">
<mat-error *ngIf="taskForm.get('time')?.invalid">Este campo es obligatorio</mat-error>
</mat-form-field>
<!-- Notas -->
<mat-form-field appearance="fill" class="full-width">
<mat-label>Notas</mat-label>
<textarea matInput formControlName="notes" placeholder="Opcional"></textarea>

View File

@ -1,23 +1,188 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateTaskComponent } from './create-task.component';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
import { of, throwError } from 'rxjs';
describe('CreateTaskComponent', () => {
let component: CreateTaskComponent;
let fixture: ComponentFixture<CreateTaskComponent>;
let httpMock: HttpTestingController;
let toastrService: jasmine.SpyObj<ToastrService>;
beforeEach(async () => {
const toastrSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']);
await TestBed.configureTestingModule({
declarations: [CreateTaskComponent]
imports: [ReactiveFormsModule, HttpClientTestingModule],
declarations: [CreateTaskComponent],
providers: [
{ provide: MatDialogRef, useValue: { close: jasmine.createSpy('close') } },
{ provide: MAT_DIALOG_DATA, useValue: {} },
{ provide: ToastrService, useValue: toastrSpy },
]
})
.compileComponents();
fixture = TestBed.createComponent(CreateTaskComponent);
component = fixture.componentInstance;
httpMock = TestBed.inject(HttpTestingController);
toastrService = TestBed.inject(ToastrService) as jasmine.SpyObj<ToastrService>;
fixture.detectChanges();
});
afterEach(() => {
httpMock.verify();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should initialize the form with default values', () => {
expect(component.taskForm).toBeTruthy();
expect(component.taskForm.get('commandGroup')?.value).toBe('');
expect(component.taskForm.get('extraCommands')?.value).toEqual([]);
expect(component.taskForm.get('date')?.value).toBe('');
expect(component.taskForm.get('time')?.value).toBe('');
expect(component.taskForm.get('notes')?.value).toBe('');
});
it('should load command groups and set availableCommandGroups', () => {
const mockCommandGroups = { 'hydra:member': [{ uuid: '1', name: 'Group 1' }, { uuid: '2', name: 'Group 2' }] };
component.loadCommandGroups();
const req = httpMock.expectOne(`${component.baseUrl}/command-groups`);
expect(req.request.method).toBe('GET');
req.flush(mockCommandGroups);
expect(component.availableCommandGroups.length).toBe(2);
expect(component.availableCommandGroups).toEqual(mockCommandGroups['hydra:member']);
});
it('should handle error when loading command groups', () => {
component.loadCommandGroups();
const req = httpMock.expectOne(`${component.baseUrl}/command-groups`);
req.flush('Error loading command groups', { status: 500, statusText: 'Server Error' });
expect(toastrService.error).toHaveBeenCalledWith('Error al cargar los grupos de comandos');
});
it('should load individual commands and set availableIndividualCommands', () => {
const mockCommands = { 'hydra:member': [{ uuid: '1', name: 'Command 1' }, { uuid: '2', name: 'Command 2' }] };
component.loadIndividualCommands();
const req = httpMock.expectOne(`${component.baseUrl}/commands`);
expect(req.request.method).toBe('GET');
req.flush(mockCommands);
expect(component.availableIndividualCommands.length).toBe(2);
expect(component.availableIndividualCommands).toEqual(mockCommands['hydra:member']);
});
it('should handle error when loading individual commands', () => {
component.loadIndividualCommands();
const req = httpMock.expectOne(`${component.baseUrl}/commands`);
req.flush('Error loading individual commands', { status: 500, statusText: 'Server Error' });
expect(toastrService.error).toHaveBeenCalledWith('Error al cargar los comandos individuales');
});
it('should fetch selected group commands on command group change', () => {
component.taskForm.patchValue({ commandGroup: '1' });
component.onCommandGroupChange();
const mockGroupCommands = { commands: [{ uuid: '1', name: 'Group Command 1' }, { uuid: '2', name: 'Group Command 2' }] };
const req = httpMock.expectOne(`${component.baseUrl}/command-groups/1`);
expect(req.request.method).toBe('GET');
req.flush(mockGroupCommands);
expect(component.selectedGroupCommands.length).toBe(2);
expect(component.selectedGroupCommands).toEqual(mockGroupCommands.commands);
});
it('should handle error when fetching group commands', () => {
component.taskForm.patchValue({ commandGroup: '1' });
component.onCommandGroupChange();
const req = httpMock.expectOne(`${component.baseUrl}/command-groups/1`);
req.flush('Error loading group commands', { status: 500, statusText: 'Server Error' });
expect(toastrService.error).toHaveBeenCalledWith('Error al cargar los comandos del grupo seleccionado');
});
it('should save the task successfully', () => {
const mockFormData = {
commandGroup: '1',
extraCommands: ['1', '2'],
date: '2024-09-27',
time: '12:00',
notes: 'Test notes'
};
component.taskForm.setValue(mockFormData);
component.saveTask();
const expectedPayload = {
commandGroups: ['/command-groups/1'],
commands: ['/commands/1', '/commands/2'],
dateTime: jasmine.any(String),
notes: 'Test notes'
};
const req = httpMock.expectOne(component.apiUrl);
expect(req.request.method).toBe('POST');
req.flush({}); // Mock successful response
expect(toastrService.success).toHaveBeenCalledWith('Tarea creada con éxito');
expect(component.dialogRef.close).toHaveBeenCalledWith(true);
});
it('should not save the task if form is invalid', () => {
component.taskForm.setValue({
commandGroup: '',
extraCommands: [],
date: '',
time: '',
notes: ''
});
component.saveTask();
expect(toastrService.error).toHaveBeenCalledWith('Por favor, rellene todos los campos obligatorios');
expect(component.dialogRef.close).not.toHaveBeenCalled();
});
it('should handle error when saving the task', () => {
const mockFormData = {
commandGroup: '1',
extraCommands: ['1'],
date: '2024-09-27',
time: '12:00',
notes: 'Test notes'
};
component.taskForm.setValue(mockFormData);
component.saveTask();
const req = httpMock.expectOne(component.apiUrl);
expect(req.request.method).toBe('POST');
req.flush('Error creating task', { status: 500, statusText: 'Server Error' });
expect(toastrService.error).toHaveBeenCalledWith('Error al crear la tarea');
});
it('should close the dialog', () => {
component.close();
expect(component.dialogRef.close).toHaveBeenCalled();
});
});

View File

@ -14,17 +14,20 @@ export class CreateTaskComponent implements OnInit {
taskForm: FormGroup;
availableCommandGroups: any[] = [];
selectedGroupCommands: any[] = [];
availableIndividualCommands: any[] = [];
apiUrl = `${this.baseUrl}/command-tasks`;
editing: boolean = false; // Flag to check if in edit mode
constructor(
private fb: FormBuilder,
private http: HttpClient,
private toastr: ToastrService,
public dialogRef: MatDialogRef<CreateTaskComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
@Inject(MAT_DIALOG_DATA) public data: any // Inject the task data if editing
) {
this.taskForm = this.fb.group({
commandGroup: ['', Validators.required],
commandGroup: [''],
extraCommands: [[]],
date: ['', Validators.required],
time: ['', Validators.required],
notes: ['']
@ -33,6 +36,13 @@ export class CreateTaskComponent implements OnInit {
ngOnInit(): void {
this.loadCommandGroups();
this.loadIndividualCommands();
// If task data is provided, load it into the form (edit mode)
if (this.data && this.data.task) {
this.editing = true;
this.loadTaskData(this.data.task);
}
}
loadCommandGroups(): void {
@ -46,6 +56,33 @@ export class CreateTaskComponent implements OnInit {
);
}
loadIndividualCommands(): void {
this.http.get<any>(`${this.baseUrl}/commands`).subscribe(
(data) => {
this.availableIndividualCommands = data['hydra:member'];
},
(error) => {
this.toastr.error('Error al cargar los comandos individuales');
}
);
}
loadTaskData(task: any): void {
// Populate form fields with task data when editing
this.taskForm.patchValue({
commandGroup: task.commandGroup ? task.commandGroup['@id'] : '',
extraCommands: task.commands ? task.commands.map((cmd: any) => cmd['@id']) : [],
date: task.dateTime ? task.dateTime.split('T')[0] : '',
time: task.dateTime ? task.dateTime.split('T')[1].slice(0, 5) : '',
notes: task.notes || ''
});
// If a command group is selected, load its commands
if (task.commandGroup) {
this.selectedGroupCommands = task.commandGroup.commands;
}
}
onCommandGroupChange(): void {
const selectedGroupId = this.taskForm.get('commandGroup')?.value;
this.http.get<any>(`${this.baseUrl}/command-groups/${selectedGroupId}`).subscribe(
@ -67,22 +104,41 @@ export class CreateTaskComponent implements OnInit {
const formData = this.taskForm.value;
const dateTime = this.combineDateAndTime(formData.date, formData.time);
const selectedCommands = formData.extraCommands.length > 0
? formData.extraCommands.map((id: any) => `/commands/${id}`)
: [""];
const payload = {
commandGroups: '/command-groups/'+formData.commandGroup,
commandGroups: [formData.commandGroup ? `/command-groups/${formData.commandGroup}` : [""]],
commands: selectedCommands,
dateTime: dateTime,
notes: formData.notes || ''
};
console.log(payload);
stop;
this.http.post<any>(this.apiUrl, payload).subscribe({
next: () => {
this.toastr.success('Tarea creada con éxito');
this.dialogRef.close(true);
},
error: () => {
this.toastr.error('Error al crear la tarea');
}
});
if (this.editing) {
// Perform a PATCH request if editing an existing task
const taskId = this.data.task.uuid;
this.http.patch<any>(`${this.apiUrl}/${taskId}`, payload).subscribe({
next: () => {
this.toastr.success('Tarea actualizada con éxito');
this.dialogRef.close(true);
},
error: () => {
this.toastr.error('Error al actualizar la tarea');
}
});
} else {
// Perform a POST request if creating a new task
this.http.post<any>(this.apiUrl, payload).subscribe({
next: () => {
this.toastr.success('Tarea creada con éxito');
this.dialogRef.close(true);
},
error: () => {
this.toastr.error('Error al crear la tarea');
}
});
}
}
combineDateAndTime(date: string, time: string): string {
@ -90,7 +146,7 @@ export class CreateTaskComponent implements OnInit {
const [hours, minutes] = time.split(':').map(Number);
dateObj.setHours(hours, minutes, 0);
return dateObj.toISOString(); // Return in ISO format (e.g., 2021-10-01T00:00:00+00:00)
return dateObj.toISOString();
}
close(): void {

View File

@ -1,16 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Component } from '@angular/core';
import { DashboardComponent } from './dashboard.component';
@Component({
template: '<app-dashboard></app-dashboard>' // Use the component in a simple template
})
class TestHostComponent {}
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DashboardComponent]
})
.compileComponents();
declarations: [DashboardComponent, TestHostComponent] // Declare the component
}).compileComponents();
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;

View File

@ -1,19 +1,27 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialogRef } from '@angular/material/dialog';
import { ReactiveFormsModule } from '@angular/forms';
import { CreateClientComponent } from './create-client.component';
describe('CreateClientComponent', () => {
let component: CreateClientComponent;
let fixture: ComponentFixture<CreateClientComponent>;
let dialogRefSpy: jasmine.SpyObj<MatDialogRef<CreateClientComponent>>;
beforeEach(async () => {
dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['close']);
await TestBed.configureTestingModule({
declarations: [CreateClientComponent]
})
.compileComponents();
declarations: [CreateClientComponent],
imports: [ReactiveFormsModule],
providers: [
{ provide: MatDialogRef, useValue: dialogRefSpy }
]
}).compileComponents();
fixture = TestBed.createComponent(CreateClientComponent);
component = fixture.componentInstance;
component.data = { organizationalUnit: { '@id': '1' } };
fixture.detectChanges();
});

View File

@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GroupsComponent } from './groups.component';
describe('GroupsComponent', () => {
let component: GroupsComponent;
let fixture: ComponentFixture<GroupsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GroupsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(GroupsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,6 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialogRef } from '@angular/material/dialog';
import { SaveFiltersDialogComponent } from './save-filters-dialog.component';
import { ReactiveFormsModule } from '@angular/forms';
describe('SaveFiltersDialogComponent', () => {
let component: SaveFiltersDialogComponent;
@ -8,9 +9,12 @@ describe('SaveFiltersDialogComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [SaveFiltersDialogComponent]
})
.compileComponents();
declarations: [SaveFiltersDialogComponent],
imports: [ReactiveFormsModule],
providers: [
{ provide: MatDialogRef, useValue: { close: jasmine.createSpy('close') } }
]
}).compileComponents();
fixture = TestBed.createComponent(SaveFiltersDialogComponent);
component = fixture.componentInstance;
@ -20,4 +24,21 @@ describe('SaveFiltersDialogComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
it('should close dialog with null on cancel', () => {
component.onCancel();
expect(component.dialogRef.close).toHaveBeenCalledWith(null);
});
it('should close dialog with filter name on save', () => {
component.filterNameControl.setValue('Test Filter');
component.onSave();
expect(component.dialogRef.close).toHaveBeenCalledWith('Test Filter');
});
it('should not close dialog if filter name is invalid', () => {
component.filterNameControl.setValue('');
component.onSave();
expect(component.dialogRef.close).not.toHaveBeenCalled();
});
});

View File

@ -1,6 +1,8 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TreeViewComponent } from './tree-view.component';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { NestedTreeControl } from '@angular/cdk/tree';
import { MatTreeNestedDataSource } from '@angular/material/tree';
describe('TreeViewComponent', () => {
let component: TreeViewComponent;
@ -8,10 +10,13 @@ describe('TreeViewComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [TreeViewComponent]
})
.compileComponents();
declarations: [TreeViewComponent],
providers: [
{ provide: MatDialogRef, useValue: { close: jasmine.createSpy('close') } },
{ provide: MAT_DIALOG_DATA, useValue: { data: {} } }
]
}).compileComponents();
fixture = TestBed.createComponent(TreeViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
@ -20,4 +25,35 @@ describe('TreeViewComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
it('should initialize tree data correctly', () => {
const mockData = {
id: 1,
name: 'Root',
type: 'organizational-unit',
clients: [],
children: []
};
component.data = { data: mockData };
component.ngOnInit();
expect(component.dataSource.data).toEqual([mockData]);
});
it('should close the dialog', () => {
// Using type assertion to access the private dialogRef
(component as any).dialogRef.close(); // Cast to any to bypass TypeScript's private checks
expect((component as any).dialogRef.close).toHaveBeenCalled(); // Again casting to any
});
it('should determine if a node has children or clients', () => {
const nodeWithChildren: any = { children: [{ id: 2, name: 'Child', type: 'classroom' }], clients: [] };
const nodeWithClients: any = { children: [], clients: [{ id: 1, name: 'Client 1', ip: '192.168.0.1', mac: '00:00:00:00:00:01' }] };
const nodeWithoutChildrenOrClients: any = { children: [], clients: [] };
expect(component.hasChild(0, nodeWithChildren)).toBeTrue();
expect(component.hasChild(0, nodeWithClients)).toBeTrue();
expect(component.hasChild(0, nodeWithoutChildrenOrClients)).toBeFalse();
});
});

View File

@ -1,8 +1,12 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { ToastrModule } from 'ngx-toastr'; // Importa el módulo de Toastr
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; // Importa el módulo de animaciones
import { MatFormFieldModule } from '@angular/material/form-field'; // Importa MatFormFieldModule
import { MatInputModule } from '@angular/material/input'; // Importa MatInputModule
import { MatIconModule } from '@angular/material/icon'; // Importa MatIconModule
describe('LoginComponent', () => {
let component: LoginComponent;
@ -10,10 +14,17 @@ describe('LoginComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [LoginComponent],
imports: [FormsModule],
providers: [provideHttpClient(withInterceptorsFromDi())]
})
declarations: [LoginComponent],
imports: [
FormsModule,
ToastrModule.forRoot(),
BrowserAnimationsModule,
MatFormFieldModule,
MatInputModule,
MatIconModule
],
providers: [provideHttpClient(withInterceptorsFromDi())]
})
.compileComponents();
fixture = TestBed.createComponent(LoginComponent);

View File

@ -1,8 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { HttpInterceptorFn } from '@angular/common/http';
import { CustomInterceptor } from './custom.interceptor';
describe('customInterceptor', () => {
});

View File

@ -1,5 +1,5 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterModule } from '@angular/router'; // Import RouterModule
import { AuthLayoutComponent } from './auth-layout.component';
describe('AuthLayoutComponent', () => {
@ -8,10 +8,11 @@ describe('AuthLayoutComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AuthLayoutComponent]
declarations: [AuthLayoutComponent],
imports: [RouterModule.forRoot([])] // Include RouterModule with an empty route
})
.compileComponents();
fixture = TestBed.createComponent(AuthLayoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();

View File

@ -8,6 +8,7 @@ import {MatDialog} from "@angular/material/dialog";
templateUrl: './header.component.html',
styleUrls: ['./header.component.css'],
})
export class HeaderComponent implements OnInit {
isSuperAdmin: boolean = false;
@ -44,9 +45,6 @@ export class HeaderComponent implements OnInit {
data: { user: this.decodedToken.username, uuid: this.decodedToken.uuid },
width: '400px',
});
/* dialogRef.componentInstance.userEdited.subscribe(() => {
console.log("User edited successfully!")
}); */
}
}