diff --git a/alfa-client/apps/admin-e2e/src/components/benutzer/benutzer.e2e.component.ts b/alfa-client/apps/admin-e2e/src/components/benutzer/benutzer.e2e.component.ts index 868477005fff869e18df7d805009425bf67609aa..5a67a4226ff7d7d4eda4f7d57a69fdca18bd4d72 100644 --- a/alfa-client/apps/admin-e2e/src/components/benutzer/benutzer.e2e.component.ts +++ b/alfa-client/apps/admin-e2e/src/components/benutzer/benutzer.e2e.component.ts @@ -2,13 +2,13 @@ import 'cypress-real-events'; import { exist } from '../../support/cypress.util'; export class BenutzerE2EComponent { - private readonly benutzerHinzufuegenButton: string = 'add-user-button'; - private readonly userEntry: string = 'user-entry-'; + private readonly benutzerHinzufuegenButton: string = 'Add-user-button'; + private readonly userEntry: string = 'User-entry-'; private readonly userVorname: string = 'Vorname-text-input'; private readonly userNachname: string = 'Nachname-text-input'; private readonly userBenutzername: string = 'Benutzername-text-input'; private readonly userMail: string = 'E-Mail-text-input'; - private readonly addOEButton: string = 'add-organisationseinheit-button'; + private readonly addOEButton: string = 'Add-organisationseinheit-button'; private readonly adminCheckbox: string = 'Admin-checkbox-editor'; private readonly loeschenCheckbox: string = 'Loschen-checkbox-editor'; private readonly userCheckbox: string = 'User-checkbox-editor'; diff --git a/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component.ts b/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component.ts index d91e4ba2a9f45642888d4851d1ddbc6e30479084..cb9942a8cc0dba8319587c86a46bd4b947f2fed6 100644 --- a/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component.ts +++ b/alfa-client/apps/admin-e2e/src/components/organisationseinheiten/organisationseinheiten.e2e.component.ts @@ -1,4 +1,4 @@ -import { haveValue, typeText } from '../../support/cypress.util'; +import { exist, haveValue, typeText } from '../../support/cypress.util'; export class OrganisationseinheitenE2EComponent { private readonly einheitenList: string = 'organisations-einheit-list'; @@ -22,7 +22,7 @@ export class OrganisationseinheitenE2EComponent { }); } - public getSignaturText(): any { + public getSignaturText(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.signaturText); } @@ -48,8 +48,8 @@ export class OrganisationseinheitenE2EComponent { public scrollbarIsPresent(): void { this.getSignaturText().then((textarea) => { - const scrollHeight = textarea[0].scrollHeight; - const clientHeight = textarea[0].clientHeight; + const scrollHeight: number = textarea[0].scrollHeight; + const clientHeight: number = textarea[0].clientHeight; expect(scrollHeight).to.be.greaterThan(clientHeight); }); diff --git a/alfa-client/apps/admin-e2e/src/support/cypress.util.ts b/alfa-client/apps/admin-e2e/src/support/cypress.util.ts index 00bd9b0ba92548bb035316ed5f4b90452d68c989..4b45f922abb7e64be5329d03112af9769196c9fa 100644 --- a/alfa-client/apps/admin-e2e/src/support/cypress.util.ts +++ b/alfa-client/apps/admin-e2e/src/support/cypress.util.ts @@ -2,100 +2,100 @@ import { wait } from './cypress-helper'; //TODO Naming der Methoden geradeziehen -export function containClass(element: any, cssClass: string): void { +export function containClass(element: Cypress.Chainable<Element>, cssClass: string): void { element.should('have.class', cssClass); } -export function notContainClass(element: any, cssClass: string): void { +export function notContainClass(element: Cypress.Chainable<Element>, cssClass: string): void { element.should('not.have.class', cssClass); } -export function exist(element: any): void { +export function exist(element: Cypress.Chainable<Element>): void { element.should('exist'); } -export function notExist(element: any): void { +export function notExist(element: Cypress.Chainable<Element>): void { element.should('not.exist'); } -export function haveText(element: any, text: string): void { +export function haveText(element: Cypress.Chainable<Element>, text: string): void { element .invoke('text') .then((elementText) => elementText.trim()) .should('equal', text); } -export function haveValue(element: any, value: string): void { +export function haveValue(element: Cypress.Chainable<Element>, value: string): void { element.should('have.value', value); } -export function haveFocus(element: any): void { +export function haveFocus(element: Cypress.Chainable<Element>): void { element.should('have.focus'); } -export function mouseEnter(element: any): void { +export function mouseEnter(element: Cypress.Chainable<Element>): void { element.trigger('mouseenter'); } -export function mouseOver(element: any): void { +export function mouseOver(element: Cypress.Chainable<Element>): void { element.trigger('mouseover'); } -export function contains(element: any, containing: string): void { +export function contains(element: Cypress.Chainable<Element>, containing: string): void { element.should('exist').contains(containing); } -export function notContains(element: any, containing: string): void { +export function notContains(element: Cypress.Chainable<Element>, containing: string): void { element.contains(containing).should('not.exist'); } -export function haveLength(element: any, length: number): void { +export function haveLength(element: Cypress.Chainable<Element>, length: number): void { element.should('have.length', length); } -export function beChecked(element: any): void { +export function beChecked(element: Cypress.Chainable<Element>): void { element.should('be.checked'); } -export function notBeChecked(element: any): void { +export function notBeChecked(element: Cypress.Chainable<Element>): void { element.should('not.be.checked'); } -export function beEnabled(element: any): void { +export function beEnabled(element: Cypress.Chainable<Element>): void { element.should('be.enabled'); } -export function notBeEnabled(element: any): void { +export function notBeEnabled(element: Cypress.Chainable<Element>): void { element.should('not.be.enabled'); } //TODO: "first()" rausnehmen -> im html eine entprechende data-test-id ansprechen?! | trennen in "get" und "verify" -export function shouldFirstContains(element: any, containing: string) { +export function shouldFirstContains(element: Cypress.Chainable<Element>, containing: string) { element.first().should('exist').contains(containing); } -export function shouldHaveAttributeBeGreaterThan(element: any, attributeName: string, value: number) { +export function shouldHaveAttributeBeGreaterThan(element: Cypress.Chainable<Element>, attributeName: string, value: number) { element.first().should('exist').invoke(attributeName).should('be.gt', value); } -export function shouldHaveAttributeBeLowerThan(element: any, attributeName: string, value: number) { +export function shouldHaveAttributeBeLowerThan(element: Cypress.Chainable<Element>, attributeName: string, value: number) { element.first().should('exist').invoke(attributeName).should('be.gt', value); } // -export function shouldHaveAttribute(element: any, name: string, value: string) { +export function shouldHaveAttribute(element: Cypress.Chainable<Element>, name: string, value: string) { element.should('have.attr', name, value); } -export function visible(element: any) { +export function visible(element: Cypress.Chainable<Element>) { element.should('be.visible'); } -export function notBeVisible(element: any) { +export function notBeVisible(element: Cypress.Chainable<Element>) { element.should('not.be.visible'); } -export function enter(element: any): void { +export function enter(element: Cypress.Chainable<Element>): void { element.clear().type(CypressKeyboardActions.ENTER); } @@ -109,7 +109,7 @@ export function typeText(element: Cypress.Chainable<JQuery<HTMLElement>>, value: element.type(value); } -export function backspaceOn(element: any): void { +export function backspaceOn(element: Cypress.Chainable<Element>): void { element.type(CypressKeyboardActions.BACKSPACE); } diff --git a/alfa-client/apps/admin/src/app/app.component.spec.ts b/alfa-client/apps/admin/src/app/app.component.spec.ts index 1d45f7a1d75b5a1d55960a88f9847df7745448d5..f0849bfe3e6898eeb941f3572ba9ff263a006df9 100644 --- a/alfa-client/apps/admin/src/app/app.component.spec.ts +++ b/alfa-client/apps/admin/src/app/app.component.spec.ts @@ -99,26 +99,28 @@ describe('AppComponent', () => { component = fixture.componentInstance; }); - it(`should have as title 'admin'`, () => { - const appTitle: string = fixture.componentInstance.title; + describe('component', () => { + it(`should have as title 'admin'`, () => { + const appTitle: string = fixture.componentInstance.title; - expect(appTitle).toEqual('admin'); - }); + expect(appTitle).toEqual('admin'); + }); - describe('ngOnInit', () => { - it('should call authService login', () => { - component.ngOnInit(); + describe('ngOnInit', () => { + it('should call authService login', () => { + component.ngOnInit(); - expect(authenticationService.login).toHaveBeenCalled(); - }); + expect(authenticationService.login).toHaveBeenCalled(); + }); - it('should call doAfterLoggedIn', async () => { - component.doAfterLoggedIn = jest.fn(); + it('should call doAfterLoggedIn', async () => { + component.doAfterLoggedIn = jest.fn(); - component.ngOnInit(); - await fixture.whenStable(); + component.ngOnInit(); + await fixture.whenStable(); - expect(component.doAfterLoggedIn).toHaveBeenCalled(); + expect(component.doAfterLoggedIn).toHaveBeenCalled(); + }); }); describe('do after logged in', () => { @@ -146,92 +148,93 @@ describe('AppComponent', () => { }); }); - it('show not show header if apiRoot is not loaded', () => { - component.apiRootStateResource$ = of(createEmptyStateResource<ApiRootResource>()); - - notExistsAsHtmlElement(fixture, adminHeaderSelector); - }); + describe('template', () => { + it('show not show header if apiRoot is not loaded', () => { + component.apiRootStateResource$ = of(createEmptyStateResource<ApiRootResource>()); - describe('user profile button', () => { - beforeEach(() => { - component.apiRootStateResource$ = of(createStateResource(createApiRootResource())); + notExistsAsHtmlElement(fixture, adminHeaderSelector); }); - it('should show if apiRoot exists', () => { - fixture.detectChanges(); + describe('user profile button', () => { + beforeEach(() => { + component.apiRootStateResource$ = of(createStateResource(createApiRootResource())); + }); + + it('should show if apiRoot exists', () => { + fixture.detectChanges(); - existsAsHtmlElement(fixture, userProfileButtonSelector); + existsAsHtmlElement(fixture, userProfileButtonSelector); + }); }); - }); - describe('administration logo', () => { - const apiResource: ApiRootResource = createApiRootResource(); + describe('administration logo', () => { + const apiResource: ApiRootResource = createApiRootResource(); - beforeEach(() => { - component.apiRootStateResource$ = of(createStateResource(apiResource)); - fixture.detectChanges(); - }); + beforeEach(() => { + component.apiRootStateResource$ = of(createStateResource(apiResource)); + fixture.detectChanges(); + }); - it('should navigate to start page on click', () => { - dispatchEventFromFixture(fixture, logoLink, 'click'); + it('should navigate to start page on click', () => { + dispatchEventFromFixture(fixture, logoLink, 'click'); - expect(router.navigate).toHaveBeenCalledWith([], { queryParams: {} }); + expect(router.navigate).toHaveBeenCalledWith([], { queryParams: {} }); + }); }); - }); - describe('navigation', () => { - beforeEach(() => {}); - it('should show links if configuration link exists', () => { - component.apiRootStateResource$ = of(createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION]))); - fixture.detectChanges(); + describe('navigation', () => { + it('should show links if configuration link exists', () => { + component.apiRootStateResource$ = of(createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION]))); + fixture.detectChanges(); - const navbarElement: HTMLElement = getElementFromFixture(fixture, navigationSelector); + const navbarElement: HTMLElement = getElementFromFixture(fixture, navigationSelector); - expect(navbarElement.children.length).toBeGreaterThan(0); - }); + expect(navbarElement.children.length).toBeGreaterThan(0); + }); - it('should not not show links if configuration resource not available', () => { - component.apiRootStateResource$ = of(createStateResource(createApiRootResource([]))); - fixture.detectChanges(); + it('should not not show links if configuration resource not available', () => { + component.apiRootStateResource$ = of(createStateResource(createApiRootResource([]))); + fixture.detectChanges(); - const navbarElement: HTMLElement = getElementFromFixture(fixture, navigationSelector); + const navbarElement: HTMLElement = getElementFromFixture(fixture, navigationSelector); - expect(navbarElement.children.length).toBe(0); + expect(navbarElement.children.length).toBe(0); + }); }); - }); - describe('build version', () => { - const apiResource: ApiRootResource = createApiRootResource(); + describe('build version', () => { + const apiResource: ApiRootResource = createApiRootResource(); - beforeEach(() => { - component.apiRootStateResource$ = of(createStateResource(apiResource)); - }); + beforeEach(() => { + component.apiRootStateResource$ = of(createStateResource(apiResource)); + }); - it('should show after apiRoot loaded', () => { - fixture.detectChanges(); + it('should show after apiRoot loaded', () => { + fixture.detectChanges(); - const buildVersionElement = getElementFromFixture(fixture, buildVersionSelector); - expect(buildVersionElement.textContent.trim()).toEqual(`Version: ${apiResource.version}`); + const buildVersionElement = getElementFromFixture(fixture, buildVersionSelector); + expect(buildVersionElement.textContent.trim()).toEqual(`Version: ${apiResource.version}`); + }); }); - }); - describe('router outlet', () => { - beforeEach(() => {}); + describe('router outlet', () => { + beforeEach(() => {}); - it('should exist if configuration resource available', () => { - component.apiRootStateResource$ = of(createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION]))); + it('should exist if configuration resource available', () => { + component.apiRootStateResource$ = of(createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION]))); - fixture.detectChanges(); + fixture.detectChanges(); - existsAsHtmlElement(fixture, routerOutletSelector); - }); + existsAsHtmlElement(fixture, routerOutletSelector); + }); - it('should not exist if configuration resource not available', () => { - component.apiRootStateResource$ = of(createStateResource(createApiRootResource())); + it('should not exist if configuration resource not available', () => { + component.apiRootStateResource$ = of(createStateResource(createApiRootResource())); - fixture.detectChanges(); + fixture.detectChanges(); - notExistsAsHtmlElement(fixture, routerOutletSelector); + notExistsAsHtmlElement(fixture, routerOutletSelector); + }); }); }); }); diff --git a/alfa-client/apps/admin/src/app/app.component.ts b/alfa-client/apps/admin/src/app/app.component.ts index 77de8e417f61f6ef636955150115c16607f3284a..48bd89bfa5c91ccbf7fa364250662e6a326e16e6 100644 --- a/alfa-client/apps/admin/src/app/app.component.ts +++ b/alfa-client/apps/admin/src/app/app.component.ts @@ -1,7 +1,7 @@ import { ApiRootLinkRel, ApiRootResource, ApiRootService } from '@alfa-client/api-root-shared'; import { StateResource } from '@alfa-client/tech-shared'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Params, Router } from '@angular/router'; import { AuthenticationService } from 'libs/authentication/src/lib/authentication.service'; import { Observable } from 'rxjs'; @@ -32,14 +32,14 @@ export class AppComponent implements OnInit { } removeAuthenticationParams() { - const queryParams = { ...this.route.snapshot.queryParams }; - delete queryParams['iss']; - delete queryParams['state']; - delete queryParams['session_state']; - delete queryParams['code']; - + const queryParams = this.getQueryParamsWithoutAuthentication(); this.router.navigate([], { queryParams }); } + private getQueryParamsWithoutAuthentication(): Params { + const { iss, state, session_state, code, ...queryParams } = this.route.snapshot.queryParams; + return queryParams; + } + protected readonly ApiRootLinkRel = ApiRootLinkRel; } diff --git a/alfa-client/apps/admin/src/pages/users-roles/user-add-page/user-add-page.component.html b/alfa-client/apps/admin/src/pages/users-roles/user-add-page/user-add-page.component.html index e250d918ada26e686d0b469614579efd32beb7df..dad58a8662dd19eb059273474e2df1474305d3b4 100644 --- a/alfa-client/apps/admin/src/pages/users-roles/user-add-page/user-add-page.component.html +++ b/alfa-client/apps/admin/src/pages/users-roles/user-add-page/user-add-page.component.html @@ -1 +1 @@ -<admin-user-add /> +<admin-user-add-form /> diff --git a/alfa-client/apps/admin/src/pages/users-roles/user-add-page/user-add-page.component.ts b/alfa-client/apps/admin/src/pages/users-roles/user-add-page/user-add-page.component.ts index 72e8eb19d601683042989f84b29caf5da2ddc3aa..f3bcc1eaf5f74df921a17932bea865ce96a61907 100644 --- a/alfa-client/apps/admin/src/pages/users-roles/user-add-page/user-add-page.component.ts +++ b/alfa-client/apps/admin/src/pages/users-roles/user-add-page/user-add-page.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-user-add-page', + selector: 'user-add-page', templateUrl: './user-add-page.component.html', }) export class UserAddPageComponent {} diff --git a/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach-signatur/postfach-signatur.component.spec.ts b/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach-signatur/postfach-signatur.component.spec.ts index ba1b3c8bfa751c7711220edb3d4a91e3c887e8d6..9d8db826b2b839234c1a12ce65caa10e2480e2bb 100644 --- a/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach-signatur/postfach-signatur.component.spec.ts +++ b/alfa-client/libs/admin/settings/src/lib/postfach/postfach-container/postfach-form/postfach-signatur/postfach-signatur.component.spec.ts @@ -14,12 +14,12 @@ describe('PostfachSignaturComponent', () => { let fixture: ComponentFixture<PostfachSignaturComponent>; let formService: Mock<PostfachFormService>; - let postFachService: Mock<PostfachService>; + let postfachService: Mock<PostfachService>; const signaturTextarea = getDataTestIdOf('signatur-text'); beforeEach(async () => { formService = mock(PostfachFormService); - postFachService = mock(PostfachService); + postfachService = mock(PostfachService); await TestBed.configureTestingModule({ imports: [ReactiveFormsModule], declarations: [PostfachFormComponent, MockComponent(TextareaEditorComponent)], @@ -30,7 +30,7 @@ describe('PostfachSignaturComponent', () => { }, { provide: PostfachService, - useValue: postFachService, + useValue: postfachService, }, ], }).compileComponents(); diff --git a/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.component.html b/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.component.html index 796154394f88512179d85d06dc6f3c56165df50a..1536bc42050127ed1f837d7a36de32b1ef513025 100644 --- a/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.component.html +++ b/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.component.html @@ -8,7 +8,7 @@ </div> <h3 class="text-md mb-4 block font-medium text-text">Organisationseinheiten</h3> - <ods-button-with-spinner text="Organisationseinheit hinzufügen" variant="outline" dataTestId="add-organisationseinheit-button" /> + <ods-button-with-spinner text="Organisationseinheit hinzufügen" variant="outline" dataTestId="Add-organisationseinheit-button" /> <h2 class="heading-2 mt-4">Rollen für OZG-Cloud</h2> <div [formGroupName]="UserAddFormService.ROLLEN_GROUP" class="mb-8 flex gap-56"> diff --git a/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.component.ts b/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.component.ts index d1d34c5758b1a1dbc29bfc85f5e3a5ac803a3880..ee7830b3127a701025620bc208c559b2c05eb6d4 100644 --- a/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.component.ts +++ b/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.component.ts @@ -1,8 +1,8 @@ import { Component, inject } from '@angular/core'; -import { UserAddFormService } from './user-add-form-service'; +import { UserAddFormService } from './user-add-form.service'; @Component({ - selector: 'admin-user-add', + selector: 'admin-user-add-form', providers: [UserAddFormService], templateUrl: './user-add-form.component.html', }) diff --git a/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form-service.ts b/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.service.ts similarity index 97% rename from alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form-service.ts rename to alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.service.ts index dadf58f31bf42880f19814bd8c2993d354cac6bf..c62a13582acc0eef0685e212995f82cc487e40e4 100644 --- a/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form-service.ts +++ b/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form.service.ts @@ -66,7 +66,7 @@ export class UserAddFormService extends AbstractFormService { } isAnyChecked(group: UntypedFormGroup): boolean { - return Object.keys(group.controls).some((key) => group.controls[key].value === true); + return Object.keys(group.controls).some((key) => group.controls[key].value); } disableUncheckedCheckboxes(alfaGroup: UntypedFormGroup): void { @@ -86,6 +86,6 @@ export class UserAddFormService extends AbstractFormService { } protected getPathPrefix(): string { - throw UserAddFormService.USER_ADD_PREFIX; + return UserAddFormService.USER_ADD_PREFIX; } } diff --git a/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form-service.spec.ts b/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-formservice.spec.ts similarity index 98% rename from alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form-service.spec.ts rename to alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-formservice.spec.ts index 1f9991ca63cfe0b61b41c49374b6d1c402b7766d..9ab65b7d351285b013d3833e3e9c977c63f486ac 100644 --- a/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-form-service.spec.ts +++ b/alfa-client/libs/admin/settings/src/lib/users-roles/user-add-form/user-add-formservice.spec.ts @@ -1,6 +1,6 @@ import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { UserAddFormService } from './user-add-form-service'; +import { UserAddFormService } from './user-add-form.service'; import SpyInstance = jest.SpyInstance; describe('UserAddFormService', () => { diff --git a/alfa-client/libs/admin/settings/src/lib/users-roles/users-roles.component.html b/alfa-client/libs/admin/settings/src/lib/users-roles/users-roles.component.html index 7d83748eaa430c942e91e270af7f92af0d8b01d3..b9d08648462df8ef917ab8c201bd0f8948e68165 100644 --- a/alfa-client/libs/admin/settings/src/lib/users-roles/users-roles.component.html +++ b/alfa-client/libs/admin/settings/src/lib/users-roles/users-roles.component.html @@ -2,11 +2,11 @@ <ods-button-with-spinner text="Benutzer hinzufügen" class="py-8" - dataTestId="add-user-button" + dataTestId="Add-user-button" (clickEmitter)="navigateToAddUser()" /> <ods-list *ngIf="users$ | async as users"> - <ods-list-item *ngFor="let user of users.resource" [path]="user.username" [attr.data-test-id]="'user-entry-' + user.username"> + <ods-list-item *ngFor="let user of users.resource" [path]="user.username" [attr.data-test-id]="'User-entry-' + user.username"> <div class="flex-1 basis-1/2"> <div class="mb-2 flex flex-wrap items-center gap-3"> <h3 class="text-md font-semibold">{{ user | toUserName }}</h3>