diff --git a/src/app/extensions/captcha/exports/lazy-captcha/lazy-captcha.component.spec.ts b/src/app/extensions/captcha/exports/lazy-captcha/lazy-captcha.component.spec.ts index d10339efbd..1d6725f378 100644 --- a/src/app/extensions/captcha/exports/lazy-captcha/lazy-captcha.component.spec.ts +++ b/src/app/extensions/captcha/exports/lazy-captcha/lazy-captcha.component.spec.ts @@ -1,10 +1,14 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { FormControl, FormGroup } from '@angular/forms'; import { By } from '@angular/platform-browser'; -import { RECAPTCHA_V3_SITE_KEY } from 'ng-recaptcha'; +import { TranslateModule } from '@ngx-translate/core'; +import { MockDirective } from 'ng-mocks'; +import { RECAPTCHA_V3_SITE_KEY, ReCaptchaV3Service } from 'ng-recaptcha'; import { EMPTY, of } from 'rxjs'; import { anyString, instance, mock, when } from 'ts-mockito'; +import { ServerHtmlDirective } from 'ish-core/directives/server-html.directive'; + import { CaptchaFacade } from '../../facades/captcha.facade'; import { CaptchaV2Component, CaptchaV2ComponentModule } from '../../shared/captcha-v2/captcha-v2.component'; import { CaptchaV3Component, CaptchaV3ComponentModule } from '../../shared/captcha-v3/captcha-v3.component'; @@ -30,8 +34,13 @@ describe('Lazy Captcha Component', () => { .overrideModule(CaptchaV2ComponentModule, { set: { entryComponents: [CaptchaV2Component] } }) .overrideModule(CaptchaV3ComponentModule, { set: { + imports: [TranslateModule.forRoot()], + declarations: [CaptchaV3Component, MockDirective(ServerHtmlDirective)], entryComponents: [CaptchaV3Component], - providers: [{ provide: RECAPTCHA_V3_SITE_KEY, useValue: 'captchaSiteKeyQWERTY' }], + providers: [ + { provide: RECAPTCHA_V3_SITE_KEY, useValue: 'captchaSiteKeyQWERTY' }, + { provide: ReCaptchaV3Service }, + ], }, }) .compileComponents(); @@ -70,7 +79,14 @@ describe('Lazy Captcha Component', () => { fixture.detectChanges(); tick(500); - expect(element).toMatchInlineSnapshot(``); + expect(element).toMatchInlineSnapshot(` +
+
+

+
+ `); const v3Cmp: CaptchaV3Component = fixture.debugElement.query(By.css('ish-captcha-v3'))?.componentInstance; expect(v3Cmp).toBeTruthy(); })); diff --git a/src/app/extensions/captcha/exports/lazy-captcha/lazy-captcha.component.ts b/src/app/extensions/captcha/exports/lazy-captcha/lazy-captcha.component.ts index d31d289359..544919acf3 100644 --- a/src/app/extensions/captcha/exports/lazy-captcha/lazy-captcha.component.ts +++ b/src/app/extensions/captcha/exports/lazy-captcha/lazy-captcha.component.ts @@ -88,6 +88,7 @@ export class LazyCaptchaComponent implements OnInit, AfterViewInit, OnDestroy { const componentRef = this.anchor.createComponent(factory); componentRef.instance.parentForm = this.form; + componentRef.changeDetectorRef.markForCheck(); } else if (version === 2) { this.formControl.setValidators([Validators.required]); this.formControl.updateValueAndValidity(); @@ -103,6 +104,7 @@ export class LazyCaptchaComponent implements OnInit, AfterViewInit, OnDestroy { componentRef.instance.cssClass = this.cssClass; componentRef.instance.parentForm = this.form; + componentRef.changeDetectorRef.markForCheck(); } }); } diff --git a/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.html b/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.html new file mode 100644 index 0000000000..e5972a8a9e --- /dev/null +++ b/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.html @@ -0,0 +1,12 @@ +
+
+

+
+
diff --git a/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.spec.ts b/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.spec.ts index abebf37700..4cc9308666 100644 --- a/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.spec.ts +++ b/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.spec.ts @@ -1,7 +1,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormControl, FormGroup } from '@angular/forms'; +import { TranslateModule } from '@ngx-translate/core'; +import { MockDirective } from 'ng-mocks'; import { RECAPTCHA_V3_SITE_KEY, RecaptchaV3Module } from 'ng-recaptcha'; +import { ServerHtmlDirective } from 'ish-core/directives/server-html.directive'; +import { findAllDataTestingIDs } from 'ish-core/utils/dev/html-query-utils'; + import { CaptchaV3Component } from './captcha-v3.component'; describe('Captcha V3 Component', () => { @@ -12,8 +17,8 @@ describe('Captcha V3 Component', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [CaptchaV3Component], - imports: [RecaptchaV3Module], + declarations: [CaptchaV3Component, MockDirective(ServerHtmlDirective)], + imports: [RecaptchaV3Module, TranslateModule.forRoot()], providers: [{ provide: RECAPTCHA_V3_SITE_KEY, useValue: captchaSiteKey }], }).compileComponents(); }); @@ -33,4 +38,13 @@ describe('Captcha V3 Component', () => { expect(element).toBeTruthy(); expect(() => fixture.detectChanges()).not.toThrow(); }); + + it('should render recaptcha info text when created', () => { + fixture.detectChanges(); + expect(findAllDataTestingIDs(fixture)).toMatchInlineSnapshot(` + Array [ + "recaptcha-v3-info", + ] + `); + }); }); diff --git a/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.ts b/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.ts index 14b13002af..a625751a73 100644 --- a/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.ts +++ b/src/app/extensions/captcha/shared/captcha-v3/captcha-v3.component.ts @@ -1,9 +1,12 @@ import { ChangeDetectionStrategy, Component, Input, NgModule, OnDestroy, OnInit } from '@angular/core'; import { FormGroup, Validators } from '@angular/forms'; +import { TranslateModule } from '@ngx-translate/core'; import { RECAPTCHA_V3_SITE_KEY, ReCaptchaV3Service, RecaptchaV3Module } from 'ng-recaptcha'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { DirectivesModule } from 'ish-core/directives.module'; + import { SitekeyProviderService, getSynchronizedSiteKey, @@ -16,7 +19,7 @@ import { */ @Component({ selector: 'ish-captcha-v3', - template: '', + templateUrl: './captcha-v3.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class CaptchaV3Component implements OnInit, OnDestroy { @@ -45,7 +48,7 @@ export class CaptchaV3Component implements OnInit, OnDestroy { } @NgModule({ - imports: [RecaptchaV3Module], + imports: [RecaptchaV3Module, TranslateModule, DirectivesModule], declarations: [CaptchaV3Component], providers: [ { diff --git a/src/assets/i18n/de_DE.json b/src/assets/i18n/de_DE.json index 934739bb24..b224503125 100644 --- a/src/assets/i18n/de_DE.json +++ b/src/assets/i18n/de_DE.json @@ -868,6 +868,7 @@ "quote.state.unknown": "Unbekannt", "quoterequest.not_editable.error": "Sie haben die Preisanfrage bereits gesendet. Laden Sie diese Seite neu, um die Änderungen zu sehen.", "recaptcha.v2.incorrect.error": "Beweisen Sie, dass Sie keine Maschine sind.", + "recaptcha.v3.info_text": "Diese Seite ist durch reCAPTCHA geschützt und es gelten die Google Datenschutzbestimmungen und Nutzungsbedingungen.", "recentlyViewed.component.heading": "Zuletzt angesehen", "registration.tac.error.tip": "Bitte stimmen Sie den Bedingungen zu, um fortzufahren.", "registration.tac_privacy_policy.text": "Ich akzeptiere die AGB und Datenschutzhinweise.", diff --git a/src/assets/i18n/en_US.json b/src/assets/i18n/en_US.json index 5b6ac69171..70578a09fc 100644 --- a/src/assets/i18n/en_US.json +++ b/src/assets/i18n/en_US.json @@ -870,6 +870,7 @@ "quote.state.unknown": "Unknown", "quoterequest.not_editable.error": "You have already submitted the quote request. Please reload this page to view the changes.", "recaptcha.v2.incorrect.error": "Please verify you are a real person.", + "recaptcha.v3.info_text": "This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.", "recentlyViewed.component.heading": "Recently Viewed", "registration.tac.error.tip": "Please agree to the terms to continue.", "registration.tac_privacy_policy.text": "I agree to the Terms & Conditions and Privacy Policy.", diff --git a/src/assets/i18n/fr_FR.json b/src/assets/i18n/fr_FR.json index c41a705c5a..bd68a6a4f4 100644 --- a/src/assets/i18n/fr_FR.json +++ b/src/assets/i18n/fr_FR.json @@ -870,6 +870,7 @@ "quote.state.unknown": "Inconnu", "quoterequest.not_editable.error": "Vous avez déjà soumis la demande de devis. Veuillez rafraîchir cette page pour afficher les modifications.", "recaptcha.v2.incorrect.error": "Veuillez vérifier que vous êtes une personne réelle.", + "recaptcha.v3.info_text": "Ce site est protégé par reCAPTCHA et la politique de confidentialité de Google ainsi que ses conditions de service s'appliquent.", "recentlyViewed.component.heading": "Récemment consultés", "registration.tac.error.tip": "Veuillez accepter les conditions pour continuer.", "registration.tac_privacy_policy.text": "J’accepte les Conditions générales et la Politique de confidentialité.", diff --git a/src/styles/global/forms/forms.scss b/src/styles/global/forms/forms.scss index b0f1d82f3a..7461b74f34 100644 --- a/src/styles/global/forms/forms.scss +++ b/src/styles/global/forms/forms.scss @@ -99,6 +99,10 @@ input[type='file'] { } } +.grecaptcha-badge { + visibility: hidden; +} + .filter-dropdown { position: relative; padding-bottom: 15px;