From f7d7cd3a83b3a637a81c4e6e465334de07357ef4 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Fri, 12 Apr 2024 10:40:06 +0800
Subject: [PATCH 01/66] CXSPA-6672: add otp login
---
feature-libs/user/_index.scss | 2 +-
.../login-form/login-form.module.ts | 20 ++++-
.../login-form/otp-login-form.component.ts | 78 +++++++++++++++++++
.../login-form/use-otp-login-form.ts | 14 ++++
.../account/core/connectors/converters.ts | 12 ++-
.../core/connectors/user-account.adapter.ts | 9 ++-
.../core/connectors/user-account.connector.ts | 9 ++-
.../account/core/facade/facade-providers.ts | 13 +++-
.../user/account/core/facade/index.ts | 1 +
.../core/facade/verification-token.service.ts | 34 ++++++++
...efault-occ-user-account-endpoint.config.ts | 7 +-
.../config/occ-user-account-endpoint.model.ts | 5 ++
.../occ/adapters/occ-user-account.adapter.ts | 38 ++++++++-
.../user/account/root/facade/index.ts | 1 +
.../root/facade/verification-token.facade.ts | 26 +++++++
.../user/account/root/model/user.model.ts | 12 +++
feature-libs/user/account/styles/_index.scss | 1 +
.../user/account/styles/_otp-login-form.scss | 10 +++
.../features/user/user-feature.module.ts | 6 ++
.../src/environments/environment.prod.ts | 1 +
.../src/environments/environment.ts | 1 +
.../models/build.process.env.d.ts | 1 +
.../environments/models/environment.model.ts | 1 +
23 files changed, 289 insertions(+), 13 deletions(-)
create mode 100644 feature-libs/user/account/components/login-form/otp-login-form.component.ts
create mode 100644 feature-libs/user/account/components/login-form/use-otp-login-form.ts
create mode 100644 feature-libs/user/account/core/facade/verification-token.service.ts
create mode 100644 feature-libs/user/account/root/facade/verification-token.facade.ts
create mode 100644 feature-libs/user/account/styles/_otp-login-form.scss
diff --git a/feature-libs/user/_index.scss b/feature-libs/user/_index.scss
index e86984fedb2..b0b3c3c223a 100644
--- a/feature-libs/user/_index.scss
+++ b/feature-libs/user/_index.scss
@@ -8,7 +8,7 @@ $skipComponentStyles: () !default;
$selectors: cx-address-book, cx-address-form, cx-suggested-addresses-dialog,
cx-login, cx-login-form, cx-register, cx-reset-password, cx-close-account,
cx-close-account-modal, cx-my-account-v2-profile, cx-my-account-v2-email,
- cx-my-account-v2-password !default;
+ cx-my-account-v2-password, cx-otp-login-form !default;
@each $selector in $selectors {
#{$selector} {
diff --git a/feature-libs/user/account/components/login-form/login-form.module.ts b/feature-libs/user/account/components/login-form/login-form.module.ts
index f89dd4404c1..eff4bbd6892 100644
--- a/feature-libs/user/account/components/login-form/login-form.module.ts
+++ b/feature-libs/user/account/components/login-form/login-form.module.ts
@@ -5,7 +5,7 @@
*/
import { CommonModule } from '@angular/common';
-import { NgModule } from '@angular/core';
+import { NgModule, inject } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import {
@@ -18,6 +18,7 @@ import {
UrlModule,
WindowRef,
provideDefaultConfig,
+ provideDefaultConfigFactory,
} from '@spartacus/core';
import {
FormErrorsModule,
@@ -26,6 +27,17 @@ import {
} from '@spartacus/storefront';
import { LoginFormComponentService } from './login-form-component.service';
import { LoginFormComponent } from './login-form.component';
+import { OneTimePasswordLoginFormComponent } from './otp-login-form.component';
+
+import { USE_ONE_TIME_PASSWORD_LOGIN } from './use-otp-login-form';
+
+const oneTimePasswordLoginFormMapping: CmsConfig = {
+ cmsComponents: {
+ ReturningCustomerLoginComponent: {
+ component: OneTimePasswordLoginFormComponent,
+ },
+ },
+};
@NgModule({
imports: [
@@ -56,7 +68,11 @@ import { LoginFormComponent } from './login-form.component';
},
},
}),
+ provideDefaultConfigFactory(() =>
+ inject(USE_ONE_TIME_PASSWORD_LOGIN) ? oneTimePasswordLoginFormMapping : {}
+ ),
],
- declarations: [LoginFormComponent],
+ declarations: [LoginFormComponent, OneTimePasswordLoginFormComponent],
+ exports: [LoginFormComponent, OneTimePasswordLoginFormComponent],
})
export class LoginFormModule {}
diff --git a/feature-libs/user/account/components/login-form/otp-login-form.component.ts b/feature-libs/user/account/components/login-form/otp-login-form.component.ts
new file mode 100644
index 00000000000..b98fb4b6cba
--- /dev/null
+++ b/feature-libs/user/account/components/login-form/otp-login-form.component.ts
@@ -0,0 +1,78 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ ChangeDetectionStrategy,
+ Component,
+ HostBinding,
+ inject,
+} from '@angular/core';
+import {
+ UntypedFormControl,
+ UntypedFormGroup,
+ Validators,
+} from '@angular/forms';
+import { WindowRef } from '@spartacus/core';
+import { CustomFormValidators } from '@spartacus/storefront';
+import { BehaviorSubject, Observable, tap } from 'rxjs';
+import { VerificationTokenFacade } from '../../root/facade/verification-token.facade';
+import { LoginForm } from '../../root/model';
+import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from './use-otp-login-form';
+
+@Component({
+ selector: 'cx-otp-login-form',
+ templateUrl: './login-form.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class OneTimePasswordLoginFormComponent {
+ protected verificationTokenFacade = inject(VerificationTokenFacade);
+ protected winRef = inject(WindowRef);
+
+ protected busy$ = new BehaviorSubject(false);
+
+ isUpdating$: Observable = this.busy$.pipe(
+ tap((state) => {
+ const userId = this.winRef.nativeWindow?.history?.state?.['newUid'];
+ if (userId) {
+ this.form.patchValue({ userId });
+ }
+ state === true ? this.form.disable() : this.form.enable();
+ })
+ );
+
+ form: UntypedFormGroup = new UntypedFormGroup({
+ userId: new UntypedFormControl('', [
+ Validators.required,
+ CustomFormValidators.emailValidator,
+ ]),
+ password: new UntypedFormControl('', Validators.required),
+ });
+
+ @HostBinding('class.user-form') style = true;
+
+ onSubmit(): void {
+ if (!this.form.valid) {
+ this.form.markAllAsTouched();
+ return;
+ }
+
+ this.busy$.next(true);
+ this.verificationTokenFacade.createVerificationToken(
+ this.collectDataFromLoginForm()
+ );
+ this.busy$.next(false);
+ }
+
+ collectDataFromLoginForm(): LoginForm {
+ return {
+ // TODO: consider dropping toLowerCase as this should not be part of the UI,
+ // as it's too opinionated and doesn't work with other AUTH services
+ loginId: this.form.value.userId.toLowerCase(),
+ password: this.form.value.password,
+ purpose: ONE_TIME_PASSWORD_LOGIN_PURPOSE,
+ };
+ }
+}
diff --git a/feature-libs/user/account/components/login-form/use-otp-login-form.ts b/feature-libs/user/account/components/login-form/use-otp-login-form.ts
new file mode 100644
index 00000000000..3f9e29cf811
--- /dev/null
+++ b/feature-libs/user/account/components/login-form/use-otp-login-form.ts
@@ -0,0 +1,14 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { InjectionToken } from '@angular/core';
+
+export const USE_ONE_TIME_PASSWORD_LOGIN = new InjectionToken(
+ 'feature flag to enable enhanced UI related pages login',
+ { providedIn: 'root', factory: () => false }
+);
+
+export const ONE_TIME_PASSWORD_LOGIN_PURPOSE = 'LOGIN';
diff --git a/feature-libs/user/account/core/connectors/converters.ts b/feature-libs/user/account/core/connectors/converters.ts
index 33b343472f8..163a5c2c131 100644
--- a/feature-libs/user/account/core/connectors/converters.ts
+++ b/feature-libs/user/account/core/connectors/converters.ts
@@ -6,7 +6,7 @@
import { InjectionToken } from '@angular/core';
import { Converter } from '@spartacus/core';
-import { User } from '@spartacus/user/account/root';
+import { LoginForm, User, VerificationToken } from '@spartacus/user/account/root';
export const USER_ACCOUNT_NORMALIZER = new InjectionToken>(
'UserAccountNormalizer'
@@ -15,3 +15,13 @@ export const USER_ACCOUNT_NORMALIZER = new InjectionToken>(
export const USER_ACCOUNT_SERIALIZER = new InjectionToken>(
'UserAccountSerializer'
);
+
+
+export const VERIFICATION_TOKEN_NORMALIZER = new InjectionToken>(
+ 'VerificationTokenNormalizer'
+);
+
+export const LOGIN_FORM_SERIALIZER = new InjectionToken>(
+ 'LoginFormSerializer'
+);
+
diff --git a/feature-libs/user/account/core/connectors/user-account.adapter.ts b/feature-libs/user/account/core/connectors/user-account.adapter.ts
index 01fc77dab4a..153fc8bc804 100644
--- a/feature-libs/user/account/core/connectors/user-account.adapter.ts
+++ b/feature-libs/user/account/core/connectors/user-account.adapter.ts
@@ -4,9 +4,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import {
+ LoginForm,
+ User,
+ VerificationToken,
+} from '@spartacus/user/account/root';
import { Observable } from 'rxjs';
-import { User } from '@spartacus/user/account/root';
export abstract class UserAccountAdapter {
abstract load(userId: string): Observable;
+ abstract createVerificationToken(
+ form: LoginForm
+ ): Observable;
}
diff --git a/feature-libs/user/account/core/connectors/user-account.connector.ts b/feature-libs/user/account/core/connectors/user-account.connector.ts
index d2c5580da52..428414d4853 100644
--- a/feature-libs/user/account/core/connectors/user-account.connector.ts
+++ b/feature-libs/user/account/core/connectors/user-account.connector.ts
@@ -5,8 +5,12 @@
*/
import { Injectable } from '@angular/core';
+import {
+ LoginForm,
+ User,
+ VerificationToken,
+} from '@spartacus/user/account/root';
import { Observable } from 'rxjs';
-import { User } from '@spartacus/user/account/root';
import { UserAccountAdapter } from './user-account.adapter';
@Injectable()
@@ -16,4 +20,7 @@ export class UserAccountConnector {
get(userId: string): Observable {
return this.adapter.load(userId);
}
+ createVerificationToken(form: LoginForm): Observable {
+ return this.adapter.createVerificationToken(form);
+ }
}
diff --git a/feature-libs/user/account/core/facade/facade-providers.ts b/feature-libs/user/account/core/facade/facade-providers.ts
index c6039c582dd..da7ba9f8c32 100644
--- a/feature-libs/user/account/core/facade/facade-providers.ts
+++ b/feature-libs/user/account/core/facade/facade-providers.ts
@@ -4,9 +4,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { UserAccountService } from './user-account.service';
-import { UserAccountFacade } from '@spartacus/user/account/root';
import { Provider } from '@angular/core';
+import {
+ UserAccountFacade,
+ VerificationTokenFacade,
+} from '@spartacus/user/account/root';
+import { UserAccountService } from './user-account.service';
+import { VerificationTokenService } from './verification-token.service';
export const facadeProviders: Provider[] = [
UserAccountService,
@@ -14,4 +18,9 @@ export const facadeProviders: Provider[] = [
provide: UserAccountFacade,
useExisting: UserAccountService,
},
+ VerificationTokenService,
+ {
+ provide: VerificationTokenFacade,
+ useExisting: VerificationTokenService,
+ },
];
diff --git a/feature-libs/user/account/core/facade/index.ts b/feature-libs/user/account/core/facade/index.ts
index ec5c6228586..eed2971eff1 100644
--- a/feature-libs/user/account/core/facade/index.ts
+++ b/feature-libs/user/account/core/facade/index.ts
@@ -5,3 +5,4 @@
*/
export * from './user-account.service';
+export * from './verification-token.service';
diff --git a/feature-libs/user/account/core/facade/verification-token.service.ts b/feature-libs/user/account/core/facade/verification-token.service.ts
new file mode 100644
index 00000000000..6238ae2ade9
--- /dev/null
+++ b/feature-libs/user/account/core/facade/verification-token.service.ts
@@ -0,0 +1,34 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable } from '@angular/core';
+import { Command, CommandService } from '@spartacus/core';
+import { Observable } from 'rxjs';
+import { VerificationTokenFacade } from '../../root/facade';
+import { LoginForm, VerificationToken } from '../../root/model';
+import { UserAccountConnector } from '../connectors';
+
+@Injectable()
+export class VerificationTokenService implements VerificationTokenFacade {
+ protected createVerificationTokenCommand: Command<
+ { form: LoginForm },
+ VerificationToken
+ > = this.command.create(({ form }) =>
+ this.connector.createVerificationToken(form)
+ );
+
+ constructor(
+ protected connector: UserAccountConnector,
+ protected command: CommandService
+ ) {}
+
+ /**
+ * create verification token
+ */
+ createVerificationToken(form: LoginForm): Observable {
+ return this.createVerificationTokenCommand.execute({ form });
+ }
+}
diff --git a/feature-libs/user/account/occ/adapters/config/default-occ-user-account-endpoint.config.ts b/feature-libs/user/account/occ/adapters/config/default-occ-user-account-endpoint.config.ts
index 52120c3a04f..d4340ec1665 100644
--- a/feature-libs/user/account/occ/adapters/config/default-occ-user-account-endpoint.config.ts
+++ b/feature-libs/user/account/occ/adapters/config/default-occ-user-account-endpoint.config.ts
@@ -8,6 +8,11 @@ import { OccConfig } from '@spartacus/core';
export const defaultOccUserAccountConfig: OccConfig = {
backend: {
- occ: { endpoints: { user: 'users/${userId}' } },
+ occ: {
+ endpoints: {
+ user: 'users/${userId}',
+ createVerificationToken: 'users/anonymous/verificationToken',
+ },
+ },
},
};
diff --git a/feature-libs/user/account/occ/adapters/config/occ-user-account-endpoint.model.ts b/feature-libs/user/account/occ/adapters/config/occ-user-account-endpoint.model.ts
index cdd6bfbaf19..e04ae1cd358 100644
--- a/feature-libs/user/account/occ/adapters/config/occ-user-account-endpoint.model.ts
+++ b/feature-libs/user/account/occ/adapters/config/occ-user-account-endpoint.model.ts
@@ -11,6 +11,11 @@ export interface UserAccountOccEndpoints {
* Get user account details
*/
user?: string | OccEndpoint;
+
+ /**
+ * Create one time password for user login
+ */
+ createVerificationToken?: string | OccEndpoint;
}
declare module '@spartacus/core' {
interface OccEndpoints extends UserAccountOccEndpoints {}
diff --git a/feature-libs/user/account/occ/adapters/occ-user-account.adapter.ts b/feature-libs/user/account/occ/adapters/occ-user-account.adapter.ts
index 494284031d2..7971877823c 100644
--- a/feature-libs/user/account/occ/adapters/occ-user-account.adapter.ts
+++ b/feature-libs/user/account/occ/adapters/occ-user-account.adapter.ts
@@ -4,23 +4,33 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { HttpClient } from '@angular/common/http';
-import { inject, Injectable } from '@angular/core';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { Injectable, inject } from '@angular/core';
import {
ConverterService,
+ InterceptorUtil,
LoggerService,
- normalizeHttpError,
Occ,
OccEndpointsService,
+ USE_CLIENT_TOKEN,
+ normalizeHttpError,
} from '@spartacus/core';
import {
+ LOGIN_FORM_SERIALIZER,
USER_ACCOUNT_NORMALIZER,
UserAccountAdapter,
+ VERIFICATION_TOKEN_NORMALIZER,
} from '@spartacus/user/account/core';
-import { User } from '@spartacus/user/account/root';
+import {
+ LoginForm,
+ User,
+ VerificationToken,
+} from '@spartacus/user/account/root';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
+const CONTENT_TYPE_JSON_HEADER = { 'Content-Type': 'application/json' };
+
@Injectable()
export class OccUserAccountAdapter implements UserAccountAdapter {
protected logger = inject(LoggerService);
@@ -40,4 +50,24 @@ export class OccUserAccountAdapter implements UserAccountAdapter {
this.converter.pipeable(USER_ACCOUNT_NORMALIZER)
);
}
+
+ createVerificationToken(form: LoginForm): Observable {
+ const url = this.occEndpoints.buildUrl('createVerificationToken');
+
+ const headers = InterceptorUtil.createHeader(
+ USE_CLIENT_TOKEN,
+ true,
+ new HttpHeaders({
+ ...CONTENT_TYPE_JSON_HEADER,
+ })
+ );
+ form = this.converter.convert(form, LOGIN_FORM_SERIALIZER);
+
+ return this.http.post(url, form, { headers }).pipe(
+ catchError((error) => {
+ throw normalizeHttpError(error, this.logger);
+ }),
+ this.converter.pipeable(VERIFICATION_TOKEN_NORMALIZER)
+ );
+ }
}
diff --git a/feature-libs/user/account/root/facade/index.ts b/feature-libs/user/account/root/facade/index.ts
index 52276c66cfd..fa1ec5e5a4d 100644
--- a/feature-libs/user/account/root/facade/index.ts
+++ b/feature-libs/user/account/root/facade/index.ts
@@ -5,3 +5,4 @@
*/
export * from './user-account.facade';
+export * from './verification-token.facade';
diff --git a/feature-libs/user/account/root/facade/verification-token.facade.ts b/feature-libs/user/account/root/facade/verification-token.facade.ts
new file mode 100644
index 00000000000..bae1c7fa3ce
--- /dev/null
+++ b/feature-libs/user/account/root/facade/verification-token.facade.ts
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable } from '@angular/core';
+import { facadeFactory } from '@spartacus/core';
+import { Observable } from 'rxjs';
+import { USER_ACCOUNT_CORE_FEATURE } from '../feature-name';
+import { LoginForm, VerificationToken } from '../model';
+
+@Injectable({
+ providedIn: 'root',
+ useFactory: () =>
+ facadeFactory({
+ facade: VerificationTokenFacade,
+ feature: USER_ACCOUNT_CORE_FEATURE,
+ methods: ['createVerificationToken'],
+ }),
+})
+export abstract class VerificationTokenFacade {
+ abstract createVerificationToken(
+ form: LoginForm
+ ): Observable;
+}
diff --git a/feature-libs/user/account/root/model/user.model.ts b/feature-libs/user/account/root/model/user.model.ts
index 976fa7fe4f1..d89ba2eeb9a 100644
--- a/feature-libs/user/account/root/model/user.model.ts
+++ b/feature-libs/user/account/root/model/user.model.ts
@@ -13,3 +13,15 @@ export interface User {
customerId?: string;
title?: string;
}
+
+export interface LoginForm {
+ purpose: string;
+ loginId: string;
+ password: string;
+}
+
+export interface VerificationToken {
+ expiresIn: string;
+ tokenId: string;
+}
+
diff --git a/feature-libs/user/account/styles/_index.scss b/feature-libs/user/account/styles/_index.scss
index 0dff952259b..791101b695f 100644
--- a/feature-libs/user/account/styles/_index.scss
+++ b/feature-libs/user/account/styles/_index.scss
@@ -1,3 +1,4 @@
@import './login';
@import './login-form';
@import './my-account-v2-user';
+@import './otp-login-form';
diff --git a/feature-libs/user/account/styles/_otp-login-form.scss b/feature-libs/user/account/styles/_otp-login-form.scss
new file mode 100644
index 00000000000..0ed3a2d68bd
--- /dev/null
+++ b/feature-libs/user/account/styles/_otp-login-form.scss
@@ -0,0 +1,10 @@
+%cx-otp-login-form {
+ &.user-form {
+ cx-spinner {
+ display: none;
+ }
+ button {
+ flex: 100%;
+ }
+ }
+}
diff --git a/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts
index b948e9a0674..57d38ef58a9 100644
--- a/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts
+++ b/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts
@@ -28,6 +28,8 @@ import {
USER_PROFILE_FEATURE,
} from '@spartacus/user/profile/root';
import { environment } from '../../../../environments/environment';
+import { USE_ONE_TIME_PASSWORD_LOGIN } from '@spartacus/user/account/components';
+
@NgModule({
imports: [UserAccountRootModule, UserProfileRootModule],
@@ -71,6 +73,10 @@ import { environment } from '../../../../environments/environment';
provide: USE_MY_ACCOUNT_V2_PASSWORD,
useValue: environment.myAccountV2,
},
+ {
+ provide: USE_ONE_TIME_PASSWORD_LOGIN,
+ useValue: environment.otpUserLogin,
+ },
provideConfig({
i18n: {
resources: userProfileTranslations,
diff --git a/projects/storefrontapp/src/environments/environment.prod.ts b/projects/storefrontapp/src/environments/environment.prod.ts
index 73df861f676..7e6e81a28c2 100644
--- a/projects/storefrontapp/src/environments/environment.prod.ts
+++ b/projects/storefrontapp/src/environments/environment.prod.ts
@@ -22,4 +22,5 @@ export const environment: Environment = {
requestedDeliveryDate: buildProcess.env.CX_REQUESTED_DELIVERY_DATE,
pdfInvoices: buildProcess.env.CX_PDF_INVOICES,
myAccountV2: buildProcess.env.CX_MY_ACCOUNT_V2 ?? false,
+ otpUserLogin: buildProcess.env.CX_OTP_USER_LOGIN ?? false,
};
diff --git a/projects/storefrontapp/src/environments/environment.ts b/projects/storefrontapp/src/environments/environment.ts
index 5ac06fae96e..182485edb5f 100644
--- a/projects/storefrontapp/src/environments/environment.ts
+++ b/projects/storefrontapp/src/environments/environment.ts
@@ -35,4 +35,5 @@ export const environment: Environment = {
requestedDeliveryDate: buildProcess.env.CX_REQUESTED_DELIVERY_DATE ?? false,
pdfInvoices: buildProcess.env.CX_PDF_INVOICES ?? false,
myAccountV2: buildProcess.env.CX_MY_ACCOUNT_V2 ?? false,
+ otpUserLogin: buildProcess.env.CX_OTP_USER_LOGIN ?? false,
};
diff --git a/projects/storefrontapp/src/environments/models/build.process.env.d.ts b/projects/storefrontapp/src/environments/models/build.process.env.d.ts
index 3e52ae56530..6b4cf23c8f6 100644
--- a/projects/storefrontapp/src/environments/models/build.process.env.d.ts
+++ b/projects/storefrontapp/src/environments/models/build.process.env.d.ts
@@ -24,4 +24,5 @@ interface Env {
CX_REQUESTED_DELIVERY_DATE: boolean;
CX_PDF_INVOICES: boolean;
CX_MY_ACCOUNT_V2: boolean;
+ CX_OTP_USER_LOGIN: boolean;
}
diff --git a/projects/storefrontapp/src/environments/models/environment.model.ts b/projects/storefrontapp/src/environments/models/environment.model.ts
index 754f3f7d5c2..a84a39ed7c7 100644
--- a/projects/storefrontapp/src/environments/models/environment.model.ts
+++ b/projects/storefrontapp/src/environments/models/environment.model.ts
@@ -20,4 +20,5 @@ export interface Environment {
requestedDeliveryDate: boolean;
pdfInvoices: boolean;
myAccountV2: boolean;
+ otpUserLogin: boolean;
}
From 7076eeb30cf0564b4421d369565f7c41bf74862f Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Fri, 12 Apr 2024 10:42:17 +0800
Subject: [PATCH 02/66] CXSPA-6689: add otp login component
---
feature-libs/user/_index.scss | 6 +-
.../assets/translations/en/userAccount.json | 14 ++-
.../assets/translations/translations.ts | 2 +-
.../components/otp-login-form/index.ts | 9 ++
.../otp-login-form-component.service.ts | 85 +++++++++++++++++++
.../otp-login-form.component.html | 59 +++++++++++++
.../otp-login-form.component.ts | 85 +++++++++++++++++++
.../otp-login-form/otp-login-form.module.ts | 62 ++++++++++++++
.../user-account-component.module.ts | 3 +
.../account/root/user-account-root.module.ts | 1 +
feature-libs/user/account/styles/_index.scss | 1 +
.../user/account/styles/_otp-login-form.scss | 45 ++++++++++
.../removed-public-api-deprecation.ts | 3 +-
.../4_0/rename-symbol/rename-symbol.ts | 7 ++
projects/schematics/src/shared/constants.ts | 1 +
15 files changed, 377 insertions(+), 6 deletions(-)
create mode 100644 feature-libs/user/account/components/otp-login-form/index.ts
create mode 100644 feature-libs/user/account/components/otp-login-form/otp-login-form-component.service.ts
create mode 100644 feature-libs/user/account/components/otp-login-form/otp-login-form.component.html
create mode 100644 feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
create mode 100644 feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts
create mode 100644 feature-libs/user/account/styles/_otp-login-form.scss
diff --git a/feature-libs/user/_index.scss b/feature-libs/user/_index.scss
index e86984fedb2..8366c1fa8f1 100644
--- a/feature-libs/user/_index.scss
+++ b/feature-libs/user/_index.scss
@@ -6,9 +6,9 @@
$skipComponentStyles: () !default;
$selectors: cx-address-book, cx-address-form, cx-suggested-addresses-dialog,
- cx-login, cx-login-form, cx-register, cx-reset-password, cx-close-account,
- cx-close-account-modal, cx-my-account-v2-profile, cx-my-account-v2-email,
- cx-my-account-v2-password !default;
+ cx-login, cx-login-form, cx-otp-login-form, cx-register, cx-reset-password,
+ cx-close-account, cx-close-account-modal, cx-my-account-v2-profile,
+ cx-my-account-v2-email, cx-my-account-v2-password !default;
@each $selector in $selectors {
#{$selector} {
diff --git a/feature-libs/user/account/assets/translations/en/userAccount.json b/feature-libs/user/account/assets/translations/en/userAccount.json
index 9e9e12627ea..1505afa95db 100644
--- a/feature-libs/user/account/assets/translations/en/userAccount.json
+++ b/feature-libs/user/account/assets/translations/en/userAccount.json
@@ -15,6 +15,18 @@
},
"wrongEmailFormat": "This is not a valid email format."
},
+ "otpLoginForm": {
+ "sentOTP": "Verification code has been sent to {{target}}. Please enter the code.",
+ "sendRateLime": "in {{waitTime}}s",
+ "resend": "Resend",
+ "verificationCode": {
+ "label": "Verification Code",
+ "placeholder": "Enter Verification Code"
+ },
+ "noReceiveCode": "Didn't receive the code?",
+ "verify": "Verify",
+ "back": "Back"
+ },
"miniLogin": {
"userGreeting": "Hi, {{name}}",
"signInRegister": "Sign In / Register"
@@ -22,4 +34,4 @@
"myAccountV2User": {
"signOut": "Sign Out"
}
-}
+}
\ No newline at end of file
diff --git a/feature-libs/user/account/assets/translations/translations.ts b/feature-libs/user/account/assets/translations/translations.ts
index fecdc65fe92..df9cd470cd3 100644
--- a/feature-libs/user/account/assets/translations/translations.ts
+++ b/feature-libs/user/account/assets/translations/translations.ts
@@ -12,5 +12,5 @@ export const userAccountTranslations: TranslationResources = {
};
export const userAccountTranslationChunksConfig: TranslationChunksConfig = {
- userAccount: ['loginForm', 'miniLogin', 'myAccountV2User'],
+ userAccount: ['loginForm', 'otpLoginForm', 'miniLogin', 'myAccountV2User'],
};
diff --git a/feature-libs/user/account/components/otp-login-form/index.ts b/feature-libs/user/account/components/otp-login-form/index.ts
new file mode 100644
index 00000000000..597303f60b4
--- /dev/null
+++ b/feature-libs/user/account/components/otp-login-form/index.ts
@@ -0,0 +1,9 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './login-form.component';
+export * from './login-form.module';
+export * from './otp-login-form-component.service';
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form-component.service.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form-component.service.ts
new file mode 100644
index 00000000000..947017865ad
--- /dev/null
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form-component.service.ts
@@ -0,0 +1,85 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable } from '@angular/core';
+import {
+ UntypedFormControl,
+ UntypedFormGroup,
+ Validators,
+} from '@angular/forms';
+import {
+ AuthService,
+ GlobalMessageService,
+ GlobalMessageType,
+ WindowRef,
+} from '@spartacus/core';
+import { BehaviorSubject, from } from 'rxjs';
+import { tap, withLatestFrom } from 'rxjs/operators';
+
+@Injectable()
+export class OTPLoginFormComponentService {
+ constructor(
+ protected auth: AuthService,
+ protected globalMessage: GlobalMessageService,
+ protected winRef: WindowRef
+ ) {}
+
+ protected busy$ = new BehaviorSubject(false);
+
+ isUpdating$ = this.busy$.pipe(
+ tap((state) => {
+ state === true ? this.form.disable() : this.form.enable();
+ })
+ );
+
+ form: UntypedFormGroup = new UntypedFormGroup({
+ tokenId: new UntypedFormControl('', [Validators.required]),
+ tokenCode: new UntypedFormControl('', Validators.required),
+ });
+
+ login() {
+ if (!this.form.valid) {
+ this.form.markAllAsTouched();
+ return;
+ }
+ debugger;
+ this.busy$.next(true);
+
+ from(
+ this.auth.loginWithCredentials(
+ this.form.value.tokenId,
+ this.form.value.tokenCode
+ )
+ )
+ .pipe(
+ withLatestFrom(this.auth.isUserLoggedIn()),
+ tap(([_, isLoggedIn]) => this.onSuccess(isLoggedIn))
+ )
+ .subscribe();
+ }
+
+ displayMessage(target: string) {
+ this.globalMessage.add(
+ {
+ key: 'otpLoginForm.sentOTP',
+ params: { target },
+ },
+ GlobalMessageType.MSG_TYPE_CONFIRMATION,
+ 10000
+ );
+ }
+
+ protected onSuccess(isLoggedIn: boolean): void {
+ if (isLoggedIn) {
+ // We want to remove error messages on successful login (primary the bad
+ // username/password combination)
+ this.globalMessage.remove(GlobalMessageType.MSG_TYPE_ERROR);
+ this.form.reset();
+ }
+
+ this.busy$.next(false);
+ }
+}
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.html b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.html
new file mode 100644
index 00000000000..44174935b08
--- /dev/null
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.html
@@ -0,0 +1,59 @@
+
+
+
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
new file mode 100644
index 00000000000..5f6750d4eea
--- /dev/null
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
@@ -0,0 +1,85 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ HostBinding,
+} from '@angular/core';
+import { UntypedFormGroup } from '@angular/forms';
+import { ActivatedRoute } from '@angular/router';
+import { Observable } from 'rxjs';
+import { OTPLoginFormComponentService } from './otp-login-form-component.service';
+
+@Component({
+ selector: 'cx-otp-login-form',
+ templateUrl: './otp-login-form.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class OTPLoginFormComponent {
+ constructor(
+ protected service: OTPLoginFormComponentService,
+ private route: ActivatedRoute,
+ private cdr: ChangeDetectorRef
+ ) {}
+
+ form: UntypedFormGroup = this.service.form;
+ isUpdating$: Observable = this.service.isUpdating$;
+
+ @HostBinding('class.user-form') style = true;
+
+ tokenId: string;
+
+ tokenCode: string;
+
+ target: string;
+
+ password: string;
+
+ waitTime: int = 60;
+
+ isResendDisabled: boolean = true;
+
+ ngOnInit() {
+ this.route.params.subscribe((params) => {
+ this.tokenId = '';
+ this.password = params['password'];
+ this.target = params['targetEmail'];
+ });
+ this.tokenId = '';
+ this.setWaitTime();
+ this.service.displayMessage(this.target);
+ }
+
+ onSubmit(): void {
+ debugger;
+ this.service.login();
+ }
+
+ resendOTP(): void {
+ this.isResendDisabled = true;
+ this.waitTime = 60;
+ this.service.displayMessage(this.target);
+ this.setWaitTime();
+ }
+
+ setWaitTime(): void {
+ let interval = setInterval(() => {
+ this.waitTime--;
+ this.cdr.detectChanges();
+ if (this.waitTime <= 0) {
+ clearInterval(interval);
+ this.isResendDisabled = false;
+ this.cdr.detectChanges();
+ }
+ }, 1000);
+ }
+
+ openInfoDailog(): void {
+ throw new Error('Method not implemented.');
+ }
+}
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts
new file mode 100644
index 00000000000..edc5dfc7460
--- /dev/null
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts
@@ -0,0 +1,62 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { RouterModule } from '@angular/router';
+import {
+ AuthService,
+ CmsConfig,
+ FeaturesConfigModule,
+ GlobalMessageService,
+ I18nModule,
+ NotAuthGuard,
+ UrlModule,
+ WindowRef,
+ provideDefaultConfig,
+} from '@spartacus/core';
+import {
+ FormErrorsModule,
+ PasswordVisibilityToggleModule,
+ SpinnerModule,
+} from '@spartacus/storefront';
+import { OTPLoginFormComponentService } from './otp-login-form-component.service';
+import { OTPLoginFormComponent } from './otp-login-form.component';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ RouterModule,
+ UrlModule,
+ I18nModule,
+ FormErrorsModule,
+ SpinnerModule,
+ PasswordVisibilityToggleModule,
+ FeaturesConfigModule,
+ ],
+ providers: [
+ provideDefaultConfig({
+ cmsComponents: {
+ ReturningCustomerOTPLoginComponent: {
+ component: OTPLoginFormComponent,
+ guards: [NotAuthGuard],
+ providers: [
+ {
+ provide: OTPLoginFormComponentService,
+ useClass: OTPLoginFormComponentService,
+ deps: [AuthService, GlobalMessageService, WindowRef],
+ },
+ ],
+ },
+ },
+ }),
+ ],
+ declarations: [OTPLoginFormComponent],
+})
+export class OTPLoginFormModule {}
diff --git a/feature-libs/user/account/components/user-account-component.module.ts b/feature-libs/user/account/components/user-account-component.module.ts
index 660502c6d38..d91f292f68f 100644
--- a/feature-libs/user/account/components/user-account-component.module.ts
+++ b/feature-libs/user/account/components/user-account-component.module.ts
@@ -6,6 +6,8 @@
import { NgModule } from '@angular/core';
import { LoginFormModule } from './login-form/login-form.module';
+import { OTPLoginFormModule } from './otp-login-form/otp-login-form.module';
+
import { LoginRegisterModule } from './login-register/login-register.module';
import { LoginModule } from './login/login.module';
import { MyAccountV2UserModule } from './my-account-v2-user';
@@ -14,6 +16,7 @@ import { MyAccountV2UserModule } from './my-account-v2-user';
imports: [
LoginModule,
LoginFormModule,
+ OTPLoginFormModule,
LoginRegisterModule,
MyAccountV2UserModule,
],
diff --git a/feature-libs/user/account/root/user-account-root.module.ts b/feature-libs/user/account/root/user-account-root.module.ts
index f8b8c7f2d1f..b453d0c6f3a 100644
--- a/feature-libs/user/account/root/user-account-root.module.ts
+++ b/feature-libs/user/account/root/user-account-root.module.ts
@@ -20,6 +20,7 @@ export function defaultUserAccountComponentsConfig(): CmsConfig {
cmsComponents: [
'LoginComponent',
'ReturningCustomerLoginComponent',
+ 'ReturningCustomerOTPLoginComponent',
'ReturningCustomerRegisterComponent',
'MyAccountViewUserComponent',
],
diff --git a/feature-libs/user/account/styles/_index.scss b/feature-libs/user/account/styles/_index.scss
index 0dff952259b..9ec88ff8fd0 100644
--- a/feature-libs/user/account/styles/_index.scss
+++ b/feature-libs/user/account/styles/_index.scss
@@ -1,3 +1,4 @@
@import './login';
@import './login-form';
+@import './otp-login-form';
@import './my-account-v2-user';
diff --git a/feature-libs/user/account/styles/_otp-login-form.scss b/feature-libs/user/account/styles/_otp-login-form.scss
new file mode 100644
index 00000000000..2e6814bd771
--- /dev/null
+++ b/feature-libs/user/account/styles/_otp-login-form.scss
@@ -0,0 +1,45 @@
+%cx-otp-login-form {
+ &.user-form {
+ .resend-link-text {
+ width: 100%;
+ margin: auto;
+
+ .left-text {
+ padding: 0;
+ text-align: left;
+ }
+
+ .right-text {
+ padding: 0;
+ text-align: right;
+ }
+
+ a.disabled-link {
+ pointer-events: none;
+ color: #a7cce8;
+ text-decoration: none;
+ }
+
+ a {
+ font-size: 16px;
+ font-weight: 400;
+ color: #1672b7;
+ white-space: nowrap;
+ text-decoration: none;
+ }
+ }
+
+ .verify-container {
+ width: 100%;
+ margin-top: 40px;
+ }
+
+ cx-spinner {
+ display: none;
+ }
+
+ button {
+ flex: 100%;
+ }
+ }
+}
diff --git a/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts b/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts
index 63a607f8c3d..fdfababa283 100644
--- a/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts
+++ b/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts
@@ -47,6 +47,7 @@ import {
OCC_USER_ACCOUNT_ADAPTER,
OCC_USER_ADAPTER,
OCC_USER_PROFILE_ADAPTER,
+ OTP_LOGIN_FORM_MODULE,
PAGE_EVENT_BUILDER,
PAGE_EVENT_MODULE,
PRODUCT_VARIANTS_MODULE,
@@ -369,7 +370,7 @@ export const REMOVED_PUBLIC_API_DATA: DeprecatedNode[] = [
{
node: USER_COMPONENT_MODULE,
importPath: SPARTACUS_STOREFRONTLIB,
- comment: `'${USER_COMPONENT_MODULE}' - Following module imports '${LOGIN_MODULE}', '${LOGIN_FORM_MODULE}', '${LOGIN_REGISTER_MODULE}', '${REGISTER_COMPONENT_MODULE}' were removed. Those modules are now part of ${SPARTACUS_USER}.`,
+ comment: `'${USER_COMPONENT_MODULE}' - Following module imports '${LOGIN_MODULE}', '${LOGIN_FORM_MODULE}','${OTP_LOGIN_FORM_MODULE}', '${LOGIN_REGISTER_MODULE}', '${REGISTER_COMPONENT_MODULE}' were removed. Those modules are now part of ${SPARTACUS_USER}.`,
},
// projects/storefrontlib/cms-components/myaccount/close-account/components/close-account-modal/close-account-modal.component.ts
{
diff --git a/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts b/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts
index a0e599ff716..01a1f5a5266 100644
--- a/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts
+++ b/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts
@@ -44,6 +44,7 @@ import {
LOGIN_REGISTER_MODULE,
OCC_ASM_ADAPTER,
ORDER_ENTRY,
+ OTP_LOGIN_FORM_MODULE,
PERMISSION_ROUTING_CONFIG,
PERSONALIZATION_ACTION,
PERSONALIZATION_CONFIG,
@@ -418,6 +419,12 @@ export const RENAMED_SYMBOLS_DATA: RenamedSymbol[] = [
previousImportPath: SPARTACUS_STOREFRONTLIB,
newImportPath: SPARTACUS_USER_ACCOUNT_COMPONENTS,
},
+ // projects/storefrontlib/cms-components/user/otp-login-form/otp-login-form.module.ts
+ {
+ previousNode: OTP_LOGIN_FORM_MODULE,
+ previousImportPath: SPARTACUS_STOREFRONTLIB,
+ newImportPath: SPARTACUS_USER_ACCOUNT_COMPONENTS,
+ },
// projects/storefrontlib/cms-components/user/login-register/login-register.module.ts
{
previousNode: LOGIN_REGISTER_MODULE,
diff --git a/projects/schematics/src/shared/constants.ts b/projects/schematics/src/shared/constants.ts
index 884ac911024..1007bf0b9d8 100644
--- a/projects/schematics/src/shared/constants.ts
+++ b/projects/schematics/src/shared/constants.ts
@@ -864,6 +864,7 @@ export const RESET_PASSWORD_FORM_COMPONENT = 'ResetPasswordFormComponent';
export const LOGIN_COMPONENT = 'LoginComponent';
export const LOGIN_MODULE = 'LoginModule';
export const LOGIN_FORM_MODULE = 'LoginFormModule';
+export const OTP_LOGIN_FORM_MODULE = 'OTPLoginFormModule';
export const LOGIN_REGISTER_COMPONENT = 'LoginRegisterComponent';
export const LOGIN_REGISTER_MODULE = 'LoginRegisterModule';
From 1d99f57c59a48ee74425ea80107c6d2027386085 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Fri, 12 Apr 2024 14:05:54 +0800
Subject: [PATCH 03/66] CXSPA-6689: code refined
---
feature-libs/user/account/components/login-form/index.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/feature-libs/user/account/components/login-form/index.ts b/feature-libs/user/account/components/login-form/index.ts
index 81181c5629d..73b08aa59d2 100644
--- a/feature-libs/user/account/components/login-form/index.ts
+++ b/feature-libs/user/account/components/login-form/index.ts
@@ -7,3 +7,5 @@
export * from './login-form-component.service';
export * from './login-form.component';
export * from './login-form.module';
+export * from './otp-login-form.component';
+export * from './use-otp-login-form';
From 24bff84b792aba5a895ec1ccd82bdfc198401a76 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Fri, 12 Apr 2024 14:51:48 +0800
Subject: [PATCH 04/66] refine code
---
.../user/account/components/login-form/index.ts | 2 ++
.../login-form/otp-login-form.component.ts | 17 ++++++++---------
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/feature-libs/user/account/components/login-form/index.ts b/feature-libs/user/account/components/login-form/index.ts
index 81181c5629d..73b08aa59d2 100644
--- a/feature-libs/user/account/components/login-form/index.ts
+++ b/feature-libs/user/account/components/login-form/index.ts
@@ -7,3 +7,5 @@
export * from './login-form-component.service';
export * from './login-form.component';
export * from './login-form.module';
+export * from './otp-login-form.component';
+export * from './use-otp-login-form';
diff --git a/feature-libs/user/account/components/login-form/otp-login-form.component.ts b/feature-libs/user/account/components/login-form/otp-login-form.component.ts
index b98fb4b6cba..a1ef08faed2 100644
--- a/feature-libs/user/account/components/login-form/otp-login-form.component.ts
+++ b/feature-libs/user/account/components/login-form/otp-login-form.component.ts
@@ -4,18 +4,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import {
- ChangeDetectionStrategy,
- Component,
- HostBinding,
- inject,
-} from '@angular/core';
+import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core';
import {
UntypedFormControl,
UntypedFormGroup,
Validators,
} from '@angular/forms';
-import { WindowRef } from '@spartacus/core';
+import { RoutingService, WindowRef } from '@spartacus/core';
import { CustomFormValidators } from '@spartacus/storefront';
import { BehaviorSubject, Observable, tap } from 'rxjs';
import { VerificationTokenFacade } from '../../root/facade/verification-token.facade';
@@ -28,8 +23,11 @@ import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from './use-otp-login-form';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OneTimePasswordLoginFormComponent {
- protected verificationTokenFacade = inject(VerificationTokenFacade);
- protected winRef = inject(WindowRef);
+ constructor(
+ protected routingService: RoutingService,
+ protected verificationTokenFacade: VerificationTokenFacade,
+ protected winRef: WindowRef
+ ) {}
protected busy$ = new BehaviorSubject(false);
@@ -63,6 +61,7 @@ export class OneTimePasswordLoginFormComponent {
this.verificationTokenFacade.createVerificationToken(
this.collectDataFromLoginForm()
);
+ this.routingService.go({ cxRoute: 'login/otp' });
this.busy$.next(false);
}
From e3f78a62588a67f91fcb25809220c7694e4244bb Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Fri, 12 Apr 2024 16:04:16 +0800
Subject: [PATCH 05/66] CXSPA-6689
---
...erification-token-form-component.service.ts | 11 ++++++++++-
.../verification-token-form.component.ts | 18 ++++++++++--------
.../verification-token-form.module.ts | 2 +-
.../account/root/user-account-root.module.ts | 2 +-
.../routing/default-routing-config.ts | 5 +++++
5 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
index f8bf0c1d657..6b45c39ef65 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
@@ -18,12 +18,14 @@ import {
} from '@spartacus/core';
import { BehaviorSubject, from } from 'rxjs';
import { tap, withLatestFrom } from 'rxjs/operators';
+import { VerificationTokenFacade } from '../../root/facade';
@Injectable()
export class VerificationTokenFormComponentService {
constructor(
protected auth: AuthService,
protected globalMessage: GlobalMessageService,
+ protected verificationTokenFacade: VerificationTokenFacade,
protected winRef: WindowRef
) {}
@@ -45,7 +47,6 @@ export class VerificationTokenFormComponentService {
this.form.markAllAsTouched();
return;
}
- debugger;
this.busy$.next(true);
from(
@@ -72,6 +73,14 @@ export class VerificationTokenFormComponentService {
);
}
+ sentOTP(loginId: string, password: string, purpose: string) {
+ this.verificationTokenFacade.createVerificationToken({
+ loginId,
+ password,
+ purpose,
+ });
+ }
+
protected onSuccess(isLoggedIn: boolean): void {
if (isLoggedIn) {
// We want to remove error messages on successful login (primary the bad
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index 5c6eeda3eda..19a231bb2be 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -40,34 +40,36 @@ export class VerificationTokenFormComponent {
password: string;
+ purpose: string;
+
waitTime: int = 60;
isResendDisabled: boolean = true;
ngOnInit() {
this.route.params.subscribe((params) => {
- this.tokenId = '';
+ this.tokenId = params['tokenId'];
this.password = params['password'];
- this.target = params['targetEmail'];
+ this.target = params['loginId'];
+ this.purpose = params['purpose'];
+ this.service.displayMessage(this.target);
});
- this.tokenId = '';
- this.setWaitTime();
- this.service.displayMessage(this.target);
+ this.startWaitTimeInterval();
}
onSubmit(): void {
- debugger;
this.service.login();
}
resendOTP(): void {
this.isResendDisabled = true;
this.waitTime = 60;
+ this.startWaitTimeInterval();
+ this.service.sentOTP(this.target, this.password, this.purpose);
this.service.displayMessage(this.target);
- this.setWaitTime();
}
- setWaitTime(): void {
+ startWaitTimeInterval(): void {
let interval = setInterval(() => {
this.waitTime--;
this.cdr.detectChanges();
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
index a5d1dbcc0a3..2b5a90a40b2 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
@@ -43,7 +43,7 @@ import { VerificationTokenFormComponent } from './verification-token-form.compon
providers: [
provideDefaultConfig({
cmsComponents: {
- ReturningCustomerOTPLoginComponent: {
+ VerifyOTPTokenComponent: {
component: VerificationTokenFormComponent,
guards: [NotAuthGuard],
providers: [
diff --git a/feature-libs/user/account/root/user-account-root.module.ts b/feature-libs/user/account/root/user-account-root.module.ts
index b453d0c6f3a..ae5983064a5 100644
--- a/feature-libs/user/account/root/user-account-root.module.ts
+++ b/feature-libs/user/account/root/user-account-root.module.ts
@@ -20,7 +20,7 @@ export function defaultUserAccountComponentsConfig(): CmsConfig {
cmsComponents: [
'LoginComponent',
'ReturningCustomerLoginComponent',
- 'ReturningCustomerOTPLoginComponent',
+ 'VerifyOTPTokenComponent',
'ReturningCustomerRegisterComponent',
'MyAccountViewUserComponent',
],
diff --git a/projects/storefrontlib/cms-structure/routing/default-routing-config.ts b/projects/storefrontlib/cms-structure/routing/default-routing-config.ts
index 46dae0f8595..106a381e3e2 100644
--- a/projects/storefrontlib/cms-structure/routing/default-routing-config.ts
+++ b/projects/storefrontlib/cms-structure/routing/default-routing-config.ts
@@ -16,6 +16,11 @@ export const defaultStorefrontRoutesConfig: RoutesConfig = {
protected: false,
authFlow: true,
},
+ verifyToken: {
+ paths: ['/login/verify-token'],
+ protected: false,
+ authFlow: true,
+ },
register: {
paths: ['login/register'],
protected: false,
From 8b4685492faf376f31984d145685cf53c669ba1f Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Fri, 12 Apr 2024 18:43:02 +0800
Subject: [PATCH 06/66] test
---
.../account/root/user-account-root.module.ts | 43 +++++++++++++++++--
1 file changed, 40 insertions(+), 3 deletions(-)
diff --git a/feature-libs/user/account/root/user-account-root.module.ts b/feature-libs/user/account/root/user-account-root.module.ts
index b453d0c6f3a..ce36cd0ef5b 100644
--- a/feature-libs/user/account/root/user-account-root.module.ts
+++ b/feature-libs/user/account/root/user-account-root.module.ts
@@ -5,7 +5,16 @@
*/
import { NgModule } from '@angular/core';
-import { CmsConfig, provideDefaultConfigFactory } from '@spartacus/core';
+import { RouterModule } from '@angular/router';
+import {
+ AuthGuard,
+ CmsConfig,
+ RoutingConfig,
+ provideDefaultConfig,
+ provideDefaultConfigFactory,
+} from '@spartacus/core';
+import { CmsPageGuard } from '@spartacus/storefront';
+import { VerificationTokenFormComponent } from '../components/verification-token-form';
import { UserAccountEventModule } from './events/user-account-event.module';
import {
USER_ACCOUNT_CORE_FEATURE,
@@ -33,7 +42,35 @@ export function defaultUserAccountComponentsConfig(): CmsConfig {
}
@NgModule({
- imports: [UserAccountEventModule],
- providers: [provideDefaultConfigFactory(defaultUserAccountComponentsConfig)],
+ imports: [
+ UserAccountEventModule,
+ RouterModule.forChild([
+ {
+ // @ts-ignore
+ path: null,
+ canActivate: [AuthGuard, CmsPageGuard],
+ component: VerificationTokenFormComponent,
+ data: {
+ cxRoute: 'loginByVerifyToken',
+ // cxContext: {
+ // [ORDER_ENTRIES_CONTEXT]: SavedCartOrderEntriesContextToken,
+ // },
+ },
+ },
+ ]),
+ ],
+ providers: [
+ provideDefaultConfigFactory(defaultUserAccountComponentsConfig),
+ provideDefaultConfig({
+ routing: {
+ routes: {
+ loginByVerifyToken: {
+ paths: ['login/verify-token'],
+ paramsMapping: { savedCartId: 'savedCartId' },
+ },
+ },
+ },
+ }),
+ ],
})
export class UserAccountRootModule {}
From 780f91228bec5e41d1370edaeb7565b7b1bf424d Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Mon, 15 Apr 2024 16:07:44 +0800
Subject: [PATCH 07/66] add route
---
.../login-form/otp-login-form.component.ts | 59 ++++++++++++++---
.../account/root/user-account-root.module.ts | 63 +++++++++----------
2 files changed, 80 insertions(+), 42 deletions(-)
diff --git a/feature-libs/user/account/components/login-form/otp-login-form.component.ts b/feature-libs/user/account/components/login-form/otp-login-form.component.ts
index 01ace3a7d4b..2d2ea34ab57 100644
--- a/feature-libs/user/account/components/login-form/otp-login-form.component.ts
+++ b/feature-libs/user/account/components/login-form/otp-login-form.component.ts
@@ -10,11 +10,18 @@ import {
UntypedFormGroup,
Validators,
} from '@angular/forms';
-import { RoutingService, WindowRef } from '@spartacus/core';
+import {
+ GlobalMessageService,
+ GlobalMessageType,
+ HttpErrorModel,
+ RoutingService,
+ TranslationService,
+ WindowRef,
+} from '@spartacus/core';
import { CustomFormValidators } from '@spartacus/storefront';
import { BehaviorSubject, Observable, tap } from 'rxjs';
import { VerificationTokenFacade } from '../../root/facade/verification-token.facade';
-import { LoginForm } from '../../root/model';
+import { LoginForm, VerificationToken } from '../../root/model';
import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from './use-otp-login-form';
@Component({
@@ -26,7 +33,9 @@ export class OneTimePasswordLoginFormComponent {
constructor(
protected routingService: RoutingService,
protected verificationTokenFacade: VerificationTokenFacade,
- protected winRef: WindowRef
+ protected winRef: WindowRef,
+ protected translationService: TranslationService,
+ protected globalMessage: GlobalMessageService
) {}
protected busy$ = new BehaviorSubject(false);
@@ -58,14 +67,48 @@ export class OneTimePasswordLoginFormComponent {
}
this.busy$.next(true);
- this.verificationTokenFacade.createVerificationToken(
- this.collectDataFromLoginForm()
- );
- this.routingService.go({ cxRoute: 'login/otp' });
+ const loginForm = this.collectDataFromLoginForm();
+ this.verificationTokenFacade.createVerificationToken(loginForm).subscribe({
+ next: (result: VerificationToken) =>
+ this.goToVerificationTokenForm(result, loginForm),
+ error: (error: HttpErrorModel) =>
+ this.onCreateVerificationTokenFail(error),
+ complete: () => this.busy$.next(false),
+ });
+ }
+
+ protected onCreateVerificationTokenFail(error: HttpErrorModel): void {
this.busy$.next(false);
+ const errorDetails = error.details ?? [];
+ if (errorDetails.length === 0) {
+ this.globalMessage.add(
+ { key: 'httpHandlers.unknownError' },
+ GlobalMessageType.MSG_TYPE_ERROR
+ );
+ }
+ errorDetails.forEach((err) => {
+ this.globalMessage.add(
+ { raw: err.message },
+ GlobalMessageType.MSG_TYPE_ERROR
+ );
+ });
+ }
+
+ protected goToVerificationTokenForm(
+ verificationToken: VerificationToken,
+ loginForm: LoginForm
+ ): void {
+ this.routingService.go({
+ cxRoute: 'verifyToken',
+ params: {
+ loginId: loginForm.loginId,
+ password: loginForm.password,
+ tokenId: verificationToken.tokenId,
+ },
+ });
}
- collectDataFromLoginForm(): LoginForm {
+ protected collectDataFromLoginForm(): LoginForm {
return {
// TODO: consider dropping toLowerCase as this should not be part of the UI,
// as it's too opinionated and doesn't work with other AUTH services
diff --git a/feature-libs/user/account/root/user-account-root.module.ts b/feature-libs/user/account/root/user-account-root.module.ts
index 4be1443024b..ff53708d80d 100644
--- a/feature-libs/user/account/root/user-account-root.module.ts
+++ b/feature-libs/user/account/root/user-account-root.module.ts
@@ -5,16 +5,7 @@
*/
import { NgModule } from '@angular/core';
-import { RouterModule } from '@angular/router';
-import {
- AuthGuard,
- CmsConfig,
- RoutingConfig,
- provideDefaultConfig,
- provideDefaultConfigFactory,
-} from '@spartacus/core';
-import { CmsPageGuard } from '@spartacus/storefront';
-import { VerificationTokenFormComponent } from '../components/verification-token-form';
+import { CmsConfig, provideDefaultConfigFactory } from '@spartacus/core';
import { UserAccountEventModule } from './events/user-account-event.module';
import {
USER_ACCOUNT_CORE_FEATURE,
@@ -44,33 +35,37 @@ export function defaultUserAccountComponentsConfig(): CmsConfig {
@NgModule({
imports: [
UserAccountEventModule,
- RouterModule.forChild([
- {
- // @ts-ignore
- path: null,
- canActivate: [AuthGuard, CmsPageGuard],
- component: VerificationTokenFormComponent,
- data: {
- cxRoute: 'loginByVerifyToken',
- // cxContext: {
- // [ORDER_ENTRIES_CONTEXT]: SavedCartOrderEntriesContextToken,
- // },
- },
- },
- ]),
+ // RouterModule.forChild([
+ // {
+ // // @ts-ignore
+ // path: null,
+ // canActivate: [AuthGuard, CmsPageGuard],
+ // component: PageLayoutComponent,
+ // data: {
+ // cxRoute: 'loginByVerifyToken',
+ // // cxContext: {
+ // // [ORDER_ENTRIES_CONTEXT]: SavedCartOrderEntriesContextToken,
+ // // },
+ // },
+ // },
+ // ]),
],
providers: [
provideDefaultConfigFactory(defaultUserAccountComponentsConfig),
- provideDefaultConfig({
- routing: {
- routes: {
- loginByVerifyToken: {
- paths: ['login/verify-token'],
- paramsMapping: { savedCartId: 'savedCartId' },
- },
- },
- },
- }),
+ // provideDefaultConfig({
+ // routing: {
+ // routes: {
+ // loginByVerifyToken: {
+ // paths: ['login/verify-token'],
+ // paramsMapping: {
+ // loginId: 'loginId',
+ // password: 'password',
+ // tokenId: 'tokenId',
+ // },
+ // },
+ // },
+ // },
+ // }),
],
})
export class UserAccountRootModule {}
From e27548007e5d836067342a1c5256d577c871d9d3 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 16 Apr 2024 18:08:28 +0800
Subject: [PATCH 08/66] CXSPA-6689: add dialog for otp token
---
feature-libs/user/_index.scss | 8 +--
.../assets/translations/en/userAccount.json | 8 +++
.../assets/translations/translations.ts | 1 +
...efault-verification-token-layout.config.ts | 22 +++++++
.../verification-token-dialog.component.html | 52 ++++++++++++++++
.../verification-token-dialog.component.ts | 35 +++++++++++
...rification-token-form-component.service.ts | 2 +-
.../verification-token-form.component.html | 2 +-
.../verification-token-form.component.ts | 13 +++-
.../verification-token-form.module.ts | 14 ++++-
.../root/model/augmented-core.model.ts | 13 ++++
feature-libs/user/account/root/model/index.ts | 1 +
feature-libs/user/account/styles/_index.scss | 1 +
.../styles/_verification-token-dialog.scss | 62 +++++++++++++++++++
.../styles/_verification-token-form.scss | 4 +-
15 files changed, 225 insertions(+), 13 deletions(-)
create mode 100644 feature-libs/user/account/components/verification-token-form/default-verification-token-layout.config.ts
create mode 100644 feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html
create mode 100644 feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts
create mode 100644 feature-libs/user/account/root/model/augmented-core.model.ts
create mode 100644 feature-libs/user/account/styles/_verification-token-dialog.scss
diff --git a/feature-libs/user/_index.scss b/feature-libs/user/_index.scss
index fea387be8e1..9a7f20ff777 100644
--- a/feature-libs/user/_index.scss
+++ b/feature-libs/user/_index.scss
@@ -6,10 +6,10 @@
$skipComponentStyles: () !default;
$selectors: cx-address-book, cx-address-form, cx-suggested-addresses-dialog,
- cx-login, cx-login-form, cx-verification-token-form, cx-register,
- cx-reset-password, cx-close-account, cx-close-account-modal,
- cx-my-account-v2-profile, cx-my-account-v2-email, cx-my-account-v2-password,
- cx-verification-token-form !default;
+ cx-login, cx-login-form, cx-register, cx-reset-password, cx-close-account,
+ cx-close-account-modal, cx-my-account-v2-profile, cx-my-account-v2-email,
+ cx-my-account-v2-password, cx-verification-token-form,
+ cx-verification-token-dialog !default;
@each $selector in $selectors {
#{$selector} {
diff --git a/feature-libs/user/account/assets/translations/en/userAccount.json b/feature-libs/user/account/assets/translations/en/userAccount.json
index efb995ba868..e109c12a4c0 100644
--- a/feature-libs/user/account/assets/translations/en/userAccount.json
+++ b/feature-libs/user/account/assets/translations/en/userAccount.json
@@ -27,6 +27,14 @@
"verify": "Verify",
"back": "Back"
},
+ "verificationTokenDialog": {
+ "title": "Don't receive the code",
+ "noReceiveCode": "If you have not received the code, please try:",
+ "contentLine1": "1. Wait for a while as the email may be delayed.",
+ "contentLine2": "2. Check if it is in the junk mail.",
+ "contentLine3": "3. Go back and make sure your email address and password is correct.",
+ "ok": "ok"
+ },
"miniLogin": {
"userGreeting": "Hi, {{name}}",
"signInRegister": "Sign In / Register"
diff --git a/feature-libs/user/account/assets/translations/translations.ts b/feature-libs/user/account/assets/translations/translations.ts
index 7afe2651019..a4c1ca04cd6 100644
--- a/feature-libs/user/account/assets/translations/translations.ts
+++ b/feature-libs/user/account/assets/translations/translations.ts
@@ -15,6 +15,7 @@ export const userAccountTranslationChunksConfig: TranslationChunksConfig = {
userAccount: [
'loginForm',
'verificationTokenForm',
+ 'verificationTokenDialog',
'miniLogin',
'myAccountV2User',
],
diff --git a/feature-libs/user/account/components/verification-token-form/default-verification-token-layout.config.ts b/feature-libs/user/account/components/verification-token-form/default-verification-token-layout.config.ts
new file mode 100644
index 00000000000..9c6079c35ff
--- /dev/null
+++ b/feature-libs/user/account/components/verification-token-form/default-verification-token-layout.config.ts
@@ -0,0 +1,22 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ DIALOG_TYPE,
+ LAUNCH_CALLER,
+ LayoutConfig,
+} from '@spartacus/storefront';
+import { VerificationTokenDialogComponent } from './verification-token-dialog.component';
+
+export const defaultVerificationTokenLayoutConfig: LayoutConfig = {
+ launch: {
+ [LAUNCH_CALLER.ACCOUNT_VERIFICATION_TOKEN]: {
+ inlineRoot: true,
+ component: VerificationTokenDialogComponent,
+ dialogType: DIALOG_TYPE.DIALOG,
+ },
+ },
+};
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html
new file mode 100644
index 00000000000..6888e267c40
--- /dev/null
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'verificationTokenDialog.noReceiveCode' | cxTranslate }}
+
+
+ {{ 'verificationTokenDialog.contentLine1' | cxTranslate }}
+
+
+ {{ 'verificationTokenDialog.contentLine2' | cxTranslate }}
+
+
+ {{ 'verificationTokenDialog.contentLine3' | cxTranslate }}
+
+
+
+
+
+
+
+
+
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts
new file mode 100644
index 00000000000..55b98f67545
--- /dev/null
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts
@@ -0,0 +1,35 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Component, OnInit } from '@angular/core';
+import { FocusConfig, LaunchDialogService } from '@spartacus/storefront';
+
+export enum VERIFICATION_TOKEN_DIALOG_ACTION {
+ OK = 'OK',
+}
+
+@Component({
+ selector: 'cx-verification-token-dialog',
+ templateUrl: './verification-token-dialog.component.html',
+})
+export class VerificationTokenDialogComponent implements OnInit {
+ VERIFICATION_TOKEN_DIALOG_ACTION = VERIFICATION_TOKEN_DIALOG_ACTION;
+
+ focusConfig: FocusConfig = {
+ trap: true,
+ block: true,
+ autofocus: true,
+ focusOnEscape: true,
+ };
+
+ constructor(protected launchDialogService: LaunchDialogService) {}
+
+ ngOnInit(): void {}
+
+ closeModal(reason: VERIFICATION_TOKEN_DIALOG_ACTION): void {
+ this.launchDialogService.closeDialog(reason);
+ }
+}
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
index 6b45c39ef65..c8d76d8cbfe 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
@@ -39,7 +39,7 @@ export class VerificationTokenFormComponentService {
form: UntypedFormGroup = new UntypedFormGroup({
tokenId: new UntypedFormControl('', [Validators.required]),
- tokenCode: new UntypedFormControl('', Validators.required),
+ tokenCode: new UntypedFormControl('', [Validators.required]),
});
login() {
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
index 18341164fdf..080e1a5723f 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
@@ -37,7 +37,7 @@
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index 19a231bb2be..3c40d60cc17 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -8,10 +8,13 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
+ ElementRef,
HostBinding,
+ ViewChild,
} from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
+import { LAUNCH_CALLER, LaunchDialogService } from '@spartacus/storefront';
import { Observable } from 'rxjs';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
@@ -23,6 +26,7 @@ import { VerificationTokenFormComponentService } from './verification-token-form
export class VerificationTokenFormComponent {
constructor(
protected service: VerificationTokenFormComponentService,
+ protected launchDialogService: LaunchDialogService,
private route: ActivatedRoute,
private cdr: ChangeDetectorRef
) {}
@@ -32,6 +36,8 @@ export class VerificationTokenFormComponent {
@HostBinding('class.user-form') style = true;
+ @ViewChild('noReceiveCodeLink') element: ElementRef;
+
tokenId: string;
tokenCode: string;
@@ -48,7 +54,7 @@ export class VerificationTokenFormComponent {
ngOnInit() {
this.route.params.subscribe((params) => {
- this.tokenId = params['tokenId'];
+ this.tokenId = 'test';
this.password = params['password'];
this.target = params['loginId'];
this.purpose = params['purpose'];
@@ -82,6 +88,9 @@ export class VerificationTokenFormComponent {
}
openInfoDailog(): void {
- throw new Error('Method not implemented.');
+ this.launchDialogService.openDialogAndSubscribe(
+ LAUNCH_CALLER.ACCOUNT_VERIFICATION_TOKEN,
+ this.element
+ );
}
}
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
index 2b5a90a40b2..f97053944a5 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
@@ -21,9 +21,12 @@ import {
} from '@spartacus/core';
import {
FormErrorsModule,
- PasswordVisibilityToggleModule,
+ IconModule,
+ KeyboardFocusModule,
SpinnerModule,
} from '@spartacus/storefront';
+import { defaultVerificationTokenLayoutConfig } from './default-verification-token-layout.config';
+import { VerificationTokenDialogComponent } from './verification-token-dialog.component';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
import { VerificationTokenFormComponent } from './verification-token-form.component';
@@ -31,13 +34,14 @@ import { VerificationTokenFormComponent } from './verification-token-form.compon
imports: [
CommonModule,
FormsModule,
+ KeyboardFocusModule,
ReactiveFormsModule,
RouterModule,
UrlModule,
+ IconModule,
I18nModule,
FormErrorsModule,
SpinnerModule,
- PasswordVisibilityToggleModule,
FeaturesConfigModule,
],
providers: [
@@ -56,7 +60,11 @@ import { VerificationTokenFormComponent } from './verification-token-form.compon
},
},
}),
+ provideDefaultConfig(defaultVerificationTokenLayoutConfig),
+ ],
+ declarations: [
+ VerificationTokenFormComponent,
+ VerificationTokenDialogComponent,
],
- declarations: [VerificationTokenFormComponent],
})
export class VerificationTokenFormModule {}
diff --git a/feature-libs/user/account/root/model/augmented-core.model.ts b/feature-libs/user/account/root/model/augmented-core.model.ts
new file mode 100644
index 00000000000..986f7b167b9
--- /dev/null
+++ b/feature-libs/user/account/root/model/augmented-core.model.ts
@@ -0,0 +1,13 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import '@spartacus/storefront';
+
+declare module '@spartacus/storefront' {
+ const enum LAUNCH_CALLER {
+ ACCOUNT_VERIFICATION_TOKEN = 'ACCOUNT_VERIFICATION_TOKEN',
+ }
+}
diff --git a/feature-libs/user/account/root/model/index.ts b/feature-libs/user/account/root/model/index.ts
index 2fad56d332a..5b89674523c 100644
--- a/feature-libs/user/account/root/model/index.ts
+++ b/feature-libs/user/account/root/model/index.ts
@@ -4,4 +4,5 @@
* SPDX-License-Identifier: Apache-2.0
*/
+export * from './augmented-core.model';
export * from './user.model';
diff --git a/feature-libs/user/account/styles/_index.scss b/feature-libs/user/account/styles/_index.scss
index 7397482c473..6adb0b7c7a6 100644
--- a/feature-libs/user/account/styles/_index.scss
+++ b/feature-libs/user/account/styles/_index.scss
@@ -4,3 +4,4 @@
@import './my-account-v2-user';
@import './otp-login-form';
@import './verification-token-form';
+@import './verification-token-dialog';
diff --git a/feature-libs/user/account/styles/_verification-token-dialog.scss b/feature-libs/user/account/styles/_verification-token-dialog.scss
new file mode 100644
index 00000000000..1f2dd70295d
--- /dev/null
+++ b/feature-libs/user/account/styles/_verification-token-dialog.scss
@@ -0,0 +1,62 @@
+%cx-verification-token-dialog {
+ .cx-modal-content {
+ max-width: 32rem;
+ margin-inline-start: auto;
+ margin-inline-end: auto;
+ }
+
+ .cx-dialog-header {
+ padding-top: 0.9rem;
+ padding-inline-end: 1rem;
+ padding-bottom: 0.9rem;
+ padding-inline-start: 1rem;
+ border-width: 0;
+ display: flex;
+
+ box-shadow: 0px 0px 4px rgba(85, 107, 130, 0.16), inset 0px -1px 0px #d9d9d9;
+
+ .info-icon {
+ cx-icon {
+ font-size: 1.4rem;
+ color: #0084fe;
+ }
+ }
+
+ .title {
+ font-size: 1rem;
+ font-weight: 600;
+ }
+ }
+
+ .cx-dialog-body {
+ padding: 1rem;
+
+ .cx-dialog-row {
+ margin: 0;
+ display: flex;
+ padding-top: 0;
+ padding-inline-end: 0.875rem;
+ padding-bottom: 0.85rem;
+ padding-inline-start: 2.875rem;
+
+ max-width: 100%;
+ flex-wrap: wrap;
+
+ @include media-breakpoint-down(sm) {
+ flex-direction: column;
+ padding: 0;
+ }
+ }
+
+ .cx-dialog-item {
+ padding: 0.2rem;
+ }
+ }
+
+ .cx-dialog-footer {
+ padding-top: 0;
+ padding-inline-end: 1rem;
+ padding-bottom: 0;
+ padding-inline-start: 1rem;
+ }
+}
diff --git a/feature-libs/user/account/styles/_verification-token-form.scss b/feature-libs/user/account/styles/_verification-token-form.scss
index 43dc111e0e3..294067da99d 100644
--- a/feature-libs/user/account/styles/_verification-token-form.scss
+++ b/feature-libs/user/account/styles/_verification-token-form.scss
@@ -21,7 +21,7 @@
}
a {
- font-size: 16px;
+ font-size: 1rem;
font-weight: 400;
color: #1672b7;
white-space: nowrap;
@@ -31,7 +31,7 @@
.verify-container {
width: 100%;
- margin-top: 40px;
+ margin-top: 2.5rem;
}
cx-spinner {
From b35b343756ddd22bd13ad331ad3f85edfb70e52c Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Wed, 17 Apr 2024 11:04:28 +0800
Subject: [PATCH 09/66] refine code
---
feature-libs/user/_index.scss | 2 +-
.../login-form/otp-login-form.component.ts | 22 ++++++------
.../verification-token-form.component.ts | 20 ++++-------
.../account/root/user-account-root.module.ts | 36 ++-----------------
.../user/account/styles/_otp-login-form.scss | 2 +-
5 files changed, 23 insertions(+), 59 deletions(-)
diff --git a/feature-libs/user/_index.scss b/feature-libs/user/_index.scss
index fea387be8e1..586951f061c 100644
--- a/feature-libs/user/_index.scss
+++ b/feature-libs/user/_index.scss
@@ -9,7 +9,7 @@ $selectors: cx-address-book, cx-address-form, cx-suggested-addresses-dialog,
cx-login, cx-login-form, cx-verification-token-form, cx-register,
cx-reset-password, cx-close-account, cx-close-account-modal,
cx-my-account-v2-profile, cx-my-account-v2-email, cx-my-account-v2-password,
- cx-verification-token-form !default;
+ cx-otp-login-form !default;
@each $selector in $selectors {
#{$selector} {
diff --git a/feature-libs/user/account/components/login-form/otp-login-form.component.ts b/feature-libs/user/account/components/login-form/otp-login-form.component.ts
index 2d2ea34ab57..b07b1a426e2 100644
--- a/feature-libs/user/account/components/login-form/otp-login-form.component.ts
+++ b/feature-libs/user/account/components/login-form/otp-login-form.component.ts
@@ -15,7 +15,6 @@ import {
GlobalMessageType,
HttpErrorModel,
RoutingService,
- TranslationService,
WindowRef,
} from '@spartacus/core';
import { CustomFormValidators } from '@spartacus/storefront';
@@ -25,7 +24,7 @@ import { LoginForm, VerificationToken } from '../../root/model';
import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from './use-otp-login-form';
@Component({
- selector: 'cx-verification-token-form',
+ selector: 'cx-otp-login-form',
templateUrl: './login-form.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
@@ -34,7 +33,6 @@ export class OneTimePasswordLoginFormComponent {
protected routingService: RoutingService,
protected verificationTokenFacade: VerificationTokenFacade,
protected winRef: WindowRef,
- protected translationService: TranslationService,
protected globalMessage: GlobalMessageService
) {}
@@ -98,14 +96,18 @@ export class OneTimePasswordLoginFormComponent {
verificationToken: VerificationToken,
loginForm: LoginForm
): void {
- this.routingService.go({
- cxRoute: 'verifyToken',
- params: {
- loginId: loginForm.loginId,
- password: loginForm.password,
- tokenId: verificationToken.tokenId,
+ this.routingService.go(
+ {
+ cxRoute: 'verifyToken',
},
- });
+ {
+ state: {
+ loginId: loginForm.loginId,
+ password: loginForm.password,
+ tokenId: verificationToken.tokenId,
+ },
+ }
+ );
}
protected collectDataFromLoginForm(): LoginForm {
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index 19a231bb2be..98ccd4a8fe8 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -9,9 +9,9 @@ import {
ChangeDetectorRef,
Component,
HostBinding,
+ OnInit,
} from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
-import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
@@ -20,10 +20,9 @@ import { VerificationTokenFormComponentService } from './verification-token-form
templateUrl: './verification-token-form.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class VerificationTokenFormComponent {
+export class VerificationTokenFormComponent implements OnInit {
constructor(
protected service: VerificationTokenFormComponentService,
- private route: ActivatedRoute,
private cdr: ChangeDetectorRef
) {}
@@ -40,20 +39,15 @@ export class VerificationTokenFormComponent {
password: string;
- purpose: string;
-
waitTime: int = 60;
isResendDisabled: boolean = true;
ngOnInit() {
- this.route.params.subscribe((params) => {
- this.tokenId = params['tokenId'];
- this.password = params['password'];
- this.target = params['loginId'];
- this.purpose = params['purpose'];
- this.service.displayMessage(this.target);
- });
+ this.tokenId = history.state['tokenId'];
+ this.password = history.state['password'];
+ this.target = history.state['loginId'];
+
this.startWaitTimeInterval();
}
@@ -65,7 +59,7 @@ export class VerificationTokenFormComponent {
this.isResendDisabled = true;
this.waitTime = 60;
this.startWaitTimeInterval();
- this.service.sentOTP(this.target, this.password, this.purpose);
+ this.service.sentOTP(this.target, this.password, 'LOGIN');
this.service.displayMessage(this.target);
}
diff --git a/feature-libs/user/account/root/user-account-root.module.ts b/feature-libs/user/account/root/user-account-root.module.ts
index ff53708d80d..ae5983064a5 100644
--- a/feature-libs/user/account/root/user-account-root.module.ts
+++ b/feature-libs/user/account/root/user-account-root.module.ts
@@ -33,39 +33,7 @@ export function defaultUserAccountComponentsConfig(): CmsConfig {
}
@NgModule({
- imports: [
- UserAccountEventModule,
- // RouterModule.forChild([
- // {
- // // @ts-ignore
- // path: null,
- // canActivate: [AuthGuard, CmsPageGuard],
- // component: PageLayoutComponent,
- // data: {
- // cxRoute: 'loginByVerifyToken',
- // // cxContext: {
- // // [ORDER_ENTRIES_CONTEXT]: SavedCartOrderEntriesContextToken,
- // // },
- // },
- // },
- // ]),
- ],
- providers: [
- provideDefaultConfigFactory(defaultUserAccountComponentsConfig),
- // provideDefaultConfig({
- // routing: {
- // routes: {
- // loginByVerifyToken: {
- // paths: ['login/verify-token'],
- // paramsMapping: {
- // loginId: 'loginId',
- // password: 'password',
- // tokenId: 'tokenId',
- // },
- // },
- // },
- // },
- // }),
- ],
+ imports: [UserAccountEventModule],
+ providers: [provideDefaultConfigFactory(defaultUserAccountComponentsConfig)],
})
export class UserAccountRootModule {}
diff --git a/feature-libs/user/account/styles/_otp-login-form.scss b/feature-libs/user/account/styles/_otp-login-form.scss
index c1950da6789..0ed3a2d68bd 100644
--- a/feature-libs/user/account/styles/_otp-login-form.scss
+++ b/feature-libs/user/account/styles/_otp-login-form.scss
@@ -1,4 +1,4 @@
-%cx-login-form {
+%cx-otp-login-form {
&.user-form {
cx-spinner {
display: none;
From 5fc5dac203946d6d72cb3addc0ee6b6d4cf21a9c Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Wed, 17 Apr 2024 14:08:03 +0800
Subject: [PATCH 10/66] CXSPA-6689: add new method for otp login
---
...rification-token-form-component.service.ts | 2 +-
.../src/auth/user-auth/facade/auth.service.ts | 29 +++++++++++++++++--
2 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
index c8d76d8cbfe..7dcb903df3c 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
@@ -50,7 +50,7 @@ export class VerificationTokenFormComponentService {
this.busy$.next(true);
from(
- this.auth.loginWithCredentials(
+ this.auth.otpLoginWithCredentials(
this.form.value.tokenId,
this.form.value.tokenCode
)
diff --git a/projects/core/src/auth/user-auth/facade/auth.service.ts b/projects/core/src/auth/user-auth/facade/auth.service.ts
index 5045bb880e8..fec0089f076 100644
--- a/projects/core/src/auth/user-auth/facade/auth.service.ts
+++ b/projects/core/src/auth/user-auth/facade/auth.service.ts
@@ -31,7 +31,6 @@ export class AuthService {
* Indicates whether the access token is being refreshed
*/
refreshInProgress$: Observable = new BehaviorSubject(false);
-
/**
* Indicates whether the logout is being performed
*/
@@ -89,7 +88,6 @@ export class AuthService {
*/
async loginWithCredentials(userId: string, password: string): Promise {
let uid = userId;
-
if (this.authMultisiteIsolationService) {
uid = await lastValueFrom(
this.authMultisiteIsolationService.decorateUserId(uid)
@@ -111,6 +109,33 @@ export class AuthService {
} catch {}
}
+ /**
+ * Loads a new user token with otp tokenCode and otp tokenId.
+ * @param tokenId
+ * @param tokenCode
+ */
+ async otpLoginWithCredentials(
+ tokenId: string,
+ tokenCode: string
+ ): Promise {
+ debugger;
+ let uid = tokenId;
+
+ try {
+ await this.oAuthLibWrapperService.authorizeWithPasswordFlow(
+ uid,
+ tokenCode
+ );
+
+ // OCC specific user id handling. Customize when implementing different backend
+ this.userIdService.setUserId(OCC_USER_ID_CURRENT);
+
+ this.store.dispatch(new AuthActions.Login());
+
+ this.authRedirectService.redirect();
+ } catch {}
+ }
+
/**
* Revokes tokens and clears state for logged user (tokens, userId).
* To perform logout it is best to use `logout` method. Use this method with caution.
From ab20750b5da976ebc9c844e7da03fa557062d084 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Wed, 17 Apr 2024 14:44:24 +0800
Subject: [PATCH 11/66] CXSPA-6689: bug fixed
---
.../verification-token-form.component.ts | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index 989a10ce7a9..cb0872ffed9 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -10,17 +10,11 @@ import {
Component,
ElementRef,
HostBinding,
-
+ OnInit,
ViewChild,
} from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
-import { ActivatedRoute } from '@angular/router';
import { LAUNCH_CALLER, LaunchDialogService } from '@spartacus/storefront';
-=======
- OnInit,
-} from '@angular/core';
-import { UntypedFormGroup } from '@angular/forms';
->>>>>>> feature/CXSPA-6672
import { Observable } from 'rxjs';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
@@ -33,7 +27,6 @@ export class VerificationTokenFormComponent implements OnInit {
constructor(
protected service: VerificationTokenFormComponentService,
protected launchDialogService: LaunchDialogService,
- private route: ActivatedRoute,
private cdr: ChangeDetectorRef
) {}
@@ -60,7 +53,6 @@ export class VerificationTokenFormComponent implements OnInit {
this.tokenId = history.state['tokenId'];
this.password = history.state['password'];
this.target = history.state['loginId'];
-
this.startWaitTimeInterval();
}
From 4e52f2240dd6b08fb31d96ce148b47f3b471d883 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Wed, 17 Apr 2024 17:27:14 +0800
Subject: [PATCH 12/66] add ut
---
.../otp-login-form.component.spec.ts | 210 ++++++++++++++++++
.../connectors/user-account.connector.spec.ts | 22 ++
.../facade/verification-token.service.spec.ts | 62 ++++++
.../adapters/occ-user-account.adapter.spec.ts | 58 ++++-
4 files changed, 350 insertions(+), 2 deletions(-)
create mode 100644 feature-libs/user/account/components/login-form/otp-login-form.component.spec.ts
create mode 100644 feature-libs/user/account/core/facade/verification-token.service.spec.ts
diff --git a/feature-libs/user/account/components/login-form/otp-login-form.component.spec.ts b/feature-libs/user/account/components/login-form/otp-login-form.component.spec.ts
new file mode 100644
index 00000000000..37026b51f35
--- /dev/null
+++ b/feature-libs/user/account/components/login-form/otp-login-form.component.spec.ts
@@ -0,0 +1,210 @@
+import { DebugElement, Pipe, PipeTransform } from '@angular/core';
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import {
+ ReactiveFormsModule,
+ UntypedFormControl,
+ UntypedFormGroup,
+} from '@angular/forms';
+import { By } from '@angular/platform-browser';
+import { RouterTestingModule } from '@angular/router/testing';
+import {
+ GlobalMessageService,
+ I18nTestingModule,
+ WindowRef,
+} from '@spartacus/core';
+import { FormErrorsModule, SpinnerModule } from '@spartacus/storefront';
+import { BehaviorSubject, of } from 'rxjs';
+import { VerificationTokenService } from '../../core/facade';
+import { VerificationTokenFacade } from '../../root/facade/verification-token.facade';
+import { LoginForm, VerificationToken } from '../../root/model';
+import { OneTimePasswordLoginFormComponent } from './otp-login-form.component';
+import createSpy = jasmine.createSpy;
+
+const isBusySubject = new BehaviorSubject(false);
+
+const formGroup: UntypedFormGroup = new UntypedFormGroup({
+ userId: new UntypedFormControl(),
+ password: new UntypedFormControl(),
+});
+const isUpdating$ = isBusySubject;
+
+const form: LoginForm = {
+ purpose: 'LOGIN',
+ loginId: 'mockEmail',
+ password: '1234',
+};
+
+const verificationToken: VerificationToken = {
+ expiresIn: '300',
+ tokenId: 'mockTokenId',
+};
+
+class MockWinRef {
+ get nativeWindow(): Window {
+ return {} as Window;
+ }
+}
+
+class MockVerificationTokenService
+ implements Partial
+{
+ createVerificationToken = createSpy().and.callFake(() =>
+ of(verificationToken)
+ );
+}
+
+class MockGlobalMessageService {
+ add = createSpy().and.stub();
+ remove = createSpy().and.stub();
+}
+
+@Pipe({
+ name: 'cxUrl',
+})
+class MockUrlPipe implements PipeTransform {
+ transform() {}
+}
+
+describe('OneTimePasswordLoginFormComponent', () => {
+ let component: OneTimePasswordLoginFormComponent;
+ let fixture: ComponentFixture;
+ let el: DebugElement;
+ let service: VerificationTokenFacade;
+ let winRef: WindowRef;
+
+ beforeEach(
+ waitForAsync(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ ReactiveFormsModule,
+ RouterTestingModule,
+ I18nTestingModule,
+ FormErrorsModule,
+ SpinnerModule,
+ ],
+ declarations: [OneTimePasswordLoginFormComponent, MockUrlPipe],
+ providers: [
+ {
+ provide: VerificationTokenFacade,
+ useClass: MockVerificationTokenService,
+ },
+ { provide: WindowRef, useClass: MockWinRef },
+ { provide: GlobalMessageService, useClass: MockGlobalMessageService },
+ ],
+ }).compileComponents();
+ })
+ );
+
+ beforeEach(() => {
+ winRef = TestBed.inject(WindowRef);
+ fixture = TestBed.createComponent(OneTimePasswordLoginFormComponent);
+ service = TestBed.inject(VerificationTokenService);
+ component = fixture.componentInstance;
+ el = fixture.debugElement;
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+
+ describe('login', () => {
+ const userId = 'test@email.com';
+ const password = 'secret';
+
+ it('should not patch user id', () => {
+ isUpdating$.subscribe().unsubscribe();
+ expect(form.value.userId).toEqual('');
+ });
+
+ it('should patch user id', () => {
+ spyOnProperty(winRef, 'nativeWindow', 'get').and.returnValue({
+ history: { state: { newUid: 'test.user@shop.com' } },
+ } as Window);
+ isUpdating$.subscribe().unsubscribe();
+ expect(formGroup.value.userId).toEqual('test.user@shop.com');
+ });
+
+ describe('success', () => {
+ it('should request email', () => {
+ component.onSubmit();
+ expect(service.createVerificationToken).toHaveBeenCalledWith(
+ userId,
+ password
+ );
+ });
+
+ it('should reset the form', () => {
+ spyOn(formGroup, 'reset').and.stub();
+ service.createVerificationToken(form);
+ expect(formGroup.reset).toHaveBeenCalled();
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ formGroup.setValue({
+ userId: 'invalid',
+ password: '123',
+ });
+ });
+
+ it('should not login', () => {
+ component.onSubmit();
+ expect(service.createVerificationToken).not.toHaveBeenCalled();
+ });
+
+ it('should not reset the form', () => {
+ spyOn(formGroup, 'reset').and.stub();
+ component.onSubmit();
+ expect(formGroup.reset).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('busy', () => {
+ it('should disable the submit button when form is disabled', () => {
+ component.form.disable();
+ fixture.detectChanges();
+ const submitBtn: HTMLButtonElement = el.query(
+ By.css('button')
+ ).nativeElement;
+ expect(submitBtn.disabled).toBeTruthy();
+ });
+
+ it('should show the spinner', () => {
+ isBusySubject.next(true);
+ fixture.detectChanges();
+ expect(el.query(By.css('cx-spinner'))).toBeTruthy();
+ });
+ });
+
+ describe('idle', () => {
+ it('should enable the submit button', () => {
+ component.form.enable();
+ fixture.detectChanges();
+ const submitBtn = el.query(By.css('button'));
+ expect(submitBtn.nativeElement.disabled).toBeFalsy();
+ });
+
+ it('should not show the spinner', () => {
+ isBusySubject.next(false);
+ fixture.detectChanges();
+ expect(el.query(By.css('cx-spinner'))).toBeNull();
+ });
+ });
+
+ describe('Form Interactions', () => {
+ it('should call onSubmit() method on submit', () => {
+ const request = spyOn(component, 'onSubmit');
+ const form = el.query(By.css('form'));
+ form.triggerEventHandler('submit', null);
+ expect(request).toHaveBeenCalled();
+ });
+
+ it('should call the service method on submit', () => {
+ component.onSubmit();
+ expect(service.createVerificationToken).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/feature-libs/user/account/core/connectors/user-account.connector.spec.ts b/feature-libs/user/account/core/connectors/user-account.connector.spec.ts
index 43cfb56ab4d..bcfaa0e2e6a 100644
--- a/feature-libs/user/account/core/connectors/user-account.connector.spec.ts
+++ b/feature-libs/user/account/core/connectors/user-account.connector.spec.ts
@@ -1,10 +1,25 @@
import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
+import { LoginForm, VerificationToken } from '../../root/model';
import { UserAccountAdapter } from './user-account.adapter';
import { UserAccountConnector } from './user-account.connector';
import createSpy = jasmine.createSpy;
+const form: LoginForm = {
+ purpose: 'LOGIN',
+ loginId: 'mockEmail',
+ password: '1234',
+};
+
+const verificationToken: VerificationToken = {
+ expiresIn: '300',
+ tokenId: 'mockTokenId',
+};
+
class MockUserAdapter implements UserAccountAdapter {
+ createVerificationToken = createSpy('createVerificationToken').and.callFake(
+ () => of(verificationToken)
+ );
load = createSpy('load').and.callFake((userId) => of(`load-${userId}`));
}
@@ -34,4 +49,11 @@ describe('UserConnector', () => {
expect(result).toEqual('load-user-id');
expect(adapter.load).toHaveBeenCalledWith('user-id');
});
+
+ it('should create a new customer', () => {
+ let result;
+ service.createVerificationToken(form).subscribe((res) => (result = res));
+ expect(result).toEqual(verificationToken);
+ expect(adapter.createVerificationToken).toHaveBeenCalledWith(form);
+ });
});
diff --git a/feature-libs/user/account/core/facade/verification-token.service.spec.ts b/feature-libs/user/account/core/facade/verification-token.service.spec.ts
new file mode 100644
index 00000000000..d1b114b57f2
--- /dev/null
+++ b/feature-libs/user/account/core/facade/verification-token.service.spec.ts
@@ -0,0 +1,62 @@
+import { TestBed } from '@angular/core/testing';
+import { CommandService } from '@spartacus/core';
+import {
+ UserAccountConnector,
+ VerificationTokenService,
+} from '@spartacus/user/account/core';
+import { LoginForm, VerificationToken } from '@spartacus/user/account/root';
+import { of } from 'rxjs';
+import createSpy = jasmine.createSpy;
+
+const form: LoginForm = {
+ purpose: 'LOGIN',
+ loginId: 'mockEmail',
+ password: '1234',
+};
+
+const verificationToken: VerificationToken = {
+ expiresIn: '300',
+ tokenId: 'mockTokenId',
+};
+
+class MockUserAccountConnector implements Partial {
+ createVerificationToken = createSpy().and.callFake(() =>
+ of(verificationToken)
+ );
+}
+
+describe('VerificationTokenService', () => {
+ let service: VerificationTokenService;
+ let connector: UserAccountConnector;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ { provide: UserAccountConnector, useClass: MockUserAccountConnector },
+ CommandService,
+ VerificationTokenService,
+ ],
+ });
+
+ service = TestBed.inject(VerificationTokenService);
+ connector = TestBed.inject(UserAccountConnector);
+ });
+
+ it('should inject VerificationTokenService', () => {
+ expect(service).toBeTruthy();
+ });
+
+ describe('create verification token', () => {
+ it('should create verification token for given email and password', () => {
+ let result: VerificationToken | undefined;
+ service
+ .createVerificationToken(form)
+ .subscribe((data) => {
+ result = data;
+ })
+ .unsubscribe();
+ expect(result).toEqual(verificationToken);
+ expect(connector.createVerificationToken).toHaveBeenCalledWith(form);
+ });
+ });
+});
diff --git a/feature-libs/user/account/occ/adapters/occ-user-account.adapter.spec.ts b/feature-libs/user/account/occ/adapters/occ-user-account.adapter.spec.ts
index 180ed4dd8d8..2cac5c49d93 100644
--- a/feature-libs/user/account/occ/adapters/occ-user-account.adapter.spec.ts
+++ b/feature-libs/user/account/occ/adapters/occ-user-account.adapter.spec.ts
@@ -10,8 +10,15 @@ import {
OccConfig,
OccEndpointsService,
} from '@spartacus/core';
-import { User } from '@spartacus/user/account/root';
-import { USER_ACCOUNT_NORMALIZER } from '@spartacus/user/account/core';
+import {
+ USER_ACCOUNT_NORMALIZER,
+ VERIFICATION_TOKEN_NORMALIZER,
+} from '@spartacus/user/account/core';
+import {
+ LoginForm,
+ User,
+ VerificationToken,
+} from '@spartacus/user/account/root';
import { OccUserAccountAdapter } from './occ-user-account.adapter';
export const mockOccModuleConfig: OccConfig = {
@@ -54,6 +61,17 @@ const user: User = {
displayUid: password,
};
+const form: LoginForm = {
+ purpose: 'LOGIN',
+ loginId: 'mockEmail',
+ password: password,
+};
+
+const verificationToken: VerificationToken = {
+ expiresIn: '300',
+ tokenId: 'mockTokenId',
+};
+
describe('OccUserAccountAdapter', () => {
let occUserAccountAdapter: OccUserAccountAdapter;
let httpMock: HttpTestingController;
@@ -115,4 +133,40 @@ describe('OccUserAccountAdapter', () => {
expect(converter.pipeable).toHaveBeenCalledWith(USER_ACCOUNT_NORMALIZER);
});
});
+
+ describe('create verification token', () => {
+ it('should create verification token for given email and password', () => {
+ occUserAccountAdapter
+ .createVerificationToken(form)
+ .subscribe((result) => {
+ expect(result).toEqual(verificationToken);
+ });
+
+ const mockReq = httpMock.expectOne((req) => {
+ return req.method === 'POST';
+ });
+
+ expect(occEndpointsService.buildUrl).toHaveBeenCalledWith(
+ 'createVerificationToken',
+ {
+ state: { form },
+ }
+ );
+ expect(mockReq.cancelled).toBeFalsy();
+ expect(mockReq.request.responseType).toEqual('json');
+ mockReq.flush(verificationToken);
+ });
+
+ it('should use converter', () => {
+ occUserAccountAdapter.createVerificationToken(form).subscribe();
+ httpMock
+ .expectOne((req) => {
+ return req.method === 'POST';
+ })
+ .flush(verificationToken);
+ expect(converter.pipeable).toHaveBeenCalledWith(
+ VERIFICATION_TOKEN_NORMALIZER
+ );
+ });
+ });
});
From cdb17c54244b71b7eff4b61eeb16a579b8feebd1 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Thu, 18 Apr 2024 11:19:41 +0800
Subject: [PATCH 13/66] add otp cms component
---
.../account/components/login-form/index.ts | 2 -
.../login-form/login-form.module.ts | 21 +------
.../login-form/use-otp-login-form.ts | 14 -----
.../components/otp-login-form/index.ts | 8 +++
.../otp-login-form.component.html | 60 +++++++++++++++++++
.../otp-login-form.component.spec.ts | 0
.../otp-login-form.component.ts | 6 +-
.../otp-login-form/otp-login-form.module.ts | 52 ++++++++++++++++
.../user/account/components/public_api.ts | 4 +-
.../user-account-component.module.ts | 3 +-
.../components/user-account-constants.ts | 7 +++
.../verification-token-form.component.ts | 7 ++-
.../verification-token-form.module.ts | 3 +-
.../account/root/user-account-root.module.ts | 1 +
.../features/user/user-feature.module.ts | 8 +--
15 files changed, 150 insertions(+), 46 deletions(-)
delete mode 100644 feature-libs/user/account/components/login-form/use-otp-login-form.ts
create mode 100644 feature-libs/user/account/components/otp-login-form/index.ts
create mode 100644 feature-libs/user/account/components/otp-login-form/otp-login-form.component.html
rename feature-libs/user/account/components/{login-form => otp-login-form}/otp-login-form.component.spec.ts (100%)
rename feature-libs/user/account/components/{login-form => otp-login-form}/otp-login-form.component.ts (96%)
create mode 100644 feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts
create mode 100644 feature-libs/user/account/components/user-account-constants.ts
diff --git a/feature-libs/user/account/components/login-form/index.ts b/feature-libs/user/account/components/login-form/index.ts
index 73b08aa59d2..81181c5629d 100644
--- a/feature-libs/user/account/components/login-form/index.ts
+++ b/feature-libs/user/account/components/login-form/index.ts
@@ -7,5 +7,3 @@
export * from './login-form-component.service';
export * from './login-form.component';
export * from './login-form.module';
-export * from './otp-login-form.component';
-export * from './use-otp-login-form';
diff --git a/feature-libs/user/account/components/login-form/login-form.module.ts b/feature-libs/user/account/components/login-form/login-form.module.ts
index eff4bbd6892..cc6a01fd67f 100644
--- a/feature-libs/user/account/components/login-form/login-form.module.ts
+++ b/feature-libs/user/account/components/login-form/login-form.module.ts
@@ -5,7 +5,7 @@
*/
import { CommonModule } from '@angular/common';
-import { NgModule, inject } from '@angular/core';
+import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import {
@@ -18,7 +18,6 @@ import {
UrlModule,
WindowRef,
provideDefaultConfig,
- provideDefaultConfigFactory,
} from '@spartacus/core';
import {
FormErrorsModule,
@@ -27,17 +26,6 @@ import {
} from '@spartacus/storefront';
import { LoginFormComponentService } from './login-form-component.service';
import { LoginFormComponent } from './login-form.component';
-import { OneTimePasswordLoginFormComponent } from './otp-login-form.component';
-
-import { USE_ONE_TIME_PASSWORD_LOGIN } from './use-otp-login-form';
-
-const oneTimePasswordLoginFormMapping: CmsConfig = {
- cmsComponents: {
- ReturningCustomerLoginComponent: {
- component: OneTimePasswordLoginFormComponent,
- },
- },
-};
@NgModule({
imports: [
@@ -68,11 +56,8 @@ const oneTimePasswordLoginFormMapping: CmsConfig = {
},
},
}),
- provideDefaultConfigFactory(() =>
- inject(USE_ONE_TIME_PASSWORD_LOGIN) ? oneTimePasswordLoginFormMapping : {}
- ),
],
- declarations: [LoginFormComponent, OneTimePasswordLoginFormComponent],
- exports: [LoginFormComponent, OneTimePasswordLoginFormComponent],
+ declarations: [LoginFormComponent],
+ exports: [LoginFormComponent],
})
export class LoginFormModule {}
diff --git a/feature-libs/user/account/components/login-form/use-otp-login-form.ts b/feature-libs/user/account/components/login-form/use-otp-login-form.ts
deleted file mode 100644
index 3f9e29cf811..00000000000
--- a/feature-libs/user/account/components/login-form/use-otp-login-form.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 SAP Spartacus team
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import { InjectionToken } from '@angular/core';
-
-export const USE_ONE_TIME_PASSWORD_LOGIN = new InjectionToken(
- 'feature flag to enable enhanced UI related pages login',
- { providedIn: 'root', factory: () => false }
-);
-
-export const ONE_TIME_PASSWORD_LOGIN_PURPOSE = 'LOGIN';
diff --git a/feature-libs/user/account/components/otp-login-form/index.ts b/feature-libs/user/account/components/otp-login-form/index.ts
new file mode 100644
index 00000000000..f34c35cd3f4
--- /dev/null
+++ b/feature-libs/user/account/components/otp-login-form/index.ts
@@ -0,0 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './otp-login-form.component';
+export * from './otp-login-form.module';
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.html b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.html
new file mode 100644
index 00000000000..be40dacc4ba
--- /dev/null
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+ *
+
diff --git a/feature-libs/user/account/components/login-form/otp-login-form.component.spec.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
similarity index 100%
rename from feature-libs/user/account/components/login-form/otp-login-form.component.spec.ts
rename to feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
diff --git a/feature-libs/user/account/components/login-form/otp-login-form.component.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
similarity index 96%
rename from feature-libs/user/account/components/login-form/otp-login-form.component.ts
rename to feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
index b07b1a426e2..310cfc4fdf6 100644
--- a/feature-libs/user/account/components/login-form/otp-login-form.component.ts
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
@@ -21,11 +21,13 @@ import { CustomFormValidators } from '@spartacus/storefront';
import { BehaviorSubject, Observable, tap } from 'rxjs';
import { VerificationTokenFacade } from '../../root/facade/verification-token.facade';
import { LoginForm, VerificationToken } from '../../root/model';
-import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from './use-otp-login-form';
+import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from '../user-account-constants';
+
+
@Component({
selector: 'cx-otp-login-form',
- templateUrl: './login-form.component.html',
+ templateUrl: './otp-login-form.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OneTimePasswordLoginFormComponent {
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts
new file mode 100644
index 00000000000..41bda3c119d
--- /dev/null
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts
@@ -0,0 +1,52 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { RouterModule } from '@angular/router';
+import {
+ CmsConfig,
+ FeaturesConfigModule,
+ I18nModule,
+ NotAuthGuard,
+ UrlModule,
+ provideDefaultConfig,
+} from '@spartacus/core';
+import {
+ FormErrorsModule,
+ PasswordVisibilityToggleModule,
+ SpinnerModule,
+} from '@spartacus/storefront';
+import { OneTimePasswordLoginFormComponent } from './otp-login-form.component';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ RouterModule,
+ UrlModule,
+ I18nModule,
+ FormErrorsModule,
+ SpinnerModule,
+ PasswordVisibilityToggleModule,
+ FeaturesConfigModule,
+ ],
+ providers: [
+ provideDefaultConfig({
+ cmsComponents: {
+ ReturningCustomerOTPLoginComponent: {
+ component: OneTimePasswordLoginFormComponent,
+ guards: [NotAuthGuard],
+ },
+ },
+ }),
+ ],
+ declarations: [OneTimePasswordLoginFormComponent],
+ exports: [OneTimePasswordLoginFormComponent],
+})
+export class OneTimePasswordLoginFormModeule {}
diff --git a/feature-libs/user/account/components/public_api.ts b/feature-libs/user/account/components/public_api.ts
index 6d7429113b3..a2014b0e5d1 100644
--- a/feature-libs/user/account/components/public_api.ts
+++ b/feature-libs/user/account/components/public_api.ts
@@ -7,5 +7,7 @@
export * from './login-form/index';
export * from './login-register/index';
export * from './login/index';
-export * from './user-account-component.module';
export * from './my-account-v2-user/index';
+export * from './otp-login-form/index';
+export * from './user-account-component.module';
+export * from './verification-token-form/index';
diff --git a/feature-libs/user/account/components/user-account-component.module.ts b/feature-libs/user/account/components/user-account-component.module.ts
index 16477be59d3..406046ad9af 100644
--- a/feature-libs/user/account/components/user-account-component.module.ts
+++ b/feature-libs/user/account/components/user-account-component.module.ts
@@ -11,15 +11,16 @@ import { VerificationTokenFormModule } from './verification-token-form/verificat
import { LoginRegisterModule } from './login-register/login-register.module';
import { LoginModule } from './login/login.module';
import { MyAccountV2UserModule } from './my-account-v2-user';
+import { OneTimePasswordLoginFormModeule } from './otp-login-form';
@NgModule({
imports: [
LoginModule,
LoginFormModule,
VerificationTokenFormModule,
-
LoginRegisterModule,
MyAccountV2UserModule,
+ OneTimePasswordLoginFormModeule,
],
})
export class UserAccountComponentsModule {}
diff --git a/feature-libs/user/account/components/user-account-constants.ts b/feature-libs/user/account/components/user-account-constants.ts
new file mode 100644
index 00000000000..7dfa2a0cdd7
--- /dev/null
+++ b/feature-libs/user/account/components/user-account-constants.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export const ONE_TIME_PASSWORD_LOGIN_PURPOSE = 'LOGIN';
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index cb0872ffed9..f6b9a1e4ac8 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -16,6 +16,7 @@ import {
import { UntypedFormGroup } from '@angular/forms';
import { LAUNCH_CALLER, LaunchDialogService } from '@spartacus/storefront';
import { Observable } from 'rxjs';
+import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from '../user-account-constants';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
@Component({
@@ -64,7 +65,11 @@ export class VerificationTokenFormComponent implements OnInit {
this.isResendDisabled = true;
this.waitTime = 60;
this.startWaitTimeInterval();
- this.service.sentOTP(this.target, this.password, 'LOGIN');
+ this.service.sentOTP(
+ this.target,
+ this.password,
+ ONE_TIME_PASSWORD_LOGIN_PURPOSE
+ );
this.service.displayMessage(this.target);
}
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
index f97053944a5..1fc2f50d6ad 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
@@ -29,6 +29,7 @@ import { defaultVerificationTokenLayoutConfig } from './default-verification-tok
import { VerificationTokenDialogComponent } from './verification-token-dialog.component';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
import { VerificationTokenFormComponent } from './verification-token-form.component';
+import { VerificationTokenFacade } from '../../root/facade';
@NgModule({
imports: [
@@ -54,7 +55,7 @@ import { VerificationTokenFormComponent } from './verification-token-form.compon
{
provide: VerificationTokenFormComponentService,
useClass: VerificationTokenFormComponentService,
- deps: [AuthService, GlobalMessageService, WindowRef],
+ deps: [AuthService, GlobalMessageService, VerificationTokenFacade, WindowRef],
},
],
},
diff --git a/feature-libs/user/account/root/user-account-root.module.ts b/feature-libs/user/account/root/user-account-root.module.ts
index ae5983064a5..398847c60d0 100644
--- a/feature-libs/user/account/root/user-account-root.module.ts
+++ b/feature-libs/user/account/root/user-account-root.module.ts
@@ -23,6 +23,7 @@ export function defaultUserAccountComponentsConfig(): CmsConfig {
'VerifyOTPTokenComponent',
'ReturningCustomerRegisterComponent',
'MyAccountViewUserComponent',
+ 'ReturningCustomerOTPLoginComponent',
],
},
// by default core is bundled together with components
diff --git a/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts
index 57d38ef58a9..e0ad639bd1b 100644
--- a/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts
+++ b/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts
@@ -11,8 +11,8 @@ import {
userAccountTranslations,
} from '@spartacus/user/account/assets';
import {
- UserAccountRootModule,
USER_ACCOUNT_FEATURE,
+ UserAccountRootModule,
} from '@spartacus/user/account/root';
import {
userProfileTranslationChunksConfig,
@@ -28,7 +28,7 @@ import {
USER_PROFILE_FEATURE,
} from '@spartacus/user/profile/root';
import { environment } from '../../../../environments/environment';
-import { USE_ONE_TIME_PASSWORD_LOGIN } from '@spartacus/user/account/components';
+
@NgModule({
@@ -73,10 +73,6 @@ import { USE_ONE_TIME_PASSWORD_LOGIN } from '@spartacus/user/account/components'
provide: USE_MY_ACCOUNT_V2_PASSWORD,
useValue: environment.myAccountV2,
},
- {
- provide: USE_ONE_TIME_PASSWORD_LOGIN,
- useValue: environment.otpUserLogin,
- },
provideConfig({
i18n: {
resources: userProfileTranslations,
From b30b17cc48bf4624563ec7da0ab8eb3b1955268c Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Thu, 18 Apr 2024 11:32:18 +0800
Subject: [PATCH 14/66] remove env var
---
.../src/app/spartacus/features/user/user-feature.module.ts | 4 +---
projects/storefrontapp/src/environments/environment.prod.ts | 1 -
projects/storefrontapp/src/environments/environment.ts | 1 -
.../src/environments/models/build.process.env.d.ts | 1 -
.../src/environments/models/environment.model.ts | 1 -
5 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts
index e0ad639bd1b..b948e9a0674 100644
--- a/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts
+++ b/projects/storefrontapp/src/app/spartacus/features/user/user-feature.module.ts
@@ -11,8 +11,8 @@ import {
userAccountTranslations,
} from '@spartacus/user/account/assets';
import {
- USER_ACCOUNT_FEATURE,
UserAccountRootModule,
+ USER_ACCOUNT_FEATURE,
} from '@spartacus/user/account/root';
import {
userProfileTranslationChunksConfig,
@@ -29,8 +29,6 @@ import {
} from '@spartacus/user/profile/root';
import { environment } from '../../../../environments/environment';
-
-
@NgModule({
imports: [UserAccountRootModule, UserProfileRootModule],
providers: [
diff --git a/projects/storefrontapp/src/environments/environment.prod.ts b/projects/storefrontapp/src/environments/environment.prod.ts
index 7e6e81a28c2..73df861f676 100644
--- a/projects/storefrontapp/src/environments/environment.prod.ts
+++ b/projects/storefrontapp/src/environments/environment.prod.ts
@@ -22,5 +22,4 @@ export const environment: Environment = {
requestedDeliveryDate: buildProcess.env.CX_REQUESTED_DELIVERY_DATE,
pdfInvoices: buildProcess.env.CX_PDF_INVOICES,
myAccountV2: buildProcess.env.CX_MY_ACCOUNT_V2 ?? false,
- otpUserLogin: buildProcess.env.CX_OTP_USER_LOGIN ?? false,
};
diff --git a/projects/storefrontapp/src/environments/environment.ts b/projects/storefrontapp/src/environments/environment.ts
index 182485edb5f..5ac06fae96e 100644
--- a/projects/storefrontapp/src/environments/environment.ts
+++ b/projects/storefrontapp/src/environments/environment.ts
@@ -35,5 +35,4 @@ export const environment: Environment = {
requestedDeliveryDate: buildProcess.env.CX_REQUESTED_DELIVERY_DATE ?? false,
pdfInvoices: buildProcess.env.CX_PDF_INVOICES ?? false,
myAccountV2: buildProcess.env.CX_MY_ACCOUNT_V2 ?? false,
- otpUserLogin: buildProcess.env.CX_OTP_USER_LOGIN ?? false,
};
diff --git a/projects/storefrontapp/src/environments/models/build.process.env.d.ts b/projects/storefrontapp/src/environments/models/build.process.env.d.ts
index 6b4cf23c8f6..3e52ae56530 100644
--- a/projects/storefrontapp/src/environments/models/build.process.env.d.ts
+++ b/projects/storefrontapp/src/environments/models/build.process.env.d.ts
@@ -24,5 +24,4 @@ interface Env {
CX_REQUESTED_DELIVERY_DATE: boolean;
CX_PDF_INVOICES: boolean;
CX_MY_ACCOUNT_V2: boolean;
- CX_OTP_USER_LOGIN: boolean;
}
diff --git a/projects/storefrontapp/src/environments/models/environment.model.ts b/projects/storefrontapp/src/environments/models/environment.model.ts
index a84a39ed7c7..754f3f7d5c2 100644
--- a/projects/storefrontapp/src/environments/models/environment.model.ts
+++ b/projects/storefrontapp/src/environments/models/environment.model.ts
@@ -20,5 +20,4 @@ export interface Environment {
requestedDeliveryDate: boolean;
pdfInvoices: boolean;
myAccountV2: boolean;
- otpUserLogin: boolean;
}
From 6ba6f02fab598b023d3a48da1226673e066304d0 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Thu, 18 Apr 2024 16:02:15 +0800
Subject: [PATCH 15/66] refine code
---
.../components/login-form/login-form.module.ts | 1 -
.../otp-login-form.component.spec.ts | 9 ++++++---
.../otp-login-form/otp-login-form.component.ts | 10 ++++++----
.../otp-login-form/otp-login-form.module.ts | 1 -
.../verification-token-form-component.service.ts | 2 +-
feature-libs/user/account/root/model/index.ts | 1 +
.../user/account/root/model/otp-login.model.ts | 16 ++++++++++++++++
.../user/account/root/model/user.model.ts | 12 ------------
8 files changed, 30 insertions(+), 22 deletions(-)
create mode 100644 feature-libs/user/account/root/model/otp-login.model.ts
diff --git a/feature-libs/user/account/components/login-form/login-form.module.ts b/feature-libs/user/account/components/login-form/login-form.module.ts
index cc6a01fd67f..f89dd4404c1 100644
--- a/feature-libs/user/account/components/login-form/login-form.module.ts
+++ b/feature-libs/user/account/components/login-form/login-form.module.ts
@@ -58,6 +58,5 @@ import { LoginFormComponent } from './login-form.component';
}),
],
declarations: [LoginFormComponent],
- exports: [LoginFormComponent],
})
export class LoginFormModule {}
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
index 37026b51f35..18ab74fc904 100644
--- a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
@@ -13,10 +13,13 @@ import {
WindowRef,
} from '@spartacus/core';
import { FormErrorsModule, SpinnerModule } from '@spartacus/storefront';
+import { VerificationTokenService } from '@spartacus/user/account/core';
+import {
+ LoginForm,
+ VerificationToken,
+ VerificationTokenFacade,
+} from '@spartacus/user/account/root';
import { BehaviorSubject, of } from 'rxjs';
-import { VerificationTokenService } from '../../core/facade';
-import { VerificationTokenFacade } from '../../root/facade/verification-token.facade';
-import { LoginForm, VerificationToken } from '../../root/model';
import { OneTimePasswordLoginFormComponent } from './otp-login-form.component';
import createSpy = jasmine.createSpy;
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
index 310cfc4fdf6..8efd2cff970 100644
--- a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
@@ -19,11 +19,13 @@ import {
} from '@spartacus/core';
import { CustomFormValidators } from '@spartacus/storefront';
import { BehaviorSubject, Observable, tap } from 'rxjs';
-import { VerificationTokenFacade } from '../../root/facade/verification-token.facade';
-import { LoginForm, VerificationToken } from '../../root/model';
-import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from '../user-account-constants';
-
+import {
+ LoginForm,
+ VerificationToken,
+ VerificationTokenFacade,
+} from '@spartacus/user/account/root';
+import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from '../user-account-constants';
@Component({
selector: 'cx-otp-login-form',
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts
index 41bda3c119d..316534fc7d0 100644
--- a/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.module.ts
@@ -47,6 +47,5 @@ import { OneTimePasswordLoginFormComponent } from './otp-login-form.component';
}),
],
declarations: [OneTimePasswordLoginFormComponent],
- exports: [OneTimePasswordLoginFormComponent],
})
export class OneTimePasswordLoginFormModeule {}
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
index 7dcb903df3c..b190740e6f9 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
@@ -16,9 +16,9 @@ import {
GlobalMessageType,
WindowRef,
} from '@spartacus/core';
+import { VerificationTokenFacade } from '@spartacus/user/account/root';
import { BehaviorSubject, from } from 'rxjs';
import { tap, withLatestFrom } from 'rxjs/operators';
-import { VerificationTokenFacade } from '../../root/facade';
@Injectable()
export class VerificationTokenFormComponentService {
diff --git a/feature-libs/user/account/root/model/index.ts b/feature-libs/user/account/root/model/index.ts
index 5b89674523c..beafee30f70 100644
--- a/feature-libs/user/account/root/model/index.ts
+++ b/feature-libs/user/account/root/model/index.ts
@@ -5,4 +5,5 @@
*/
export * from './augmented-core.model';
+export * from './otp-login.model';
export * from './user.model';
diff --git a/feature-libs/user/account/root/model/otp-login.model.ts b/feature-libs/user/account/root/model/otp-login.model.ts
new file mode 100644
index 00000000000..7591837865a
--- /dev/null
+++ b/feature-libs/user/account/root/model/otp-login.model.ts
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export interface LoginForm {
+ purpose: string;
+ loginId: string;
+ password: string;
+}
+
+export interface VerificationToken {
+ expiresIn: string;
+ tokenId: string;
+}
diff --git a/feature-libs/user/account/root/model/user.model.ts b/feature-libs/user/account/root/model/user.model.ts
index d89ba2eeb9a..976fa7fe4f1 100644
--- a/feature-libs/user/account/root/model/user.model.ts
+++ b/feature-libs/user/account/root/model/user.model.ts
@@ -13,15 +13,3 @@ export interface User {
customerId?: string;
title?: string;
}
-
-export interface LoginForm {
- purpose: string;
- loginId: string;
- password: string;
-}
-
-export interface VerificationToken {
- expiresIn: string;
- tokenId: string;
-}
-
From b3ccbfcee8966f13f32e6d2b9636e535b129d29d Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Thu, 18 Apr 2024 16:07:57 +0800
Subject: [PATCH 16/66] CXSPA-6689: code refined
---
projects/core/src/auth/user-auth/facade/auth.service.ts | 6 ++----
.../src/auth/user-auth/services/auth-redirect.service.ts | 2 ++
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/projects/core/src/auth/user-auth/facade/auth.service.ts b/projects/core/src/auth/user-auth/facade/auth.service.ts
index fec0089f076..eb4a3e56baa 100644
--- a/projects/core/src/auth/user-auth/facade/auth.service.ts
+++ b/projects/core/src/auth/user-auth/facade/auth.service.ts
@@ -118,12 +118,9 @@ export class AuthService {
tokenId: string,
tokenCode: string
): Promise {
- debugger;
- let uid = tokenId;
-
try {
await this.oAuthLibWrapperService.authorizeWithPasswordFlow(
- uid,
+ tokenId,
tokenCode
);
@@ -132,6 +129,7 @@ export class AuthService {
this.store.dispatch(new AuthActions.Login());
+ debugger;
this.authRedirectService.redirect();
} catch {}
}
diff --git a/projects/core/src/auth/user-auth/services/auth-redirect.service.ts b/projects/core/src/auth/user-auth/services/auth-redirect.service.ts
index 09829e8173b..587f0342ac3 100644
--- a/projects/core/src/auth/user-auth/services/auth-redirect.service.ts
+++ b/projects/core/src/auth/user-auth/services/auth-redirect.service.ts
@@ -65,8 +65,10 @@ export class AuthRedirectService implements OnDestroy {
.pipe(take(1))
.subscribe((redirectUrl) => {
if (redirectUrl === undefined) {
+ debugger;
this.routing.go('/');
} else {
+ debugger;
this.routing.goByUrl(redirectUrl);
}
this.clearRedirectUrl();
From 131b5d65d2f9b6cdc1481a9301566fb65bb45820 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Fri, 19 Apr 2024 14:46:25 +0800
Subject: [PATCH 17/66] add ut
---
.../otp-login-form.component.spec.ts | 75 ++++++++++---------
.../otp-login-form.component.ts | 39 +++++-----
.../user-account-component.module.ts | 5 +-
.../connectors/user-account.connector.spec.ts | 2 +-
.../facade/verification-token.service.spec.ts | 2 +-
.../adapters/occ-user-account.adapter.spec.ts | 7 +-
6 files changed, 66 insertions(+), 64 deletions(-)
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
index 18ab74fc904..18a398e0b3b 100644
--- a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
@@ -1,15 +1,12 @@
import { DebugElement, Pipe, PipeTransform } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import {
- ReactiveFormsModule,
- UntypedFormControl,
- UntypedFormGroup,
-} from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import {
GlobalMessageService,
I18nTestingModule,
+ RoutingService,
WindowRef,
} from '@spartacus/core';
import { FormErrorsModule, SpinnerModule } from '@spartacus/storefront';
@@ -19,21 +16,13 @@ import {
VerificationToken,
VerificationTokenFacade,
} from '@spartacus/user/account/root';
-import { BehaviorSubject, of } from 'rxjs';
+import { of } from 'rxjs';
import { OneTimePasswordLoginFormComponent } from './otp-login-form.component';
import createSpy = jasmine.createSpy;
-const isBusySubject = new BehaviorSubject(false);
-
-const formGroup: UntypedFormGroup = new UntypedFormGroup({
- userId: new UntypedFormControl(),
- password: new UntypedFormControl(),
-});
-const isUpdating$ = isBusySubject;
-
-const form: LoginForm = {
+const loginForm: LoginForm = {
purpose: 'LOGIN',
- loginId: 'mockEmail',
+ loginId: 'test@email.com',
password: '1234',
};
@@ -61,6 +50,10 @@ class MockGlobalMessageService {
remove = createSpy().and.stub();
}
+class MockRoutingService implements Partial {
+ go = () => Promise.resolve(true);
+}
+
@Pipe({
name: 'cxUrl',
})
@@ -93,6 +86,7 @@ describe('OneTimePasswordLoginFormComponent', () => {
},
{ provide: WindowRef, useClass: MockWinRef },
{ provide: GlobalMessageService, useClass: MockGlobalMessageService },
+ { provide: RoutingService, useClass: MockRoutingService },
],
}).compileComponents();
})
@@ -101,7 +95,7 @@ describe('OneTimePasswordLoginFormComponent', () => {
beforeEach(() => {
winRef = TestBed.inject(WindowRef);
fixture = TestBed.createComponent(OneTimePasswordLoginFormComponent);
- service = TestBed.inject(VerificationTokenService);
+ service = TestBed.inject(VerificationTokenFacade);
component = fixture.componentInstance;
el = fixture.debugElement;
fixture.detectChanges();
@@ -112,41 +106,42 @@ describe('OneTimePasswordLoginFormComponent', () => {
});
describe('login', () => {
- const userId = 'test@email.com';
- const password = 'secret';
-
it('should not patch user id', () => {
- isUpdating$.subscribe().unsubscribe();
- expect(form.value.userId).toEqual('');
+ component.isUpdating$.subscribe().unsubscribe();
+ expect(component.form.value.userId).toEqual('');
});
it('should patch user id', () => {
spyOnProperty(winRef, 'nativeWindow', 'get').and.returnValue({
- history: { state: { newUid: 'test.user@shop.com' } },
+ history: { state: { newUid: loginForm.loginId } },
} as Window);
- isUpdating$.subscribe().unsubscribe();
- expect(formGroup.value.userId).toEqual('test.user@shop.com');
+ component.isUpdating$.subscribe().unsubscribe();
+ expect(component.form.value.userId).toEqual(loginForm.loginId);
});
describe('success', () => {
+ beforeEach(() => {
+ component.form.setValue({
+ userId: loginForm.loginId,
+ password: loginForm.password,
+ });
+ });
+
it('should request email', () => {
component.onSubmit();
- expect(service.createVerificationToken).toHaveBeenCalledWith(
- userId,
- password
- );
+ expect(service.createVerificationToken).toHaveBeenCalledWith(loginForm);
});
it('should reset the form', () => {
- spyOn(formGroup, 'reset').and.stub();
- service.createVerificationToken(form);
- expect(formGroup.reset).toHaveBeenCalled();
+ spyOn(component.form, 'reset').and.stub();
+ component.onSubmit();
+ expect(component.form.reset).toHaveBeenCalled();
});
});
describe('error', () => {
beforeEach(() => {
- formGroup.setValue({
+ component.form.setValue({
userId: 'invalid',
password: '123',
});
@@ -158,9 +153,9 @@ describe('OneTimePasswordLoginFormComponent', () => {
});
it('should not reset the form', () => {
- spyOn(formGroup, 'reset').and.stub();
+ spyOn(component.form, 'reset').and.stub();
component.onSubmit();
- expect(formGroup.reset).not.toHaveBeenCalled();
+ expect(component.form.reset).not.toHaveBeenCalled();
});
});
});
@@ -176,7 +171,8 @@ describe('OneTimePasswordLoginFormComponent', () => {
});
it('should show the spinner', () => {
- isBusySubject.next(true);
+ // @ts-ignore
+ component.busy$.next(true);
fixture.detectChanges();
expect(el.query(By.css('cx-spinner'))).toBeTruthy();
});
@@ -191,7 +187,8 @@ describe('OneTimePasswordLoginFormComponent', () => {
});
it('should not show the spinner', () => {
- isBusySubject.next(false);
+ // @ts-ignore
+ component.isUpdating$.next(false);
fixture.detectChanges();
expect(el.query(By.css('cx-spinner'))).toBeNull();
});
@@ -206,6 +203,10 @@ describe('OneTimePasswordLoginFormComponent', () => {
});
it('should call the service method on submit', () => {
+ component.form.setValue({
+ userId: loginForm.loginId,
+ password: loginForm.password,
+ });
component.onSubmit();
expect(service.createVerificationToken).toHaveBeenCalled();
});
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
index 8efd2cff970..b82f7084b72 100644
--- a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
@@ -75,10 +75,28 @@ export class OneTimePasswordLoginFormComponent {
this.goToVerificationTokenForm(result, loginForm),
error: (error: HttpErrorModel) =>
this.onCreateVerificationTokenFail(error),
- complete: () => this.busy$.next(false),
+ complete: () => this.onCreateVerificationTokenComplete(),
});
}
+ protected goToVerificationTokenForm(
+ verificationToken: VerificationToken,
+ loginForm: LoginForm
+ ): void {
+ this.routingService.go(
+ {
+ cxRoute: 'verifyToken',
+ },
+ {
+ state: {
+ loginId: loginForm.loginId,
+ password: loginForm.password,
+ tokenId: verificationToken.tokenId,
+ },
+ }
+ );
+ }
+
protected onCreateVerificationTokenFail(error: HttpErrorModel): void {
this.busy$.next(false);
const errorDetails = error.details ?? [];
@@ -96,22 +114,9 @@ export class OneTimePasswordLoginFormComponent {
});
}
- protected goToVerificationTokenForm(
- verificationToken: VerificationToken,
- loginForm: LoginForm
- ): void {
- this.routingService.go(
- {
- cxRoute: 'verifyToken',
- },
- {
- state: {
- loginId: loginForm.loginId,
- password: loginForm.password,
- tokenId: verificationToken.tokenId,
- },
- }
- );
+ protected onCreateVerificationTokenComplete(): void {
+ this.form.reset();
+ this.busy$.next(false);
}
protected collectDataFromLoginForm(): LoginForm {
diff --git a/feature-libs/user/account/components/user-account-component.module.ts b/feature-libs/user/account/components/user-account-component.module.ts
index 406046ad9af..d0cecd90fef 100644
--- a/feature-libs/user/account/components/user-account-component.module.ts
+++ b/feature-libs/user/account/components/user-account-component.module.ts
@@ -6,12 +6,11 @@
import { NgModule } from '@angular/core';
import { LoginFormModule } from './login-form/login-form.module';
-import { VerificationTokenFormModule } from './verification-token-form/verification-token-form.module';
-
import { LoginRegisterModule } from './login-register/login-register.module';
import { LoginModule } from './login/login.module';
import { MyAccountV2UserModule } from './my-account-v2-user';
-import { OneTimePasswordLoginFormModeule } from './otp-login-form';
+import { OneTimePasswordLoginFormModeule } from './otp-login-form/otp-login-form.module';
+import { VerificationTokenFormModule } from './verification-token-form/verification-token-form.module';
@NgModule({
imports: [
diff --git a/feature-libs/user/account/core/connectors/user-account.connector.spec.ts b/feature-libs/user/account/core/connectors/user-account.connector.spec.ts
index bcfaa0e2e6a..019f3ebc438 100644
--- a/feature-libs/user/account/core/connectors/user-account.connector.spec.ts
+++ b/feature-libs/user/account/core/connectors/user-account.connector.spec.ts
@@ -7,7 +7,7 @@ import createSpy = jasmine.createSpy;
const form: LoginForm = {
purpose: 'LOGIN',
- loginId: 'mockEmail',
+ loginId: 'test@email.com',
password: '1234',
};
diff --git a/feature-libs/user/account/core/facade/verification-token.service.spec.ts b/feature-libs/user/account/core/facade/verification-token.service.spec.ts
index d1b114b57f2..37f99be929a 100644
--- a/feature-libs/user/account/core/facade/verification-token.service.spec.ts
+++ b/feature-libs/user/account/core/facade/verification-token.service.spec.ts
@@ -10,7 +10,7 @@ import createSpy = jasmine.createSpy;
const form: LoginForm = {
purpose: 'LOGIN',
- loginId: 'mockEmail',
+ loginId: 'test@email.com',
password: '1234',
};
diff --git a/feature-libs/user/account/occ/adapters/occ-user-account.adapter.spec.ts b/feature-libs/user/account/occ/adapters/occ-user-account.adapter.spec.ts
index 2cac5c49d93..ee8a6777581 100644
--- a/feature-libs/user/account/occ/adapters/occ-user-account.adapter.spec.ts
+++ b/feature-libs/user/account/occ/adapters/occ-user-account.adapter.spec.ts
@@ -63,7 +63,7 @@ const user: User = {
const form: LoginForm = {
purpose: 'LOGIN',
- loginId: 'mockEmail',
+ loginId: 'test@email.com',
password: password,
};
@@ -147,10 +147,7 @@ describe('OccUserAccountAdapter', () => {
});
expect(occEndpointsService.buildUrl).toHaveBeenCalledWith(
- 'createVerificationToken',
- {
- state: { form },
- }
+ 'createVerificationToken'
);
expect(mockReq.cancelled).toBeFalsy();
expect(mockReq.request.responseType).toEqual('json');
From 97a40db2f7388a73129d7f508fb733d0700dbca5 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Fri, 19 Apr 2024 17:58:05 +0800
Subject: [PATCH 18/66] CXSPA-6689: add unit tests
---
...erification-token-dialog.component.spec.ts | 64 ++++++++
...ation-token-form-component.service.spec.ts | 108 +++++++++++++
...rification-token-form-component.service.ts | 4 +-
.../verification-token-form.component.spec.ts | 148 ++++++++++++++++++
.../verification-token-form.component.ts | 12 +-
5 files changed, 328 insertions(+), 8 deletions(-)
create mode 100644 feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts
create mode 100644 feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.spec.ts
create mode 100644 feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts
new file mode 100644
index 00000000000..e8ec2a19227
--- /dev/null
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts
@@ -0,0 +1,64 @@
+import { Pipe, PipeTransform } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { FocusDirective, LaunchDialogService } from '@spartacus/storefront';
+import {
+ VERIFICATION_TOKEN_DIALOG_ACTION,
+ VerificationTokenDialogComponent,
+} from './verification-token-dialog.component';
+
+@Pipe({
+ name: 'cxTranslate',
+})
+class MockTranslatePipe implements PipeTransform {
+ transform(): any {}
+}
+
+class MockLaunchDialogService implements Partial {
+ closeDialog(_reason: any) {}
+}
+
+describe('AsmBindCartDialogComponent', () => {
+ let component: VerificationTokenDialogComponent;
+ let fixture: ComponentFixture;
+
+ let launchDialogService: LaunchDialogService;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [
+ VerificationTokenDialogComponent,
+ MockTranslatePipe,
+ FocusDirective,
+ ],
+ providers: [
+ { provide: LaunchDialogService, useClass: MockLaunchDialogService },
+ ],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(VerificationTokenDialogComponent);
+ component = fixture.componentInstance;
+
+ launchDialogService = TestBed.inject(LaunchDialogService);
+
+ spyOn(launchDialogService, 'closeDialog').and.stub();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should close with replace action when replace button is clicked', () => {
+ fixture.detectChanges();
+
+ fixture.debugElement
+ .query(By.css('.btn-primary'))
+ .triggerEventHandler('click');
+
+ expect(launchDialogService.closeDialog).toHaveBeenCalledWith(
+ VERIFICATION_TOKEN_DIALOG_ACTION.OK
+ );
+ });
+});
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.spec.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.spec.ts
new file mode 100644
index 00000000000..00db503105b
--- /dev/null
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.spec.ts
@@ -0,0 +1,108 @@
+import { TestBed, waitForAsync } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterTestingModule } from '@angular/router/testing';
+import {
+ AuthService,
+ GlobalMessageService,
+ I18nTestingModule,
+} from '@spartacus/core';
+import { FormErrorsModule } from '@spartacus/storefront';
+import { of } from 'rxjs';
+import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
+import createSpy = jasmine.createSpy;
+
+class MockAuthService implements Partial {
+ otpLoginWithCredentials = createSpy().and.returnValue(of({}));
+ isUserLoggedIn = createSpy().and.returnValue(of(true));
+}
+
+class MockGlobalMessageService {
+ add = createSpy().and.stub();
+ remove = createSpy().and.stub();
+}
+
+describe('VerificationTokenFormComponentService', () => {
+ let service: VerificationTokenFormComponentService;
+ let authService: AuthService;
+
+ beforeEach(
+ waitForAsync(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ ReactiveFormsModule,
+ RouterTestingModule,
+ I18nTestingModule,
+ FormErrorsModule,
+ ],
+ declarations: [],
+ providers: [
+ VerificationTokenFormComponentService,
+ { provide: AuthService, useClass: MockAuthService },
+ { provide: GlobalMessageService, useClass: MockGlobalMessageService },
+ ],
+ }).compileComponents();
+ })
+ );
+
+ beforeEach(() => {
+ service = TestBed.inject(VerificationTokenFormComponentService);
+ authService = TestBed.inject(AuthService);
+ });
+
+ it('should create service', () => {
+ expect(service).toBeTruthy();
+ });
+
+ describe('login', () => {
+ const tokenId = '';
+ const tokenCode = 'XG5tyu';
+
+ it('should not patch token id', () => {
+ service.isUpdating$.subscribe().unsubscribe();
+ expect(service.form.value.tokenId).toEqual('');
+ });
+
+ describe('success', () => {
+ beforeEach(() => {
+ service.form.setValue({
+ tokenId,
+ tokenCode,
+ });
+ });
+
+ it('should request email', () => {
+ service.login();
+ expect(authService.otpLoginWithCredentials).toHaveBeenCalledWith(
+ tokenId,
+ tokenCode
+ );
+ });
+
+ it('should reset the form', () => {
+ spyOn(service.form, 'reset').and.stub();
+ service.login();
+ expect(service.form.reset).toHaveBeenCalled();
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ service.form.setValue({
+ tokenCode: '',
+ tokenId: '',
+ });
+ });
+
+ it('should not login', () => {
+ service.login();
+ expect(authService.otpLoginWithCredentials).not.toHaveBeenCalled();
+ });
+
+ it('should not reset the form', () => {
+ spyOn(service.form, 'reset').and.stub();
+ service.login();
+ expect(service.form.reset).not.toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
index b190740e6f9..748bee81238 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
@@ -14,7 +14,6 @@ import {
AuthService,
GlobalMessageService,
GlobalMessageType,
- WindowRef,
} from '@spartacus/core';
import { VerificationTokenFacade } from '@spartacus/user/account/root';
import { BehaviorSubject, from } from 'rxjs';
@@ -25,8 +24,7 @@ export class VerificationTokenFormComponentService {
constructor(
protected auth: AuthService,
protected globalMessage: GlobalMessageService,
- protected verificationTokenFacade: VerificationTokenFacade,
- protected winRef: WindowRef
+ protected verificationTokenFacade: VerificationTokenFacade
) {}
protected busy$ = new BehaviorSubject(false);
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts
new file mode 100644
index 00000000000..25026e43224
--- /dev/null
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts
@@ -0,0 +1,148 @@
+import { DebugElement, Pipe, PipeTransform } from '@angular/core';
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import {
+ ReactiveFormsModule,
+ UntypedFormControl,
+ UntypedFormGroup,
+} from '@angular/forms';
+import { By } from '@angular/platform-browser';
+import { RouterTestingModule } from '@angular/router/testing';
+import { I18nTestingModule } from '@spartacus/core';
+import {
+ FormErrorsModule,
+ LaunchDialogService,
+ SpinnerModule,
+} from '@spartacus/storefront';
+import { BehaviorSubject } from 'rxjs';
+import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
+import { VerificationTokenFormComponent } from './verification-token-form.component';
+import createSpy = jasmine.createSpy;
+
+const isBusySubject = new BehaviorSubject(false);
+class MockFormComponentService
+ implements Partial
+{
+ form: UntypedFormGroup = new UntypedFormGroup({
+ tokenId: new UntypedFormControl(),
+ tokenCode: new UntypedFormControl(),
+ });
+ isUpdating$ = isBusySubject;
+ login = createSpy().and.stub();
+ sentOTP = createSpy().and.stub();
+ displayMessage = createSpy('displayMessage').and.stub();
+}
+@Pipe({
+ name: 'cxUrl',
+})
+class MockUrlPipe implements PipeTransform {
+ transform() {}
+}
+
+class MockLaunchDialogService implements Partial {
+ openDialogAndSubscribe = createSpy().and.stub();
+}
+
+describe('VerificationTokenFormComponent', () => {
+ let component: VerificationTokenFormComponent;
+ let fixture: ComponentFixture;
+ let el: DebugElement;
+ let service: VerificationTokenFormComponentService;
+ let launchDialogService: LaunchDialogService;
+
+ beforeEach(
+ waitForAsync(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ ReactiveFormsModule,
+ RouterTestingModule,
+ I18nTestingModule,
+ FormErrorsModule,
+ SpinnerModule,
+ ],
+ declarations: [VerificationTokenFormComponent, MockUrlPipe],
+ providers: [
+ {
+ provide: VerificationTokenFormComponentService,
+ useClass: MockFormComponentService,
+ },
+ {
+ provide: LaunchDialogService,
+ useClass: MockLaunchDialogService,
+ },
+ ],
+ }).compileComponents();
+ })
+ );
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(VerificationTokenFormComponent);
+ service = TestBed.inject(VerificationTokenFormComponentService);
+ launchDialogService = TestBed.inject(LaunchDialogService);
+ component = fixture.componentInstance;
+ el = fixture.debugElement;
+ fixture.detectChanges();
+ history.pushState(
+ {
+ tokenId: '',
+ password: 'pw4all',
+ loginId: 'test@sap.com',
+ },
+ ''
+ );
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+
+ describe('busy', () => {
+ it('should disable the submit button when form is disabled', () => {
+ component.form.disable();
+ fixture.detectChanges();
+ const submitBtn: HTMLButtonElement = el.query(
+ By.css('button')
+ ).nativeElement;
+ expect(submitBtn.disabled).toBeTruthy();
+ });
+
+ it('should show the spinner', () => {
+ isBusySubject.next(true);
+ fixture.detectChanges();
+ expect(el.query(By.css('cx-spinner'))).toBeTruthy();
+ });
+ });
+
+ describe('idle', () => {
+ it('should enable the submit button', () => {
+ component.form.enable();
+ fixture.detectChanges();
+ const submitBtn = el.query(By.css('button'));
+ expect(submitBtn.nativeElement.disabled).toBeFalsy();
+ });
+
+ it('should not show the spinner', () => {
+ isBusySubject.next(false);
+ fixture.detectChanges();
+ expect(el.query(By.css('cx-spinner'))).toBeNull();
+ });
+ });
+
+ describe('Form Interactions', () => {
+ it('should call onSubmit() method on submit', () => {
+ const request = spyOn(component, 'onSubmit');
+ const form = el.query(By.css('form'));
+ form.triggerEventHandler('submit', null);
+ expect(request).toHaveBeenCalled();
+ });
+
+ it('should call the service method on submit', () => {
+ component.onSubmit();
+ expect(service.login).toHaveBeenCalled();
+ });
+
+ it('should display info dialog', () => {
+ component.openInfoDailog();
+ expect(launchDialogService.openDialogAndSubscribe).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index f6b9a1e4ac8..52c500d6112 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -46,15 +46,17 @@ export class VerificationTokenFormComponent implements OnInit {
password: string;
- waitTime: int = 60;
+ waitTime: number = 60;
isResendDisabled: boolean = true;
ngOnInit() {
- this.tokenId = history.state['tokenId'];
- this.password = history.state['password'];
- this.target = history.state['loginId'];
- this.startWaitTimeInterval();
+ if (!!history.state) {
+ this.tokenId = history.state['tokenId'];
+ this.password = history.state['password'];
+ this.target = history.state['loginId'];
+ this.startWaitTimeInterval();
+ }
}
onSubmit(): void {
From bfc51650ee81d77b85d3453cc0206ae8b49cc1dd Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Mon, 22 Apr 2024 09:47:32 +0800
Subject: [PATCH 19/66] add e2e test
---
.../otp-login-form.component.spec.ts | 17 +----
.../otp-login-form.component.ts | 32 ++-------
.../assets/src/translations/en/common.json | 3 +-
.../user_access/otp-login.e2e-flaky.cy.ts | 65 +++++++++++++++++++
4 files changed, 74 insertions(+), 43 deletions(-)
create mode 100644 projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
index 18a398e0b3b..304d9baac57 100644
--- a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.spec.ts
@@ -3,12 +3,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
-import {
- GlobalMessageService,
- I18nTestingModule,
- RoutingService,
- WindowRef,
-} from '@spartacus/core';
+import { I18nTestingModule, RoutingService, WindowRef } from '@spartacus/core';
import { FormErrorsModule, SpinnerModule } from '@spartacus/storefront';
import { VerificationTokenService } from '@spartacus/user/account/core';
import {
@@ -45,11 +40,6 @@ class MockVerificationTokenService
);
}
-class MockGlobalMessageService {
- add = createSpy().and.stub();
- remove = createSpy().and.stub();
-}
-
class MockRoutingService implements Partial {
go = () => Promise.resolve(true);
}
@@ -85,7 +75,6 @@ describe('OneTimePasswordLoginFormComponent', () => {
useClass: MockVerificationTokenService,
},
{ provide: WindowRef, useClass: MockWinRef },
- { provide: GlobalMessageService, useClass: MockGlobalMessageService },
{ provide: RoutingService, useClass: MockRoutingService },
],
}).compileComponents();
@@ -105,7 +94,7 @@ describe('OneTimePasswordLoginFormComponent', () => {
expect(component).toBeTruthy();
});
- describe('login', () => {
+ describe('create OTP', () => {
it('should not patch user id', () => {
component.isUpdating$.subscribe().unsubscribe();
expect(component.form.value.userId).toEqual('');
@@ -147,7 +136,7 @@ describe('OneTimePasswordLoginFormComponent', () => {
});
});
- it('should not login', () => {
+ it('should not create OTP', () => {
component.onSubmit();
expect(service.createVerificationToken).not.toHaveBeenCalled();
});
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
index b82f7084b72..2045898f4eb 100644
--- a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
@@ -10,13 +10,7 @@ import {
UntypedFormGroup,
Validators,
} from '@angular/forms';
-import {
- GlobalMessageService,
- GlobalMessageType,
- HttpErrorModel,
- RoutingService,
- WindowRef,
-} from '@spartacus/core';
+import { RoutingService, WindowRef } from '@spartacus/core';
import { CustomFormValidators } from '@spartacus/storefront';
import { BehaviorSubject, Observable, tap } from 'rxjs';
@@ -36,8 +30,7 @@ export class OneTimePasswordLoginFormComponent {
constructor(
protected routingService: RoutingService,
protected verificationTokenFacade: VerificationTokenFacade,
- protected winRef: WindowRef,
- protected globalMessage: GlobalMessageService
+ protected winRef: WindowRef
) {}
protected busy$ = new BehaviorSubject(false);
@@ -73,8 +66,7 @@ export class OneTimePasswordLoginFormComponent {
this.verificationTokenFacade.createVerificationToken(loginForm).subscribe({
next: (result: VerificationToken) =>
this.goToVerificationTokenForm(result, loginForm),
- error: (error: HttpErrorModel) =>
- this.onCreateVerificationTokenFail(error),
+ error: () => this.busy$.next(false),
complete: () => this.onCreateVerificationTokenComplete(),
});
}
@@ -92,28 +84,12 @@ export class OneTimePasswordLoginFormComponent {
loginId: loginForm.loginId,
password: loginForm.password,
tokenId: verificationToken.tokenId,
+ expiresIn: verificationToken.expiresIn,
},
}
);
}
- protected onCreateVerificationTokenFail(error: HttpErrorModel): void {
- this.busy$.next(false);
- const errorDetails = error.details ?? [];
- if (errorDetails.length === 0) {
- this.globalMessage.add(
- { key: 'httpHandlers.unknownError' },
- GlobalMessageType.MSG_TYPE_ERROR
- );
- }
- errorDetails.forEach((err) => {
- this.globalMessage.add(
- { raw: err.message },
- GlobalMessageType.MSG_TYPE_ERROR
- );
- });
- }
-
protected onCreateVerificationTokenComplete(): void {
this.form.reset();
this.busy$.next(false);
diff --git a/projects/assets/src/translations/en/common.json b/projects/assets/src/translations/en/common.json
index cfc837d9428..3d5452257e6 100644
--- a/projects/assets/src/translations/en/common.json
+++ b/projects/assets/src/translations/en/common.json
@@ -132,7 +132,8 @@
"password": "Password entered is not valid.",
"uid": "UID is not valid.",
"code": "Code is not valid.",
- "email": "Email is not valid."
+ "email": "Email is not valid.",
+ "loginId": "Email is not valid."
}
},
"cartNotFound": "Cart not found.",
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
new file mode 100644
index 00000000000..2ec343fad50
--- /dev/null
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
@@ -0,0 +1,65 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as login from '../../../helpers/login';
+import { viewportContext } from '../../../helpers/viewport-context';
+import { interceptPost } from '../../../support/utils/intercept';
+
+export function listenForCreateVerificationToken(): string {
+ return interceptPost(
+ 'createVerificationToken',
+ 'users/anonymous/verificationToken'
+ );
+}
+
+describe('OTP Login', () => {
+ viewportContext(['mobile'], () => {
+ before(() => {
+ cy.visit('/login');
+ });
+
+ describe('Create OTP', () => {
+ it('should be able to create a new OTP by customer click Sign In button (CXSPA-6672)', () => {
+ const user = login.registerUserFromLoginPage();
+ listenForCreateVerificationToken();
+
+ cy.log(`đź›’ Logging in user ${user.email} from the login form`);
+ cy.get('cx-otp-login-form form').within(() => {
+ cy.get('[formcontrolname="userId"]').clear().type(user.email);
+ cy.get('[formcontrolname="password"]').clear().type(user.password);
+ cy.get('button[type=submit]').click();
+ });
+ // cy.wait('@createVerificationToken')
+ // .its('response.statusCode')
+ // .should('eq', 201);
+
+ cy.get('cx-verification-token-form').should('exist');
+ cy.get('cx-verification-token-form').should('be.visible');
+ cy.get(login.userGreetSelector).should('not.exist');
+ });
+ });
+
+ describe('Failed to Create OTP', () => {
+ it('should be not able to create OTP with invalid user data (CXSPA-6672)', () => {
+ listenForCreateVerificationToken();
+ cy.get('cx-otp-login-form form').within(() => {
+ cy.get('[formcontrolname="userId"]')
+ .clear()
+ .type('test.user@sap.coma');
+ cy.get('[formcontrolname="password"]').clear().type('1234');
+ cy.get('button[type=submit]').click();
+ });
+ // cy.wait('@createVerificationToken')
+ // .its('response.statusCode')
+ // .should('eq', 400);
+
+ cy.get('cx-global-message').within(() => {
+ cy.get('span').contains('Email is not valid.');
+ });
+ });
+ });
+ });
+});
From 12d33624bdeea0e5d37fcae2750a08e7ef0c2c17 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Mon, 22 Apr 2024 16:56:09 +0800
Subject: [PATCH 20/66] CXSPA-6689: code refined
---
.../verification-token-form.component.ts | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index 52c500d6112..4054dca2acd 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -55,6 +55,14 @@ export class VerificationTokenFormComponent implements OnInit {
this.tokenId = history.state['tokenId'];
this.password = history.state['password'];
this.target = history.state['loginId'];
+ history.pushState(
+ {
+ tokenId: '',
+ password: '',
+ loginId: '',
+ },
+ 'verifyToken'
+ );
this.startWaitTimeInterval();
}
}
From a8cc45b9235fd9bd03adf695a59d06036b9b2c4b Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Mon, 22 Apr 2024 17:31:37 +0800
Subject: [PATCH 21/66] CXSPA-6689: add unit test
---
.../user-auth/facade/auth.service.spec.ts | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/projects/core/src/auth/user-auth/facade/auth.service.spec.ts b/projects/core/src/auth/user-auth/facade/auth.service.spec.ts
index 74d79f03d93..5ae04fa06f4 100644
--- a/projects/core/src/auth/user-auth/facade/auth.service.spec.ts
+++ b/projects/core/src/auth/user-auth/facade/auth.service.spec.ts
@@ -185,6 +185,30 @@ describe('AuthService', () => {
});
});
+ describe('otpLoginWithCredentials()', () => {
+ it('should login user', async () => {
+ spyOn(
+ oAuthLibWrapperService,
+ 'authorizeWithPasswordFlow'
+ ).and.callThrough();
+ spyOn(userIdService, 'setUserId').and.callThrough();
+ spyOn(authRedirectService, 'redirect').and.callThrough();
+ spyOn(store, 'dispatch').and.callThrough();
+
+ const tokenId = '';
+ const tokenCode = 'XD2iuP';
+
+ await service.otpLoginWithCredentials(tokenId, tokenCode);
+
+ expect(
+ oAuthLibWrapperService.authorizeWithPasswordFlow
+ ).toHaveBeenCalledWith(tokenId, tokenCode);
+ expect(userIdService.setUserId).toHaveBeenCalledWith(OCC_USER_ID_CURRENT);
+ expect(store.dispatch).toHaveBeenCalledWith(new AuthActions.Login());
+ expect(authRedirectService.redirect).toHaveBeenCalled();
+ });
+ });
+
describe('coreLogout()', () => {
it('should revoke tokens and logout', fakeAsync(() => {
spyOn(userIdService, 'clearUserId').and.callThrough();
From d11f38375391dff3046659feb330ae69452b7f5c Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Mon, 22 Apr 2024 17:55:38 +0800
Subject: [PATCH 22/66] CXSPA: sonar fixed
---
.../assets/translations/en/userAccount.json | 2 +-
.../verification-token-dialog.component.ts | 6 ++----
.../verification-token-form.component.ts | 2 +-
.../verification-token-form.module.ts | 7 ++++++-
.../account/core/connectors/converters.ts | 20 ++++++++++---------
feature-libs/user/account/styles/_index.scss | 1 -
.../styles/_verification-token-form.scss | 4 ++--
7 files changed, 23 insertions(+), 19 deletions(-)
diff --git a/feature-libs/user/account/assets/translations/en/userAccount.json b/feature-libs/user/account/assets/translations/en/userAccount.json
index e109c12a4c0..407ea159b5e 100644
--- a/feature-libs/user/account/assets/translations/en/userAccount.json
+++ b/feature-libs/user/account/assets/translations/en/userAccount.json
@@ -42,4 +42,4 @@
"myAccountV2User": {
"signOut": "Sign Out"
}
-}
\ No newline at end of file
+}
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts
index 55b98f67545..7a32f089c5e 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { Component, OnInit } from '@angular/core';
+import { Component } from '@angular/core';
import { FocusConfig, LaunchDialogService } from '@spartacus/storefront';
export enum VERIFICATION_TOKEN_DIALOG_ACTION {
@@ -15,7 +15,7 @@ export enum VERIFICATION_TOKEN_DIALOG_ACTION {
selector: 'cx-verification-token-dialog',
templateUrl: './verification-token-dialog.component.html',
})
-export class VerificationTokenDialogComponent implements OnInit {
+export class VerificationTokenDialogComponent {
VERIFICATION_TOKEN_DIALOG_ACTION = VERIFICATION_TOKEN_DIALOG_ACTION;
focusConfig: FocusConfig = {
@@ -27,8 +27,6 @@ export class VerificationTokenDialogComponent implements OnInit {
constructor(protected launchDialogService: LaunchDialogService) {}
- ngOnInit(): void {}
-
closeModal(reason: VERIFICATION_TOKEN_DIALOG_ACTION): void {
this.launchDialogService.closeDialog(reason);
}
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index 4054dca2acd..fae0e43f497 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -84,7 +84,7 @@ export class VerificationTokenFormComponent implements OnInit {
}
startWaitTimeInterval(): void {
- let interval = setInterval(() => {
+ const interval = setInterval(() => {
this.waitTime--;
this.cdr.detectChanges();
if (this.waitTime <= 0) {
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
index 1fc2f50d6ad..6a332aa2507 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
@@ -55,7 +55,12 @@ import { VerificationTokenFacade } from '../../root/facade';
{
provide: VerificationTokenFormComponentService,
useClass: VerificationTokenFormComponentService,
- deps: [AuthService, GlobalMessageService, VerificationTokenFacade, WindowRef],
+ deps: [
+ AuthService,
+ GlobalMessageService,
+ VerificationTokenFacade,
+ WindowRef,
+ ],
},
],
},
diff --git a/feature-libs/user/account/core/connectors/converters.ts b/feature-libs/user/account/core/connectors/converters.ts
index 163a5c2c131..434cf2c151f 100644
--- a/feature-libs/user/account/core/connectors/converters.ts
+++ b/feature-libs/user/account/core/connectors/converters.ts
@@ -6,7 +6,11 @@
import { InjectionToken } from '@angular/core';
import { Converter } from '@spartacus/core';
-import { LoginForm, User, VerificationToken } from '@spartacus/user/account/root';
+import {
+ LoginForm,
+ User,
+ VerificationToken,
+} from '@spartacus/user/account/root';
export const USER_ACCOUNT_NORMALIZER = new InjectionToken>(
'UserAccountNormalizer'
@@ -16,12 +20,10 @@ export const USER_ACCOUNT_SERIALIZER = new InjectionToken>(
'UserAccountSerializer'
);
+export const VERIFICATION_TOKEN_NORMALIZER = new InjectionToken<
+ Converter
+>('VerificationTokenNormalizer');
-export const VERIFICATION_TOKEN_NORMALIZER = new InjectionToken>(
- 'VerificationTokenNormalizer'
-);
-
-export const LOGIN_FORM_SERIALIZER = new InjectionToken>(
- 'LoginFormSerializer'
-);
-
+export const LOGIN_FORM_SERIALIZER = new InjectionToken<
+ Converter
+>('LoginFormSerializer');
diff --git a/feature-libs/user/account/styles/_index.scss b/feature-libs/user/account/styles/_index.scss
index 6adb0b7c7a6..ab2814ca398 100644
--- a/feature-libs/user/account/styles/_index.scss
+++ b/feature-libs/user/account/styles/_index.scss
@@ -2,6 +2,5 @@
@import './login-form';
@import './otp-login-form';
@import './my-account-v2-user';
-@import './otp-login-form';
@import './verification-token-form';
@import './verification-token-dialog';
diff --git a/feature-libs/user/account/styles/_verification-token-form.scss b/feature-libs/user/account/styles/_verification-token-form.scss
index 294067da99d..f846dd89fcc 100644
--- a/feature-libs/user/account/styles/_verification-token-form.scss
+++ b/feature-libs/user/account/styles/_verification-token-form.scss
@@ -6,12 +6,12 @@
.left-text {
padding: 0;
- text-align: left;
+ text-align: var(--cx-text-align, left);
}
.right-text {
padding: 0;
- text-align: right;
+ text-align: var(--cx-text-align, right);
}
a.disabled-link {
From cb9b70bf617f020fd931a819a87f620fb591a424 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 23 Apr 2024 13:33:54 +0800
Subject: [PATCH 23/66] CXSPA-6689: add unit tests
---
.../verification-token-form.component.spec.ts | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts
index 25026e43224..1a987f736f2 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts
@@ -144,5 +144,16 @@ describe('VerificationTokenFormComponent', () => {
component.openInfoDailog();
expect(launchDialogService.openDialogAndSubscribe).toHaveBeenCalled();
});
+
+ it('should resend otp token', () => {
+ component.resendOTP();
+ expect(service.sentOTP).toHaveBeenCalled();
+ });
+
+ it('should resend otp token', () => {
+ component.resendOTP();
+ expect(service.sentOTP).toHaveBeenCalled();
+ expect(service.displayMessage).toHaveBeenCalled();
+ });
});
});
From f4ef714d10309220b4b3f32d9258602a38cea8ea Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 23 Apr 2024 16:27:55 +0800
Subject: [PATCH 24/66] CXSPA-6689: code refined
---
...ation-token-form-component.service.spec.ts | 26 +++++++++++++
...rification-token-form-component.service.ts | 2 +-
.../verification-token-form.component.spec.ts | 38 ++++++++++++++-----
.../verification-token-form.component.ts | 13 ++++---
4 files changed, 62 insertions(+), 17 deletions(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.spec.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.spec.ts
index 00db503105b..6f34a716e59 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.spec.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.spec.ts
@@ -8,6 +8,7 @@ import {
} from '@spartacus/core';
import { FormErrorsModule } from '@spartacus/storefront';
import { of } from 'rxjs';
+import { VerificationTokenFacade } from '../../root/facade';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
import createSpy = jasmine.createSpy;
@@ -16,6 +17,12 @@ class MockAuthService implements Partial {
isUserLoggedIn = createSpy().and.returnValue(of(true));
}
+class MockVerificationTokenFacade implements Partial {
+ createVerificationToken = createSpy().and.returnValue(
+ of({ tokenId: 'testTokenId', expiresIn: '300' })
+ );
+}
+
class MockGlobalMessageService {
add = createSpy().and.stub();
remove = createSpy().and.stub();
@@ -24,6 +31,7 @@ class MockGlobalMessageService {
describe('VerificationTokenFormComponentService', () => {
let service: VerificationTokenFormComponentService;
let authService: AuthService;
+ let facade: VerificationTokenFacade;
beforeEach(
waitForAsync(() => {
@@ -39,6 +47,10 @@ describe('VerificationTokenFormComponentService', () => {
VerificationTokenFormComponentService,
{ provide: AuthService, useClass: MockAuthService },
{ provide: GlobalMessageService, useClass: MockGlobalMessageService },
+ {
+ provide: VerificationTokenFacade,
+ useClass: MockVerificationTokenFacade,
+ },
],
}).compileComponents();
})
@@ -47,12 +59,26 @@ describe('VerificationTokenFormComponentService', () => {
beforeEach(() => {
service = TestBed.inject(VerificationTokenFormComponentService);
authService = TestBed.inject(AuthService);
+ facade = TestBed.inject(VerificationTokenFacade);
});
it('should create service', () => {
expect(service).toBeTruthy();
});
+ it('should sent otp', () => {
+ const loginId = 'example@example.com';
+ const password = 'pw4all';
+ const purpose = 'LOGIN';
+
+ service.sentOTP(loginId, password, purpose);
+ expect(facade.createVerificationToken).toHaveBeenCalledWith({
+ loginId,
+ password,
+ purpose,
+ });
+ });
+
describe('login', () => {
const tokenId = '';
const tokenCode = 'XG5tyu';
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
index 748bee81238..1333c17df3e 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
@@ -72,7 +72,7 @@ export class VerificationTokenFormComponentService {
}
sentOTP(loginId: string, password: string, purpose: string) {
- this.verificationTokenFacade.createVerificationToken({
+ return this.verificationTokenFacade.createVerificationToken({
loginId,
password,
purpose,
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts
index 1a987f736f2..a5f27eb29e1 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.spec.ts
@@ -1,4 +1,9 @@
-import { DebugElement, Pipe, PipeTransform } from '@angular/core';
+import {
+ ChangeDetectorRef,
+ DebugElement,
+ Pipe,
+ PipeTransform,
+} from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import {
ReactiveFormsModule,
@@ -13,7 +18,8 @@ import {
LaunchDialogService,
SpinnerModule,
} from '@spartacus/storefront';
-import { BehaviorSubject } from 'rxjs';
+import { BehaviorSubject, of } from 'rxjs';
+import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from '../user-account-constants';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
import { VerificationTokenFormComponent } from './verification-token-form.component';
import createSpy = jasmine.createSpy;
@@ -28,7 +34,9 @@ class MockFormComponentService
});
isUpdating$ = isBusySubject;
login = createSpy().and.stub();
- sentOTP = createSpy().and.stub();
+ sentOTP = createSpy().and.returnValue(
+ of({ tokenId: 'testTokenId', expiresIn: '300' })
+ );
displayMessage = createSpy('displayMessage').and.stub();
}
@Pipe({
@@ -69,6 +77,7 @@ describe('VerificationTokenFormComponent', () => {
provide: LaunchDialogService,
useClass: MockLaunchDialogService,
},
+ ChangeDetectorRef,
],
}).compileComponents();
})
@@ -145,15 +154,24 @@ describe('VerificationTokenFormComponent', () => {
expect(launchDialogService.openDialogAndSubscribe).toHaveBeenCalled();
});
- it('should resend otp token', () => {
- component.resendOTP();
- expect(service.sentOTP).toHaveBeenCalled();
- });
+ it('should resend OTP', () => {
+ component.target = 'example@example.com';
+ component.password = 'password';
+ spyOn(component, 'startWaitTimeInterval');
- it('should resend otp token', () => {
component.resendOTP();
- expect(service.sentOTP).toHaveBeenCalled();
- expect(service.displayMessage).toHaveBeenCalled();
+
+ expect(component.isResendDisabled).toBe(true);
+ expect(component.waitTime).toBe(60);
+ expect(component.startWaitTimeInterval).toHaveBeenCalled();
+ expect(service.sentOTP).toHaveBeenCalledWith(
+ 'example@example.com',
+ 'password',
+ ONE_TIME_PASSWORD_LOGIN_PURPOSE
+ );
+ expect(service.displayMessage).toHaveBeenCalledWith(
+ 'example@example.com'
+ );
});
});
});
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index fae0e43f497..1e3c659b3a4 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -16,6 +16,7 @@ import {
import { UntypedFormGroup } from '@angular/forms';
import { LAUNCH_CALLER, LaunchDialogService } from '@spartacus/storefront';
import { Observable } from 'rxjs';
+import { VerificationToken } from '../../root/model';
import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from '../user-account-constants';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
@@ -75,12 +76,12 @@ export class VerificationTokenFormComponent implements OnInit {
this.isResendDisabled = true;
this.waitTime = 60;
this.startWaitTimeInterval();
- this.service.sentOTP(
- this.target,
- this.password,
- ONE_TIME_PASSWORD_LOGIN_PURPOSE
- );
- this.service.displayMessage(this.target);
+ this.service
+ .sentOTP(this.target, this.password, ONE_TIME_PASSWORD_LOGIN_PURPOSE)
+ .subscribe({
+ next: (result: VerificationToken) => (this.tokenId = result.tokenId),
+ complete: () => this.service.displayMessage(this.target),
+ });
}
startWaitTimeInterval(): void {
From 05b20411f9e626aead19dc9f9ffdf335278de623 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 23 Apr 2024 16:47:23 +0800
Subject: [PATCH 25/66] CXSPA-6689: bug fixed
---
.../verification-token-form.component.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index 1e3c659b3a4..d38d16c046b 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -64,8 +64,9 @@ export class VerificationTokenFormComponent implements OnInit {
},
'verifyToken'
);
- this.startWaitTimeInterval();
}
+ this.startWaitTimeInterval();
+ this.service.displayMessage(this.target);
}
onSubmit(): void {
From 8ef7f268a5a0a2cc4f7ce4204461fedc2ee3660c Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 23 Apr 2024 17:36:49 +0800
Subject: [PATCH 26/66] CXSPA-6689: code refined
---
projects/core/src/auth/user-auth/facade/auth.service.ts | 1 -
.../core/src/auth/user-auth/services/auth-redirect.service.ts | 2 --
2 files changed, 3 deletions(-)
diff --git a/projects/core/src/auth/user-auth/facade/auth.service.ts b/projects/core/src/auth/user-auth/facade/auth.service.ts
index eb4a3e56baa..8d75fe86f49 100644
--- a/projects/core/src/auth/user-auth/facade/auth.service.ts
+++ b/projects/core/src/auth/user-auth/facade/auth.service.ts
@@ -129,7 +129,6 @@ export class AuthService {
this.store.dispatch(new AuthActions.Login());
- debugger;
this.authRedirectService.redirect();
} catch {}
}
diff --git a/projects/core/src/auth/user-auth/services/auth-redirect.service.ts b/projects/core/src/auth/user-auth/services/auth-redirect.service.ts
index 587f0342ac3..09829e8173b 100644
--- a/projects/core/src/auth/user-auth/services/auth-redirect.service.ts
+++ b/projects/core/src/auth/user-auth/services/auth-redirect.service.ts
@@ -65,10 +65,8 @@ export class AuthRedirectService implements OnDestroy {
.pipe(take(1))
.subscribe((redirectUrl) => {
if (redirectUrl === undefined) {
- debugger;
this.routing.go('/');
} else {
- debugger;
this.routing.goByUrl(redirectUrl);
}
this.clearRedirectUrl();
From 0b5764694260152e924ec90798e85299d05c5f16 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Wed, 24 Apr 2024 10:19:12 +0800
Subject: [PATCH 27/66] refine lighthouse check
---
.../verification-token-form/verification-token-form.module.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
index 6a332aa2507..38dee64cb86 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.module.ts
@@ -25,11 +25,12 @@ import {
KeyboardFocusModule,
SpinnerModule,
} from '@spartacus/storefront';
+
import { defaultVerificationTokenLayoutConfig } from './default-verification-token-layout.config';
import { VerificationTokenDialogComponent } from './verification-token-dialog.component';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
import { VerificationTokenFormComponent } from './verification-token-form.component';
-import { VerificationTokenFacade } from '../../root/facade';
+import { VerificationTokenFacade } from '@spartacus/user/account/root';
@NgModule({
imports: [
From 9ad0965dc443b75591a0f4c80a5f9c602ac10d86 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Wed, 24 Apr 2024 11:04:55 +0800
Subject: [PATCH 28/66] refine code
---
.../verification-token-form.component.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index d38d16c046b..6782e15af21 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -16,9 +16,10 @@ import {
import { UntypedFormGroup } from '@angular/forms';
import { LAUNCH_CALLER, LaunchDialogService } from '@spartacus/storefront';
import { Observable } from 'rxjs';
-import { VerificationToken } from '../../root/model';
+
import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from '../user-account-constants';
import { VerificationTokenFormComponentService } from './verification-token-form-component.service';
+import { VerificationToken } from '@spartacus/user/account/root';
@Component({
selector: 'cx-verification-token-form',
From 33eb70ff12287fe4f73b00813433d761e99ec7e8 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Wed, 24 Apr 2024 11:10:14 +0800
Subject: [PATCH 29/66] CXSPA-6689: code refined
---
.../model/{augmented-core.model.ts => augmented.model.ts} | 0
feature-libs/user/account/root/model/index.ts | 2 +-
.../removed-public-api-deprecation.ts | 3 +--
.../src/migrations/4_0/rename-symbol/rename-symbol.ts | 7 -------
4 files changed, 2 insertions(+), 10 deletions(-)
rename feature-libs/user/account/root/model/{augmented-core.model.ts => augmented.model.ts} (100%)
diff --git a/feature-libs/user/account/root/model/augmented-core.model.ts b/feature-libs/user/account/root/model/augmented.model.ts
similarity index 100%
rename from feature-libs/user/account/root/model/augmented-core.model.ts
rename to feature-libs/user/account/root/model/augmented.model.ts
diff --git a/feature-libs/user/account/root/model/index.ts b/feature-libs/user/account/root/model/index.ts
index beafee30f70..d0ddad8c795 100644
--- a/feature-libs/user/account/root/model/index.ts
+++ b/feature-libs/user/account/root/model/index.ts
@@ -4,6 +4,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
-export * from './augmented-core.model';
+export * from './augmented.model';
export * from './otp-login.model';
export * from './user.model';
diff --git a/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts b/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts
index fdfababa283..63a607f8c3d 100644
--- a/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts
+++ b/projects/schematics/src/migrations/4_0/removed-public-api-deprecations/removed-public-api-deprecation.ts
@@ -47,7 +47,6 @@ import {
OCC_USER_ACCOUNT_ADAPTER,
OCC_USER_ADAPTER,
OCC_USER_PROFILE_ADAPTER,
- OTP_LOGIN_FORM_MODULE,
PAGE_EVENT_BUILDER,
PAGE_EVENT_MODULE,
PRODUCT_VARIANTS_MODULE,
@@ -370,7 +369,7 @@ export const REMOVED_PUBLIC_API_DATA: DeprecatedNode[] = [
{
node: USER_COMPONENT_MODULE,
importPath: SPARTACUS_STOREFRONTLIB,
- comment: `'${USER_COMPONENT_MODULE}' - Following module imports '${LOGIN_MODULE}', '${LOGIN_FORM_MODULE}','${OTP_LOGIN_FORM_MODULE}', '${LOGIN_REGISTER_MODULE}', '${REGISTER_COMPONENT_MODULE}' were removed. Those modules are now part of ${SPARTACUS_USER}.`,
+ comment: `'${USER_COMPONENT_MODULE}' - Following module imports '${LOGIN_MODULE}', '${LOGIN_FORM_MODULE}', '${LOGIN_REGISTER_MODULE}', '${REGISTER_COMPONENT_MODULE}' were removed. Those modules are now part of ${SPARTACUS_USER}.`,
},
// projects/storefrontlib/cms-components/myaccount/close-account/components/close-account-modal/close-account-modal.component.ts
{
diff --git a/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts b/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts
index 01a1f5a5266..a0e599ff716 100644
--- a/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts
+++ b/projects/schematics/src/migrations/4_0/rename-symbol/rename-symbol.ts
@@ -44,7 +44,6 @@ import {
LOGIN_REGISTER_MODULE,
OCC_ASM_ADAPTER,
ORDER_ENTRY,
- OTP_LOGIN_FORM_MODULE,
PERMISSION_ROUTING_CONFIG,
PERSONALIZATION_ACTION,
PERSONALIZATION_CONFIG,
@@ -419,12 +418,6 @@ export const RENAMED_SYMBOLS_DATA: RenamedSymbol[] = [
previousImportPath: SPARTACUS_STOREFRONTLIB,
newImportPath: SPARTACUS_USER_ACCOUNT_COMPONENTS,
},
- // projects/storefrontlib/cms-components/user/otp-login-form/otp-login-form.module.ts
- {
- previousNode: OTP_LOGIN_FORM_MODULE,
- previousImportPath: SPARTACUS_STOREFRONTLIB,
- newImportPath: SPARTACUS_USER_ACCOUNT_COMPONENTS,
- },
// projects/storefrontlib/cms-components/user/login-register/login-register.module.ts
{
previousNode: LOGIN_REGISTER_MODULE,
From 5421af99625df8a262cc42456fa6c34c96de3e52 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Wed, 24 Apr 2024 11:38:11 +0800
Subject: [PATCH 30/66] add e2e test
---
.../core/facade/verification-token.service.ts | 7 +-
.../user_access/otp-login.e2e-flaky.cy.ts | 90 ++++++++++++++++---
2 files changed, 83 insertions(+), 14 deletions(-)
diff --git a/feature-libs/user/account/core/facade/verification-token.service.ts b/feature-libs/user/account/core/facade/verification-token.service.ts
index 6238ae2ade9..f7331b24cc1 100644
--- a/feature-libs/user/account/core/facade/verification-token.service.ts
+++ b/feature-libs/user/account/core/facade/verification-token.service.ts
@@ -6,9 +6,12 @@
import { Injectable } from '@angular/core';
import { Command, CommandService } from '@spartacus/core';
+import {
+ LoginForm,
+ VerificationToken,
+ VerificationTokenFacade,
+} from '@spartacus/user/account/root';
import { Observable } from 'rxjs';
-import { VerificationTokenFacade } from '../../root/facade';
-import { LoginForm, VerificationToken } from '../../root/model';
import { UserAccountConnector } from '../connectors';
@Injectable()
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
index 2ec343fad50..bc1c54ce623 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
@@ -11,17 +11,17 @@ import { interceptPost } from '../../../support/utils/intercept';
export function listenForCreateVerificationToken(): string {
return interceptPost(
'createVerificationToken',
- 'users/anonymous/verificationToken'
+ '/users/anonymous/verificationToken?*'
);
}
describe('OTP Login', () => {
viewportContext(['mobile'], () => {
- before(() => {
- cy.visit('/login');
- });
-
describe('Create OTP', () => {
+ beforeEach(() => {
+ cy.visit('/login');
+ });
+
it('should be able to create a new OTP by customer click Sign In button (CXSPA-6672)', () => {
const user = login.registerUserFromLoginPage();
listenForCreateVerificationToken();
@@ -32,19 +32,69 @@ describe('OTP Login', () => {
cy.get('[formcontrolname="password"]').clear().type(user.password);
cy.get('button[type=submit]').click();
});
- // cy.wait('@createVerificationToken')
- // .its('response.statusCode')
- // .should('eq', 201);
+
+ cy.wait('@createVerificationToken')
+ .its('response.statusCode')
+ .should('eq', 201);
cy.get('cx-verification-token-form').should('exist');
cy.get('cx-verification-token-form').should('be.visible');
- cy.get(login.userGreetSelector).should('not.exist');
+
+ cy.log('The email being sent is asynchronous, so waiting 10s');
+ cy.wait(10000);
+
+ const API_ENDPOINT =
+ 'http://mail-ccv2.westeurope.azurecontainer.io:8025/api/v2/search';
+ cy.request({
+ method: 'GET',
+ url:
+ API_ENDPOINT + '?query=' + user.email + '&kind=to&start=0&limit=2',
+ }).then((response) => {
+ const subject =
+ '[Spartacus Electronics Site] Login Verification Code';
+ const verificationCodeEmailStartText =
+ 'Please use the following verification code to log in Spartacus Electronics Site:
';
+ const lableP = '';
+
+ const items = response.body.items;
+ const emailBody =
+ subject === items[0].Content.Headers.Subject[0]
+ ? items[0].Content.Body
+ : items[1].Content.Body;
+
+ const verificationCodeEmailStartIndex =
+ emailBody.indexOf(verificationCodeEmailStartText) +
+ verificationCodeEmailStartText.length;
+ const verificationCodeStartIndex =
+ emailBody.indexOf(lableP, verificationCodeEmailStartIndex) +
+ lableP.length;
+ const verificationCode = emailBody.substring(
+ verificationCodeStartIndex,
+ verificationCodeStartIndex + 8
+ );
+ cy.log('Extracted verification code: ' + verificationCode);
+
+ login.listenForTokenAuthenticationRequest();
+ cy.get('cx-verification-token-form form').within(() => {
+ cy.get('[formcontrolname="tokenCode"]')
+ .clear()
+ .type(verificationCode);
+ cy.get('button[type=submit]').click();
+ });
+ cy.wait('@tokenAuthentication')
+ .its('response.statusCode')
+ .should('eq', 200);
+ });
});
});
describe('Failed to Create OTP', () => {
+ beforeEach(() => {
+ cy.visit('/login');
+ });
it('should be not able to create OTP with invalid user data (CXSPA-6672)', () => {
listenForCreateVerificationToken();
+
cy.get('cx-otp-login-form form').within(() => {
cy.get('[formcontrolname="userId"]')
.clear()
@@ -52,14 +102,30 @@ describe('OTP Login', () => {
cy.get('[formcontrolname="password"]').clear().type('1234');
cy.get('button[type=submit]').click();
});
- // cy.wait('@createVerificationToken')
- // .its('response.statusCode')
- // .should('eq', 400);
+
+ cy.wait('@createVerificationToken')
+ .its('response.statusCode')
+ .should('eq', 400);
cy.get('cx-global-message').within(() => {
cy.get('span').contains('Email is not valid.');
});
});
});
+
+ describe('Chould can return back to login page from verify code page', () => {
+ it('should be not able to create OTP with invalid user data (CXSPA-6672)', () => {
+ cy.visit('/login/verify-token');
+
+ cy.get('cx-verification-token-form').should('exist');
+
+ cy.get('cx-verification-token-form form').within(() => {
+ cy.get('.btn-secondary').click();
+ });
+
+ cy.get('cx-verification-token-form').should('not.exist');
+ cy.get('cx-otp-login-form form').should('exist');
+ });
+ });
});
});
From de1c0200a64d2e61e93a67450f3a4fc82ad8df49 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Thu, 25 Apr 2024 11:31:39 +0800
Subject: [PATCH 31/66] add accessibility e2e test
---
.../otp-login-tabbing.e2e-spec-flaky.cy.ts | 64 +++++++++++++++++++
.../user_access/otp-login.e2e-flaky.cy.ts | 6 +-
.../accessibility/tabbing-order.config.ts | 14 ++++
3 files changed, 81 insertions(+), 3 deletions(-)
create mode 100644 projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
new file mode 100644
index 00000000000..ecc7b977ecc
--- /dev/null
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
@@ -0,0 +1,64 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import { verifyTabbingOrder } from '../../helpers/accessibility/tabbing-order';
+import { tabbingOrderConfig as config } from '../../helpers/accessibility/tabbing-order.config';
+
+describe('Tabbing order for OTP login', () => {
+ before(() => {
+ cy.window().then((win) => win.sessionStorage.clear());
+ });
+
+ describe('OTP login', () => {
+ context('OTP Login page', () => {
+ beforeEach(() => {
+ cy.visit('/login');
+ cy.get('cx-otp-login-form').should('exist');
+ cy.get('cx-otp-login-form form').should('exist');
+ });
+ it('should allow to navigate with tab key for otp login form(empty form)', () => {
+ verifyTabbingOrder('cx-otp-login-form', config.otpLogin);
+ });
+
+ it('should allow to navigate with tab key for otp login form(filled out form)', () => {
+ const { email: username, password } = user;
+ cy.get('cx-otp-login-form form').within(() => {
+ cy.get('[formcontrolname="userId"]').clear().type(username);
+ cy.get('[formcontrolname="password"]').clear().type(password);
+ });
+ verifyTabbingOrder('cx-otp-login-form', config.otpLogin);
+ });
+ });
+
+ // it('should allow to navigate with tab key for otp verification token form', () => {
+ // cy.visit('/login/verify-token');
+ // cy.get('cx-verification-token-form').should('exist');
+ // cy.get('cx-verification-token-form form').should('exist');
+
+ // verifyTabbingOrder(
+ // 'cx-verification-token-form',
+ // config.verifyToken
+ // );
+ // });
+
+ // it('should allow to navigate with tab key for otp verification token dialog', () => {
+ // cy.visit('/login/verify-token');
+ // cy.get('cx-verification-token-form form').within(() => {
+ // cy.get('.right-text .btn-link')
+ // .first()
+ // .contains("Didn't receive the code?")
+ // .click()
+ // .then(() => {
+ // cy.get('cx-verification-token-dialog').should('exist');
+
+ // verifyTabbingOrder(
+ // 'cx-verification-token-dialog',
+ // config.verifyTokenDialog
+ // );
+ // });
+ // });
+ // });
+ });
+});
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
index bc1c54ce623..daaaec25d73 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
@@ -113,13 +113,13 @@ describe('OTP Login', () => {
});
});
- describe('Chould can return back to login page from verify code page', () => {
- it('should be not able to create OTP with invalid user data (CXSPA-6672)', () => {
+ describe('Verification token', () => {
+ it('Should can return back to otp login page from verification token page (CXSPA-6689)', () => {
cy.visit('/login/verify-token');
cy.get('cx-verification-token-form').should('exist');
- cy.get('cx-verification-token-form form').within(() => {
+ cy.get('cx-verification-token-form form a').within(() => {
cy.get('.btn-secondary').click();
});
diff --git a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts
index 810cc8bc5d1..b2b527c4cdc 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts
@@ -2509,4 +2509,18 @@ export const tabbingOrderConfig: TabbingOrderConfig = {
// uncomment the following line to test againt S4,S7
{ type: TabbingOrderTypes.BUTTON, value: ' Assign to Customer ' },
],
+ otpLogin: [
+ { value: 'userId', type: TabbingOrderTypes.FORM_FIELD },
+ { value: 'password', type: TabbingOrderTypes.FORM_FIELD },
+ { value: '', type: TabbingOrderTypes.LINK },
+ { value: 'Forgot password?', type: TabbingOrderTypes.LINK },
+ { value: 'Sign In', type: TabbingOrderTypes.BUTTON },
+ ],
+ verifyToken: [
+ { value: 'tokenCode', type: TabbingOrderTypes.FORM_FIELD },
+ { value: "Didn't receive the code?", type: TabbingOrderTypes.LINK },
+ { value: 'Verify', type: TabbingOrderTypes.BUTTON },
+ { value: 'Back', type: TabbingOrderTypes.LINK },
+ ],
+ verifyTokenDialog: [{ value: 'ok', type: TabbingOrderTypes.BUTTON }],
};
From e697cae511675699239f8e380ff40e354a78d0c6 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Thu, 25 Apr 2024 11:34:49 +0800
Subject: [PATCH 32/66] remove unused code
---
.../otp-login-tabbing.e2e-spec-flaky.cy.ts | 30 +------------------
.../accessibility/tabbing-order.config.ts | 7 -----
2 files changed, 1 insertion(+), 36 deletions(-)
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
index ecc7b977ecc..22230805eae 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
@@ -5,6 +5,7 @@
*/
import { verifyTabbingOrder } from '../../helpers/accessibility/tabbing-order';
import { tabbingOrderConfig as config } from '../../helpers/accessibility/tabbing-order.config';
+import { user } from '../../sample-data/checkout-flow';
describe('Tabbing order for OTP login', () => {
before(() => {
@@ -31,34 +32,5 @@ describe('Tabbing order for OTP login', () => {
verifyTabbingOrder('cx-otp-login-form', config.otpLogin);
});
});
-
- // it('should allow to navigate with tab key for otp verification token form', () => {
- // cy.visit('/login/verify-token');
- // cy.get('cx-verification-token-form').should('exist');
- // cy.get('cx-verification-token-form form').should('exist');
-
- // verifyTabbingOrder(
- // 'cx-verification-token-form',
- // config.verifyToken
- // );
- // });
-
- // it('should allow to navigate with tab key for otp verification token dialog', () => {
- // cy.visit('/login/verify-token');
- // cy.get('cx-verification-token-form form').within(() => {
- // cy.get('.right-text .btn-link')
- // .first()
- // .contains("Didn't receive the code?")
- // .click()
- // .then(() => {
- // cy.get('cx-verification-token-dialog').should('exist');
-
- // verifyTabbingOrder(
- // 'cx-verification-token-dialog',
- // config.verifyTokenDialog
- // );
- // });
- // });
- // });
});
});
diff --git a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts
index b2b527c4cdc..79aed683e9c 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts
@@ -2516,11 +2516,4 @@ export const tabbingOrderConfig: TabbingOrderConfig = {
{ value: 'Forgot password?', type: TabbingOrderTypes.LINK },
{ value: 'Sign In', type: TabbingOrderTypes.BUTTON },
],
- verifyToken: [
- { value: 'tokenCode', type: TabbingOrderTypes.FORM_FIELD },
- { value: "Didn't receive the code?", type: TabbingOrderTypes.LINK },
- { value: 'Verify', type: TabbingOrderTypes.BUTTON },
- { value: 'Back', type: TabbingOrderTypes.LINK },
- ],
- verifyTokenDialog: [{ value: 'ok', type: TabbingOrderTypes.BUTTON }],
};
From 8615672029b78f31dea7f67bded6031a5c0f6b69 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Thu, 25 Apr 2024 13:30:03 +0800
Subject: [PATCH 33/66] CXSPA-6689: bug fixed
---
.../verification-token-form.component.html | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
index 080e1a5723f..6917e916568 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
@@ -22,6 +22,7 @@
From ec2a0adc9c3f2e4bb652b729df51f327cfa9d4a1 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Thu, 25 Apr 2024 13:39:05 +0800
Subject: [PATCH 34/66] CXSPA-6689: bug fixed
---
.../verification-token-form.component.html | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
index 6917e916568..6068e49dc37 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
@@ -22,7 +22,7 @@
From 005469b4dfe1f139568271a853f6ff1123589947 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Thu, 25 Apr 2024 14:03:35 +0800
Subject: [PATCH 35/66] refine e2e
---
.../accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts | 8 ++++++++
.../e2e/regression/user_access/otp-login.e2e-flaky.cy.ts | 4 ++--
.../cypress/helpers/accessibility/tabbing-order.config.ts | 7 +++++++
3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
index 22230805eae..25c46944b24 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
@@ -32,5 +32,13 @@ describe('Tabbing order for OTP login', () => {
verifyTabbingOrder('cx-otp-login-form', config.otpLogin);
});
});
+
+ it('should allow to navigate with tab key for otp verification token form', () => {
+ cy.visit('/login/verify-token');
+ cy.get('cx-verification-token-form').should('exist');
+ cy.get('cx-verification-token-form form').should('exist');
+
+ verifyTabbingOrder('cx-verification-token-form', config.verifyToken);
+ });
});
});
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
index daaaec25d73..3f0359bf969 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
@@ -119,8 +119,8 @@ describe('OTP Login', () => {
cy.get('cx-verification-token-form').should('exist');
- cy.get('cx-verification-token-form form a').within(() => {
- cy.get('.btn-secondary').click();
+ cy.get('cx-verification-token-form form').within(() => {
+ cy.get('div.verify-container a').click();
});
cy.get('cx-verification-token-form').should('not.exist');
diff --git a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts
index 79aed683e9c..93d8467f38c 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order.config.ts
@@ -2516,4 +2516,11 @@ export const tabbingOrderConfig: TabbingOrderConfig = {
{ value: 'Forgot password?', type: TabbingOrderTypes.LINK },
{ value: 'Sign In', type: TabbingOrderTypes.BUTTON },
],
+ verifyToken: [
+ { value: 'tokenCode', type: TabbingOrderTypes.FORM_FIELD },
+ { value: 'Resend', type: TabbingOrderTypes.LINK },
+ { value: "Didn't receive the code?", type: TabbingOrderTypes.LINK },
+ { value: 'Verify', type: TabbingOrderTypes.BUTTON },
+ { value: 'Back', type: TabbingOrderTypes.LINK },
+ ],
};
From d547263bc832a80f5300c594d70cf48a11bfa592 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Thu, 25 Apr 2024 14:25:07 +0800
Subject: [PATCH 36/66] fix comments
---
.../e2e/regression/user_access/otp-login.e2e-flaky.cy.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
index 3f0359bf969..9ccda504aa0 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
@@ -92,7 +92,7 @@ describe('OTP Login', () => {
beforeEach(() => {
cy.visit('/login');
});
- it('should be not able to create OTP with invalid user data (CXSPA-6672)', () => {
+ it('should not be able to create OTP with invalid user data (CXSPA-6672)', () => {
listenForCreateVerificationToken();
cy.get('cx-otp-login-form form').within(() => {
@@ -114,7 +114,7 @@ describe('OTP Login', () => {
});
describe('Verification token', () => {
- it('Should can return back to otp login page from verification token page (CXSPA-6689)', () => {
+ it('Should go back to login page when click back button (CXSPA-6689)', () => {
cy.visit('/login/verify-token');
cy.get('cx-verification-token-form').should('exist');
From f685db2c070de7758b4817e92a63f88021b460ef Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Thu, 25 Apr 2024 15:40:16 +0800
Subject: [PATCH 37/66] CXSPA: code refined for code review
---
.../assets/translations/en/userAccount.json | 4 +-
.../verification-token-dialog.component.html | 71 +++++++++----------
...ation-token-form-component.service.spec.ts | 2 +-
...rification-token-form-component.service.ts | 8 ++-
.../verification-token-form.component.spec.ts | 4 +-
.../verification-token-form.component.ts | 8 ++-
6 files changed, 50 insertions(+), 47 deletions(-)
diff --git a/feature-libs/user/account/assets/translations/en/userAccount.json b/feature-libs/user/account/assets/translations/en/userAccount.json
index 407ea159b5e..aaacc9defa1 100644
--- a/feature-libs/user/account/assets/translations/en/userAccount.json
+++ b/feature-libs/user/account/assets/translations/en/userAccount.json
@@ -16,7 +16,7 @@
"wrongEmailFormat": "This is not a valid email format."
},
"verificationTokenForm": {
- "sentOTP": "Verification code has been sent to {{target}}. Please enter the code.",
+ "createVerificationToken": "Verification code has been sent to {{target}}. Please enter the code.",
"sendRateLime": "in {{waitTime}}s",
"resend": "Resend",
"verificationCode": {
@@ -42,4 +42,4 @@
"myAccountV2User": {
"signOut": "Sign Out"
}
-}
+}
\ No newline at end of file
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html
index 6888e267c40..f69cf21c9c2 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html
@@ -5,48 +5,45 @@
>
-
-
-
+
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
index 6068e49dc37..fcc70b4b021 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
@@ -18,8 +18,8 @@
-
-
+
+
-
+
Date: Tue, 30 Apr 2024 14:33:57 +0800
Subject: [PATCH 46/66] CXSPA-6689: translation fixed
---
.../assets/translations/en/userAccount.json | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/feature-libs/user/account/assets/translations/en/userAccount.json b/feature-libs/user/account/assets/translations/en/userAccount.json
index 16d5ed4c707..db156b21dc7 100644
--- a/feature-libs/user/account/assets/translations/en/userAccount.json
+++ b/feature-libs/user/account/assets/translations/en/userAccount.json
@@ -16,12 +16,12 @@
"wrongEmailFormat": "This is not a valid email format."
},
"verificationTokenForm": {
- "createVerificationToken": "Verification code has been sent to {{target}}. Please enter the code.",
+ "createVerificationToken": "Verification code sent to {{target}}. Please check and enter the code.",
"sendRateLime": "in {{waitTime}}s",
"resend": "Resend",
"verificationCode": {
"label": "Verification Code",
- "placeholder": "Enter Verification Code"
+ "placeholder": "Enter verification code"
},
"noReceiveCode": "Didn't receive the code?",
"verify": "Verify",
@@ -29,11 +29,11 @@
},
"verificationTokenDialog": {
"title": "Don't receive the code",
- "noReceiveCode": "If you have not received the code, please try:",
- "contentLine1": "1. Wait for a while as the email may be delayed.",
- "contentLine2": "2. Check if it is in the junk mail.",
- "contentLine3": "3. Go back and make sure your email address and password is correct.",
- "ok": "ok"
+ "noReceiveCode": "If you don't receive the code, the reasons might be:",
+ "contentLine1": "1. The email hasn't come yet.",
+ "contentLine2": "2. The received email has been treated as junk email.",
+ "contentLine3": "3. Either the email address or the password you entered is incorrect.",
+ "ok": "Ok"
},
"miniLogin": {
"userGreeting": "Hi, {{name}}",
@@ -45,8 +45,8 @@
"httpHandlers": {
"validationErrors": {
"invalid": {
- "loginId": "Email is not valid."
+ "loginId": "Invalid email address."
}
}
}
-}
+}
\ No newline at end of file
From 7361b4eaa24546a007e9b19a7a9f0dc80928a77b Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 30 Apr 2024 14:36:13 +0800
Subject: [PATCH 47/66] CXSPA-6689: code refined
---
.../user/account/assets/translations/en/userAccount.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/feature-libs/user/account/assets/translations/en/userAccount.json b/feature-libs/user/account/assets/translations/en/userAccount.json
index db156b21dc7..3bc640f4732 100644
--- a/feature-libs/user/account/assets/translations/en/userAccount.json
+++ b/feature-libs/user/account/assets/translations/en/userAccount.json
@@ -21,7 +21,7 @@
"resend": "Resend",
"verificationCode": {
"label": "Verification Code",
- "placeholder": "Enter verification code"
+ "placeholder": "Enter Verification Code"
},
"noReceiveCode": "Didn't receive the code?",
"verify": "Verify",
From ed371cfb8339e35698c300e9493c89e4de4fc224 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Tue, 30 Apr 2024 14:39:24 +0800
Subject: [PATCH 48/66] revert translation change
---
.../user/account/assets/translations/en/userAccount.json | 7 -------
.../user/account/assets/translations/translations.ts | 1 -
.../user/account/core/connectors/user-account.adapter.ts | 1 +
.../user/account/core/connectors/user-account.connector.ts | 1 +
4 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/feature-libs/user/account/assets/translations/en/userAccount.json b/feature-libs/user/account/assets/translations/en/userAccount.json
index 16d5ed4c707..b5330a145fd 100644
--- a/feature-libs/user/account/assets/translations/en/userAccount.json
+++ b/feature-libs/user/account/assets/translations/en/userAccount.json
@@ -41,12 +41,5 @@
},
"myAccountV2User": {
"signOut": "Sign Out"
- },
- "httpHandlers": {
- "validationErrors": {
- "invalid": {
- "loginId": "Email is not valid."
- }
- }
}
}
diff --git a/feature-libs/user/account/assets/translations/translations.ts b/feature-libs/user/account/assets/translations/translations.ts
index 536e182b16e..a4c1ca04cd6 100644
--- a/feature-libs/user/account/assets/translations/translations.ts
+++ b/feature-libs/user/account/assets/translations/translations.ts
@@ -18,6 +18,5 @@ export const userAccountTranslationChunksConfig: TranslationChunksConfig = {
'verificationTokenDialog',
'miniLogin',
'myAccountV2User',
- 'httpHandlers',
],
};
diff --git a/feature-libs/user/account/core/connectors/user-account.adapter.ts b/feature-libs/user/account/core/connectors/user-account.adapter.ts
index 915a9791bbc..36a8f48fa02 100644
--- a/feature-libs/user/account/core/connectors/user-account.adapter.ts
+++ b/feature-libs/user/account/core/connectors/user-account.adapter.ts
@@ -13,6 +13,7 @@ import { Observable } from 'rxjs';
export abstract class UserAccountAdapter {
abstract load(userId: string): Observable;
+
abstract createVerificationToken(
verificationTokenCreation: VerificationTokenCreation
): Observable;
diff --git a/feature-libs/user/account/core/connectors/user-account.connector.ts b/feature-libs/user/account/core/connectors/user-account.connector.ts
index 1cd352ac390..1cf25424159 100644
--- a/feature-libs/user/account/core/connectors/user-account.connector.ts
+++ b/feature-libs/user/account/core/connectors/user-account.connector.ts
@@ -20,6 +20,7 @@ export class UserAccountConnector {
get(userId: string): Observable {
return this.adapter.load(userId);
}
+
createVerificationToken(
verificationTokenCreation: VerificationTokenCreation
): Observable {
From 94d36cea8d33f6e5af28b730aad6ea735ea75698 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Tue, 30 Apr 2024 14:47:12 +0800
Subject: [PATCH 49/66] revert translation change
---
projects/assets/src/translations/en/common.json | 3 ++-
.../e2e/regression/user_access/otp-login.e2e-flaky.cy.ts | 6 +++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/projects/assets/src/translations/en/common.json b/projects/assets/src/translations/en/common.json
index cfc837d9428..3d5452257e6 100644
--- a/projects/assets/src/translations/en/common.json
+++ b/projects/assets/src/translations/en/common.json
@@ -132,7 +132,8 @@
"password": "Password entered is not valid.",
"uid": "UID is not valid.",
"code": "Code is not valid.",
- "email": "Email is not valid."
+ "email": "Email is not valid.",
+ "loginId": "Email is not valid."
}
},
"cartNotFound": "Cart not found.",
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
index 9ccda504aa0..bab8b25098e 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
@@ -43,12 +43,12 @@ describe('OTP Login', () => {
cy.log('The email being sent is asynchronous, so waiting 10s');
cy.wait(10000);
- const API_ENDPOINT =
- 'http://mail-ccv2.westeurope.azurecontainer.io:8025/api/v2/search';
cy.request({
method: 'GET',
url:
- API_ENDPOINT + '?query=' + user.email + '&kind=to&start=0&limit=2',
+ 'http://mail-ccv2.westeurope.azurecontainer.io:8025/api/v2/search?query=' +
+ user.email +
+ '&kind=to&start=0&limit=2',
}).then((response) => {
const subject =
'[Spartacus Electronics Site] Login Verification Code';
From 44cfba82b655c7807e8a3271e8adb32f4937cd6c Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Tue, 30 Apr 2024 15:09:47 +0800
Subject: [PATCH 50/66] prettier:fix
---
.../user/account/assets/translations/en/userAccount.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/feature-libs/user/account/assets/translations/en/userAccount.json b/feature-libs/user/account/assets/translations/en/userAccount.json
index 232668aa50f..9f0280a8c35 100644
--- a/feature-libs/user/account/assets/translations/en/userAccount.json
+++ b/feature-libs/user/account/assets/translations/en/userAccount.json
@@ -42,4 +42,4 @@
"myAccountV2User": {
"signOut": "Sign Out"
}
-}
\ No newline at end of file
+}
From 0b989d4439b6bcebbf30d97b09ec74eaf7cfbb61 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 7 May 2024 14:19:34 +0800
Subject: [PATCH 51/66] CXSPA-6689: css fixed
---
.../user/account/styles/_verification-token-form.scss | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/feature-libs/user/account/styles/_verification-token-form.scss b/feature-libs/user/account/styles/_verification-token-form.scss
index b0879946932..d9787db7708 100644
--- a/feature-libs/user/account/styles/_verification-token-form.scss
+++ b/feature-libs/user/account/styles/_verification-token-form.scss
@@ -9,13 +9,13 @@
.left-text {
padding: 0;
width: 50%;
- text-align: var(--cx-text-align, left);
+ text-align: start;
}
.right-text {
padding: 0;
width: 50%;
- text-align: var(--cx-text-align, right);
+ text-align: end;
}
a.disabled-link {
From d7994f27f4dad54b3d4dd53e98e4d9c60dbc02d5 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Mon, 13 May 2024 16:32:07 +0800
Subject: [PATCH 52/66] enable traceability for e2e accessbility test
---
.../accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
index 25c46944b24..e4a3029114d 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/accessibility/otp-login-tabbing.e2e-spec-flaky.cy.ts
@@ -19,11 +19,11 @@ describe('Tabbing order for OTP login', () => {
cy.get('cx-otp-login-form').should('exist');
cy.get('cx-otp-login-form form').should('exist');
});
- it('should allow to navigate with tab key for otp login form(empty form)', () => {
+ it('should allow to navigate with tab key for otp login form(empty form) (CXSPA-6672)', () => {
verifyTabbingOrder('cx-otp-login-form', config.otpLogin);
});
- it('should allow to navigate with tab key for otp login form(filled out form)', () => {
+ it('should allow to navigate with tab key for otp login form(filled out form) (CXSPA-6672)', () => {
const { email: username, password } = user;
cy.get('cx-otp-login-form form').within(() => {
cy.get('[formcontrolname="userId"]').clear().type(username);
@@ -33,7 +33,7 @@ describe('Tabbing order for OTP login', () => {
});
});
- it('should allow to navigate with tab key for otp verification token form', () => {
+ it('should allow to navigate with tab key for otp verification token form (CXSPA-6689)', () => {
cy.visit('/login/verify-token');
cy.get('cx-verification-token-form').should('exist');
cy.get('cx-verification-token-form form').should('exist');
From 86935ba75ecea4a6ee96a2280d399401ebc0cb66 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 14 May 2024 16:17:26 +0800
Subject: [PATCH 53/66] CXSPA-6689: code refined after code review
---
.../verification-token-dialog.component.spec.ts | 2 +-
.../verification-token-dialog.component.ts | 5 +----
.../verification-token-form.component.ts | 2 +-
feature-libs/user/account/root/model/otp-login.model.ts | 4 ++++
4 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts
index e8ec2a19227..d704b823bcc 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts
@@ -18,7 +18,7 @@ class MockLaunchDialogService implements Partial {
closeDialog(_reason: any) {}
}
-describe('AsmBindCartDialogComponent', () => {
+describe('VerificationTokenDialogComponent', () => {
let component: VerificationTokenDialogComponent;
let fixture: ComponentFixture;
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts
index 7a32f089c5e..baf1688c980 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.ts
@@ -6,10 +6,7 @@
import { Component } from '@angular/core';
import { FocusConfig, LaunchDialogService } from '@spartacus/storefront';
-
-export enum VERIFICATION_TOKEN_DIALOG_ACTION {
- OK = 'OK',
-}
+import { VERIFICATION_TOKEN_DIALOG_ACTION } from '@spartacus/user/account/root';
@Component({
selector: 'cx-verification-token-dialog',
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
index 0beabafa2e7..b95e59ee392 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.ts
@@ -30,7 +30,7 @@ export class VerificationTokenFormComponent implements OnInit {
constructor(
protected service: VerificationTokenFormComponentService,
protected launchDialogService: LaunchDialogService,
- private cdr: ChangeDetectorRef
+ protected cdr: ChangeDetectorRef
) {}
waitTime: number = 60;
diff --git a/feature-libs/user/account/root/model/otp-login.model.ts b/feature-libs/user/account/root/model/otp-login.model.ts
index 75b0e279c38..d2df3e037c5 100644
--- a/feature-libs/user/account/root/model/otp-login.model.ts
+++ b/feature-libs/user/account/root/model/otp-login.model.ts
@@ -14,3 +14,7 @@ export interface VerificationToken {
expiresIn: string;
tokenId: string;
}
+
+export enum VERIFICATION_TOKEN_DIALOG_ACTION {
+ OK = 'OK',
+}
From 1052acaa6ec35bb083c5d299baa5fffe527f2192 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 14 May 2024 16:36:54 +0800
Subject: [PATCH 54/66] CXSPA-6689: unit failed fixed
---
.../verification-token-dialog.component.spec.ts | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts
index d704b823bcc..9a297751db8 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.spec.ts
@@ -2,10 +2,8 @@ import { Pipe, PipeTransform } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { FocusDirective, LaunchDialogService } from '@spartacus/storefront';
-import {
- VERIFICATION_TOKEN_DIALOG_ACTION,
- VerificationTokenDialogComponent,
-} from './verification-token-dialog.component';
+import { VERIFICATION_TOKEN_DIALOG_ACTION } from '@spartacus/user/account/root';
+import { VerificationTokenDialogComponent } from './verification-token-dialog.component';
@Pipe({
name: 'cxTranslate',
From 00e67c9488a0e40376c8c10f76fd134cea3898ab Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Tue, 14 May 2024 17:18:39 +0800
Subject: [PATCH 55/66] CXSPA-6689: code refined
---
.../verification-token-form-component.service.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
index 30f44423fbf..8cde094d8f0 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
@@ -20,7 +20,6 @@ import { BehaviorSubject, from } from 'rxjs';
import { tap, withLatestFrom } from 'rxjs/operators';
const globalMsgShowTime: number = 10000;
-
@Injectable()
export class VerificationTokenFormComponentService {
constructor(
From 6751b62a8b9d8a50ff28818f4811ba7464225308 Mon Sep 17 00:00:00 2001
From: niehuayang
Date: Wed, 15 May 2024 09:32:59 +0800
Subject: [PATCH 56/66] fix comments
---
.../otp-login-form/otp-login-form.component.ts | 15 +++++++++------
.../cypress.config.ci.ts | 2 ++
.../storefrontapp-e2e-cypress/cypress.config.ts | 2 ++
.../user_access/otp-login.e2e-flaky.cy.ts | 12 ++++++++----
4 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
index ecb239003d4..9e9b600615a 100644
--- a/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
+++ b/feature-libs/user/account/components/otp-login-form/otp-login-form.component.ts
@@ -4,7 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { ChangeDetectionStrategy, Component, HostBinding } from '@angular/core';
+import {
+ ChangeDetectionStrategy,
+ Component,
+ HostBinding,
+ inject,
+} from '@angular/core';
import {
UntypedFormControl,
UntypedFormGroup,
@@ -27,11 +32,9 @@ import { ONE_TIME_PASSWORD_LOGIN_PURPOSE } from '../user-account-constants';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OneTimePasswordLoginFormComponent {
- constructor(
- protected routingService: RoutingService,
- protected verificationTokenFacade: VerificationTokenFacade,
- protected winRef: WindowRef
- ) {}
+ protected routingService = inject(RoutingService);
+ protected verificationTokenFacade = inject(VerificationTokenFacade);
+ protected winRef = inject(WindowRef);
protected busy$ = new BehaviorSubject(false);
diff --git a/projects/storefrontapp-e2e-cypress/cypress.config.ci.ts b/projects/storefrontapp-e2e-cypress/cypress.config.ci.ts
index acb2671bb3a..21c0da5b2fb 100644
--- a/projects/storefrontapp-e2e-cypress/cypress.config.ci.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress.config.ci.ts
@@ -29,6 +29,8 @@ export default defineConfig({
OCC_PREFIX: '/occ/v2',
OCC_PREFIX_USER_ENDPOINT: 'users',
OCC_PREFIX_ORDER_ENDPOINT: 'orders',
+ mail_CCV2_URL: 'http://mail-ccv2.westeurope.azurecontainer.io:8025',
+ mail_CCV2_PREFIX: '/api/v2',
},
e2e: {
// We've imported your old cypress plugins here.
diff --git a/projects/storefrontapp-e2e-cypress/cypress.config.ts b/projects/storefrontapp-e2e-cypress/cypress.config.ts
index 2b012739251..3d74a2e6e6e 100644
--- a/projects/storefrontapp-e2e-cypress/cypress.config.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress.config.ts
@@ -23,6 +23,8 @@ export default defineConfig({
OCC_PREFIX: '/occ/v2',
OCC_PREFIX_USER_ENDPOINT: 'users',
OCC_PREFIX_ORDER_ENDPOINT: 'orders',
+ mail_CCV2_URL: 'http://mail-ccv2.westeurope.azurecontainer.io:8025',
+ mail_CCV2_PREFIX: '/api/v2',
},
e2e: {
// We've imported your old cypress plugins here.
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
index bab8b25098e..e4b8a5a8b28 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/user_access/otp-login.e2e-flaky.cy.ts
@@ -43,12 +43,16 @@ describe('OTP Login', () => {
cy.log('The email being sent is asynchronous, so waiting 10s');
cy.wait(10000);
+ const mailCCV2Url =
+ Cypress.env('mail_CCV2_URL') +
+ Cypress.env('mail_CCV2_PREFIX') +
+ '/search?query=' +
+ user.email +
+ '&kind=to&start=0&limit=2';
+
cy.request({
method: 'GET',
- url:
- 'http://mail-ccv2.westeurope.azurecontainer.io:8025/api/v2/search?query=' +
- user.email +
- '&kind=to&start=0&limit=2',
+ url: mailCCV2Url,
}).then((response) => {
const subject =
'[Spartacus Electronics Site] Login Verification Code';
From 74bc7ee2a1dc27dcc7b21dccf25dccdf4c7a72f0 Mon Sep 17 00:00:00 2001
From: "Ran, Lulu"
Date: Wed, 15 May 2024 11:05:43 +0800
Subject: [PATCH 57/66] CXSPA-6689: code refined
---
.../assets/translations/en/userAccount.json | 8 ++++----
.../verification-token-dialog.component.html | 2 +-
.../verification-token-form-component.service.ts | 14 +++++++-------
.../verification-token-form.component.html | 5 ++++-
.../verification-token-form.component.ts | 13 ++++++++-----
.../account/styles/_verification-token-form.scss | 4 ++++
6 files changed, 28 insertions(+), 18 deletions(-)
diff --git a/feature-libs/user/account/assets/translations/en/userAccount.json b/feature-libs/user/account/assets/translations/en/userAccount.json
index 9f0280a8c35..4405506bab8 100644
--- a/feature-libs/user/account/assets/translations/en/userAccount.json
+++ b/feature-libs/user/account/assets/translations/en/userAccount.json
@@ -17,7 +17,7 @@
},
"verificationTokenForm": {
"createVerificationToken": "Verification code sent to {{target}}. Please check and enter the code.",
- "sendRateLime": "in {{waitTime}}s",
+ "sendRateLime": "in {{waitTime}} seconds",
"resend": "Resend",
"verificationCode": {
"label": "Verification Code",
@@ -28,12 +28,12 @@
"back": "Back"
},
"verificationTokenDialog": {
- "title": "Don't receive the code",
+ "title": "Didn’t receive the code",
"noReceiveCode": "If you don't receive the code, the reasons might be:",
"contentLine1": "1. The email hasn't come yet.",
"contentLine2": "2. The received email has been treated as junk email.",
"contentLine3": "3. Either the email address or the password you entered is incorrect.",
- "ok": "Ok"
+ "close": "Close"
},
"miniLogin": {
"userGreeting": "Hi, {{name}}",
@@ -42,4 +42,4 @@
"myAccountV2User": {
"signOut": "Sign Out"
}
-}
+}
\ No newline at end of file
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html
index 8ecc31a5910..0a26b0b1d53 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-dialog.component.html
@@ -43,7 +43,7 @@
class="btn btn-primary"
type="button"
>
- {{ 'verificationTokenDialog.ok' | cxTranslate }}
+ {{ 'verificationTokenDialog.close' | cxTranslate }}
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
index 8cde094d8f0..bca81285689 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form-component.service.ts
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { Injectable } from '@angular/core';
+import { Injectable, inject } from '@angular/core';
import {
UntypedFormControl,
UntypedFormGroup,
@@ -22,12 +22,12 @@ import { tap, withLatestFrom } from 'rxjs/operators';
const globalMsgShowTime: number = 10000;
@Injectable()
export class VerificationTokenFormComponentService {
- constructor(
- protected auth: AuthService,
- protected globalMessage: GlobalMessageService,
- protected verificationTokenFacade: VerificationTokenFacade
- ) {}
-
+ constructor() {}
+ protected globalMessage: GlobalMessageService = inject(GlobalMessageService);
+ protected verificationTokenFacade: VerificationTokenFacade = inject(
+ VerificationTokenFacade
+ );
+ protected auth: AuthService = inject(AuthService);
protected busy$ = new BehaviorSubject(false);
isUpdating$ = this.busy$.pipe(
diff --git a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
index fcc70b4b021..9d2ed36a6fc 100644
--- a/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
+++ b/feature-libs/user/account/components/verification-token-form/verification-token-form.component.html
@@ -20,8 +20,9 @@