diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..bcc5b1f1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,53 @@ +--- +name: Bug report +about: Create a report to help us improve +--- + + + + + + +### [REQUIRED] Describe your environment + +- Operating System version: **\_** +- Browser version: **\_** +- Firebase UI version: **\_** +- Firebase SDK version: **\_** +- Package name: **\_** + + + +### [REQUIRED] Describe the problem + +#### Steps to reproduce + + + +#### Relevant Code + + + + + +```javascript +// TODO(you): code here to reproduce the problem +``` diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..a09db44f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +--- +name: Feature request +about: Suggest an idea for this project +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..dcfc9bae --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,40 @@ +name: Test + +on: + push: + branches: + - ** + pull_request: + branches: + - "**" + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: '20' + check-latest: true + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: latest + - name: Install dependencies + run: pnpm install + - name: Install Firebase CLI + run: npm i -g firebase-tools@14.15.2 + - name: Start Firebase emulator and run tests + run: | + firebase emulators:start --only auth --project demo-test & + sleep 15 + # Wait for emulator to be ready + until wget -q --spider http://localhost:9099 2>/dev/null; do + echo "Waiting for emulator to start..." + sleep 2 + done + echo "Emulator is ready, running tests..." + pnpm test \ No newline at end of file diff --git a/.gitignore b/.gitignore index a547bf36..d4682044 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ dist dist-ssr *.local +# Angular +.angular + # Editor directories and files .vscode/* !.vscode/extensions.json diff --git a/package.json b/package.json index 61ad0563..204b5506 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,24 @@ "name": "@firebaseui/root", "private": true, "scripts": { - "emulators": "firebase emulators:start --only auth", + "emulators": "firebase emulators:start --only auth --project demo-test", "build": "pnpm run build:translations && pnpm run build:core && pnpm run build:react", "build:core": "pnpm --filter=@firebase-ui/core run build", "build:translations": "pnpm --filter=@firebase-ui/translations run build", "build:react": "pnpm --filter=@firebase-ui/react run build", "build:angular": "pnpm --filter=@firebase-ui/angular run build", + "test": "pnpm run test:core && pnpm run test:react && pnpm run test:angular && pnpm run test:translations && pnpm run test:styles", + "test:core": "pnpm --filter=@firebase-ui/core run test", + "test:react": "pnpm --filter=@firebase-ui/react run test", + "test:angular": "pnpm --filter=@firebase-ui/angular run test", + "test:translations": "pnpm --filter=@firebase-ui/translations run test", + "test:styles": "pnpm --filter=@firebase-ui/styles run test", + "test:watch": "pnpm run test:core:watch & pnpm run test:react:watch & pnpm run test:angular:watch", + "test:core:watch": "pnpm --filter=@firebase-ui/core run test:unit:watch", + "test:react:watch": "pnpm --filter=@firebase-ui/react run test:unit:watch", + "test:angular:watch": "pnpm --filter=@firebase-ui/angular run test:watch", + "publish:tags:core": "pnpm --filter=@firebase-ui/core run publish:tags", "publish:tags:translations": "pnpm --filter=@firebase-ui/translations run publish:tags", "publish:tags:react": "pnpm --filter=@firebase-ui/react run publish:tags", diff --git a/packages/firebaseui-angular/angular.json b/packages/firebaseui-angular/angular.json new file mode 100644 index 00000000..ef0757d2 --- /dev/null +++ b/packages/firebaseui-angular/angular.json @@ -0,0 +1,37 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "firebase-ui-angular": { + "projectType": "library", + "root": "", + "sourceRoot": "src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "polyfills": ["zone.js", "zone.js/testing"], + "styles": [], + "scripts": [], + "assets": [], + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] + } + } + } + } + } +} diff --git a/packages/firebaseui-angular/karma.conf.js b/packages/firebaseui-angular/karma.conf.js new file mode 100644 index 00000000..e19874ee --- /dev/null +++ b/packages/firebaseui-angular/karma.conf.js @@ -0,0 +1,52 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: "", + frameworks: ["jasmine", "@angular-devkit/build-angular"], + plugins: [ + require("karma-jasmine"), + require("karma-chrome-launcher"), + require("karma-jasmine-html-reporter"), + require("karma-coverage"), + require("@angular-devkit/build-angular/plugins/karma"), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution order + // random: false + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter: { + dir: require("path").join(__dirname, "./coverage/"), + subdir: ".", + reporters: [{ type: "html" }, { type: "text-summary" }], + }, + reporters: ["progress", "kjhtml"], + browsers: ["ChromeHeadless"], + restartOnFileChange: true, + }); +}; diff --git a/packages/firebaseui-angular/package.json b/packages/firebaseui-angular/package.json index c5bb187a..d0622eb6 100644 --- a/packages/firebaseui-angular/package.json +++ b/packages/firebaseui-angular/package.json @@ -15,6 +15,9 @@ }, "scripts": { "build": "ng-packagr -p ng-package.json", + "test": "ng test --watch=false --browsers=ChromeHeadless --project=firebase-ui-angular", + "test:watch": "ng test --watch=true --project=firebase-ui-angular", + "test:ci": "ng test --watch=false --browsers=ChromeHeadless --code-coverage --project=firebase-ui-angular", "publish:tags": "sh -c 'TAG=\"${npm_package_name}@${npm_package_version}\"; git tag --list \"$TAG\" | grep . || git tag \"$TAG\"; git push origin \"$TAG\"'", "release": "pnpm pack --pack-destination ../../releases/" }, diff --git a/packages/firebaseui-angular/src/lib/auth/forms/email-password-form/email-password-form.component.spec.ts b/packages/firebaseui-angular/src/lib/auth/forms/email-password-form/email-password-form.component.spec.ts index 13394bca..a5990369 100644 --- a/packages/firebaseui-angular/src/lib/auth/forms/email-password-form/email-password-form.component.spec.ts +++ b/packages/firebaseui-angular/src/lib/auth/forms/email-password-form/email-password-form.component.spec.ts @@ -14,19 +14,19 @@ * limitations under the License. */ -import { CommonModule } from '@angular/common'; -import { Component, Input } from '@angular/core'; +import { CommonModule } from "@angular/common"; +import { Component, Input } from "@angular/core"; import { ComponentFixture, TestBed, fakeAsync, tick, -} from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { Router, provideRouter } from '@angular/router'; -import { TanStackField } from '@tanstack/angular-form'; -import { getFirebaseUITestProviders } from '../../../testing/test-helpers'; -import { EmailPasswordFormComponent } from './email-password-form.component'; +} from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { Router, provideRouter } from "@angular/router"; +import { TanStackField } from "@tanstack/angular-form"; +import { getFirebaseUITestProviders } from "../../../testing/test-helpers"; +import { EmailPasswordFormComponent } from "./email-password-form.component"; // Define window properties for testing declare global { @@ -38,25 +38,25 @@ declare global { // Mock Button component @Component({ - selector: 'fui-button', + selector: "fui-button", template: ``, standalone: true, }) class MockButtonComponent { - @Input() type: string = 'button'; + @Input() type: string = "button"; } // Mock TermsAndPrivacy component @Component({ - selector: 'fui-terms-and-privacy', + selector: "fui-terms-and-privacy", template: `
`, standalone: true, }) class MockTermsAndPrivacyComponent {} -describe('EmailPasswordFormComponent', () => { +describe("EmailPasswordFormComponent", () => { let component: EmailPasswordFormComponent; let fixture: ComponentFixture; let mockRouter: any; @@ -64,16 +64,16 @@ describe('EmailPasswordFormComponent', () => { // Expected error messages from the actual implementation const errorMessages = { - invalidEmail: 'Please enter a valid email address', - passwordTooShort: 'Password should be at least 8 characters', - unknownError: 'An unknown error occurred', + invalidEmail: "Please enter a valid email address", + passwordTooShort: "Password should be at least 8 characters", + unknownError: "An unknown error occurred", }; // Mock schema returned by createEmailFormSchema const mockSchema = { safeParse: (data: any) => { // Test email validation - if (!data.email.includes('@')) { + if (!data.email.includes("@")) { return { success: false, error: { @@ -101,22 +101,22 @@ describe('EmailPasswordFormComponent', () => { beforeEach(async () => { // Mock router mockRouter = { - navigateByUrl: jasmine.createSpy('navigateByUrl'), + navigateByUrl: jasmine.createSpy("navigateByUrl"), }; // Create spies for the global functions signInSpy = jasmine - .createSpy('signInWithEmailAndPassword') + .createSpy("signInWithEmailAndPassword") .and.returnValue(Promise.resolve()); // Define the function on the window object - Object.defineProperty(window, 'signInWithEmailAndPassword', { + Object.defineProperty(window, "signInWithEmailAndPassword", { value: signInSpy, writable: true, configurable: true, }); - Object.defineProperty(window, 'createEmailFormSchema', { + Object.defineProperty(window, "createEmailFormSchema", { value: () => mockSchema, writable: true, configurable: true, @@ -141,17 +141,17 @@ describe('EmailPasswordFormComponent', () => { component = fixture.componentInstance; // Set required inputs - component.forgotPasswordRoute = '/forgot-password'; - component.registerRoute = '/register'; + component.forgotPasswordRoute = "/forgot-password"; + component.registerRoute = "/register"; // Mock the validateAndSignIn method without any TypeScript errors - component.validateAndSignIn = jasmine.createSpy('validateAndSignIn'); + component.validateAndSignIn = jasmine.createSpy("validateAndSignIn"); fixture.detectChanges(); await fixture.whenStable(); // Wait for async ngOnInit }); - it('renders the form correctly', () => { + it("renders the form correctly", () => { expect(component).toBeTruthy(); // Check essential elements are present @@ -162,9 +162,9 @@ describe('EmailPasswordFormComponent', () => { By.css('input[type="password"]') ); const termsAndPrivacy = fixture.debugElement.query( - By.css('fui-terms-and-privacy') + By.css("fui-terms-and-privacy") ); - const submitButton = fixture.debugElement.query(By.css('fui-button')); + const submitButton = fixture.debugElement.query(By.css("fui-button")); expect(emailInput).toBeTruthy(); expect(passwordInput).toBeTruthy(); @@ -172,16 +172,16 @@ describe('EmailPasswordFormComponent', () => { expect(submitButton).toBeTruthy(); }); - it('submits the form when handleSubmit is called', fakeAsync(() => { + it("submits the form when handleSubmit is called", fakeAsync(() => { // Set values directly on the form state - component.form.state.values.email = 'test@example.com'; - component.form.state.values.password = 'password123'; + component.form.state.values.email = "test@example.com"; + component.form.state.values.password = "password123"; // Create a submit event - const event = new Event('submit'); + const event = new Event("submit"); Object.defineProperties(event, { - preventDefault: { value: jasmine.createSpy('preventDefault') }, - stopPropagation: { value: jasmine.createSpy('stopPropagation') }, + preventDefault: { value: jasmine.createSpy("preventDefault") }, + stopPropagation: { value: jasmine.createSpy("stopPropagation") }, }); // Call handleSubmit directly @@ -190,41 +190,41 @@ describe('EmailPasswordFormComponent', () => { // Check if validateAndSignIn was called with correct values expect(component.validateAndSignIn).toHaveBeenCalledWith( - 'test@example.com', - 'password123' + "test@example.com", + "password123" ); })); - it('displays error message when sign in fails', fakeAsync(() => { + it("displays error message when sign in fails", fakeAsync(() => { // Manually set the error - component.formError = 'Invalid credentials'; + component.formError = "Invalid credentials"; fixture.detectChanges(); // Check that the error message is displayed in the DOM - const formErrorEl = fixture.debugElement.query(By.css('.fui-form__error')); + const formErrorEl = fixture.debugElement.query(By.css(".fui-form__error")); expect(formErrorEl).toBeTruthy(); expect(formErrorEl.nativeElement.textContent.trim()).toBe( - 'Invalid credentials' + "Invalid credentials" ); })); - it('shows an error message for invalid input', () => { + it("shows an error message for invalid input", () => { // Manually set error message for testing component.formError = errorMessages.invalidEmail; fixture.detectChanges(); // Check for error display in the DOM - const formErrorEl = fixture.debugElement.query(By.css('.fui-form__error')); + const formErrorEl = fixture.debugElement.query(By.css(".fui-form__error")); expect(formErrorEl).toBeTruthy(); expect(formErrorEl.nativeElement.textContent.trim()).toBe( errorMessages.invalidEmail ); }); - it('navigates to register route when that button is clicked', () => { + it("navigates to register route when that button is clicked", () => { // Find the register button (second action button) const registerButton = fixture.debugElement.queryAll( - By.css('.fui-form__action') + By.css(".fui-form__action") )[1]; expect(registerButton).toBeTruthy(); @@ -232,13 +232,13 @@ describe('EmailPasswordFormComponent', () => { registerButton.nativeElement.click(); // Check navigation was triggered - expect(mockRouter.navigateByUrl).toHaveBeenCalledWith('/register'); + expect(mockRouter.navigateByUrl).toHaveBeenCalledWith("/register"); }); - it('navigates to forgot password route when that button is clicked', () => { + it("navigates to forgot password route when that button is clicked", () => { // Find the forgot password button (first action button) const forgotPasswordButton = fixture.debugElement.queryAll( - By.css('.fui-form__action') + By.css(".fui-form__action") )[0]; expect(forgotPasswordButton).toBeTruthy(); @@ -246,6 +246,6 @@ describe('EmailPasswordFormComponent', () => { forgotPasswordButton.nativeElement.click(); // Check navigation was triggered - expect(mockRouter.navigateByUrl).toHaveBeenCalledWith('/forgot-password'); + expect(mockRouter.navigateByUrl).toHaveBeenCalledWith("/forgot-password"); }); }); diff --git a/packages/firebaseui-angular/src/lib/auth/forms/forgot-password-form/forgot-password-form.component.spec.ts b/packages/firebaseui-angular/src/lib/auth/forms/forgot-password-form/forgot-password-form.component.spec.ts index d8cb64c1..e842539e 100644 --- a/packages/firebaseui-angular/src/lib/auth/forms/forgot-password-form/forgot-password-form.component.spec.ts +++ b/packages/firebaseui-angular/src/lib/auth/forms/forgot-password-form/forgot-password-form.component.spec.ts @@ -14,19 +14,19 @@ * limitations under the License. */ -import { CommonModule } from '@angular/common'; -import { Component, Input } from '@angular/core'; +import { CommonModule } from "@angular/common"; +import { Component, Input } from "@angular/core"; import { ComponentFixture, TestBed, fakeAsync, tick, -} from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { Router, provideRouter } from '@angular/router'; -import { TanStackField } from '@tanstack/angular-form'; -import { getFirebaseUITestProviders } from '../../../testing/test-helpers'; -import { ForgotPasswordFormComponent } from './forgot-password-form.component'; +} from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { Router, provideRouter } from "@angular/router"; +import { TanStackField } from "@tanstack/angular-form"; +import { getFirebaseUITestProviders } from "../../../testing/test-helpers"; +import { ForgotPasswordFormComponent } from "./forgot-password-form.component"; // Define window properties for testing declare global { @@ -38,25 +38,25 @@ declare global { // Mock Button component @Component({ - selector: 'fui-button', + selector: "fui-button", template: ``, standalone: true, }) class MockButtonComponent { - @Input() type: string = 'button'; + @Input() type: string = "button"; } // Mock TermsAndPrivacy component @Component({ - selector: 'fui-terms-and-privacy', + selector: "fui-terms-and-privacy", template: `
`, standalone: true, }) class MockTermsAndPrivacyComponent {} -describe('ForgotPasswordFormComponent', () => { +describe("ForgotPasswordFormComponent", () => { let component: ForgotPasswordFormComponent; let fixture: ComponentFixture; let mockRouter: any; @@ -64,15 +64,15 @@ describe('ForgotPasswordFormComponent', () => { // Expected error messages from the actual implementation const errorMessages = { - invalidEmail: 'Please enter a valid email address', - unknownError: 'An unknown error occurred', + invalidEmail: "Please enter a valid email address", + unknownError: "An unknown error occurred", }; // Mock schema returned by createForgotPasswordFormSchema const mockSchema = { safeParse: (data: any) => { // Test email validation - if (!data.email.includes('@')) { + if (!data.email.includes("@")) { return { success: false, error: { @@ -89,22 +89,22 @@ describe('ForgotPasswordFormComponent', () => { beforeEach(async () => { // Mock router mockRouter = { - navigateByUrl: jasmine.createSpy('navigateByUrl'), + navigateByUrl: jasmine.createSpy("navigateByUrl"), }; // Create spies for the global functions sendResetEmailSpy = jasmine - .createSpy('sendPasswordResetEmail') + .createSpy("sendPasswordResetEmail") .and.returnValue(Promise.resolve()); // Define the function on the window object - Object.defineProperty(window, 'sendPasswordResetEmail', { + Object.defineProperty(window, "sendPasswordResetEmail", { value: sendResetEmailSpy, writable: true, configurable: true, }); - Object.defineProperty(window, 'createForgotPasswordFormSchema', { + Object.defineProperty(window, "createForgotPasswordFormSchema", { value: () => mockSchema, writable: true, configurable: true, @@ -129,21 +129,21 @@ describe('ForgotPasswordFormComponent', () => { component = fixture.componentInstance; // Set required inputs - component.signInRoute = '/signin'; + component.signInRoute = "/signin"; // Replace the resetPassword method with a spy - spyOn(component, 'resetPassword').and.callFake(async (_email) => { + spyOn(component, "resetPassword").and.callFake(async (_email) => { return Promise.resolve(); }); // Mock the form schema - component['formSchema'] = mockSchema; + component["formSchema"] = mockSchema; fixture.detectChanges(); await fixture.whenStable(); // Wait for async ngOnInit }); - it('renders the form correctly', () => { + it("renders the form correctly", () => { expect(component).toBeTruthy(); // Check essential elements are present @@ -151,24 +151,24 @@ describe('ForgotPasswordFormComponent', () => { By.css('input[type="email"]') ); const termsAndPrivacy = fixture.debugElement.query( - By.css('fui-terms-and-privacy') + By.css("fui-terms-and-privacy") ); - const submitButton = fixture.debugElement.query(By.css('fui-button')); + const submitButton = fixture.debugElement.query(By.css("fui-button")); expect(emailInput).toBeTruthy(); expect(termsAndPrivacy).toBeTruthy(); expect(submitButton).toBeTruthy(); }); - it('submits the form when handleSubmit is called', fakeAsync(() => { + it("submits the form when handleSubmit is called", fakeAsync(() => { // Set values directly on the form state - component.form.state.values.email = 'test@example.com'; + component.form.state.values.email = "test@example.com"; // Create a submit event - const event = new Event('submit'); + const event = new Event("submit"); Object.defineProperties(event, { - preventDefault: { value: jasmine.createSpy('preventDefault') }, - stopPropagation: { value: jasmine.createSpy('stopPropagation') }, + preventDefault: { value: jasmine.createSpy("preventDefault") }, + stopPropagation: { value: jasmine.createSpy("stopPropagation") }, }); // Call handleSubmit directly @@ -176,44 +176,44 @@ describe('ForgotPasswordFormComponent', () => { tick(); // Check if resetPassword was called with correct values - expect(component.resetPassword).toHaveBeenCalledWith('test@example.com'); + expect(component.resetPassword).toHaveBeenCalledWith("test@example.com"); })); - it('displays error message when reset fails', fakeAsync(() => { + it("displays error message when reset fails", fakeAsync(() => { // Manually set the error - component.formError = 'Invalid email'; + component.formError = "Invalid email"; fixture.detectChanges(); // Check that the error message is displayed in the DOM - const formErrorEl = fixture.debugElement.query(By.css('.fui-form__error')); + const formErrorEl = fixture.debugElement.query(By.css(".fui-form__error")); expect(formErrorEl).toBeTruthy(); - expect(formErrorEl.nativeElement.textContent.trim()).toBe('Invalid email'); + expect(formErrorEl.nativeElement.textContent.trim()).toBe("Invalid email"); })); - it('shows success message when email is sent', () => { + it("shows success message when email is sent", () => { // Set emailSent to true component.emailSent = true; fixture.detectChanges(); // Check for success message const successMessage = fixture.debugElement.query( - By.css('.fui-form__success') + By.css(".fui-form__success") ); expect(successMessage).toBeTruthy(); expect(successMessage.nativeElement.textContent.trim()).toContain( - 'Check your email' + "Check your email" ); }); - it('navigates to sign in route when back button is clicked', () => { + it("navigates to sign in route when back button is clicked", () => { // Find the sign in button - const signInLink = fixture.debugElement.query(By.css('.fui-form__action')); + const signInLink = fixture.debugElement.query(By.css(".fui-form__action")); expect(signInLink).toBeTruthy(); // Click the link signInLink.nativeElement.click(); // Check navigation was triggered - expect(mockRouter.navigateByUrl).toHaveBeenCalledWith('/signin'); + expect(mockRouter.navigateByUrl).toHaveBeenCalledWith("/signin"); }); }); diff --git a/packages/firebaseui-angular/src/lib/auth/forms/phone-form/phone-form.component.spec.ts b/packages/firebaseui-angular/src/lib/auth/forms/phone-form/phone-form.component.spec.ts index 55703fac..a0870da4 100644 --- a/packages/firebaseui-angular/src/lib/auth/forms/phone-form/phone-form.component.spec.ts +++ b/packages/firebaseui-angular/src/lib/auth/forms/phone-form/phone-form.component.spec.ts @@ -14,62 +14,69 @@ * limitations under the License. */ -import { CommonModule } from '@angular/common'; -import { Component, Input } from '@angular/core'; +import { CommonModule } from "@angular/common"; +import { Component, Input } from "@angular/core"; import { ComponentFixture, TestBed, fakeAsync, tick, -} from '@angular/core/testing'; +} from "@angular/core/testing"; import { Auth, ConfirmationResult, RecaptchaVerifier, -} from '@angular/fire/auth'; -import { FirebaseUIError } from '@firebase-ui/core'; -import { TanStackField } from '@tanstack/angular-form'; -import { firstValueFrom, of } from 'rxjs'; -import { FirebaseUI, FirebaseUIPolicies } from '../../../provider'; +} from "@angular/fire/auth"; +import { FirebaseUIError } from "@firebase-ui/core"; +import { TanStackField } from "@tanstack/angular-form"; +import { firstValueFrom, of } from "rxjs"; +import { FirebaseUI, FirebaseUIPolicies } from "../../../provider"; import { PhoneFormComponent, PhoneNumberFormComponent, VerificationFormComponent, -} from './phone-form.component'; -import { mockAuth } from '../../../testing/test-helpers'; -import { providePolicies } from 'src/app/policies/providePolicies'; +} from "./phone-form.component"; +import { mockAuth } from "../../../testing/test-helpers"; +// Mock providePolicies function +const mockProvidePolicies = () => ({ + provide: Symbol("POLICY_CONFIG"), + useValue: { + termsOfServiceUrl: "https://yourdomain.com/terms", + privacyPolicyUrl: "https://yourdomain.com/privacy", + }, +}); // Mock Firebase UI Core functions const mockFuiSignInWithPhoneNumber = jasmine - .createSpy('signInWithPhoneNumber') + .createSpy("signInWithPhoneNumber") .and.returnValue( Promise.resolve({ - confirm: jasmine.createSpy('confirm').and.returnValue(Promise.resolve()), - verificationId: 'mock-verification-id', - } as ConfirmationResult), + confirm: jasmine.createSpy("confirm").and.returnValue(Promise.resolve()), + verificationId: "mock-verification-id", + } as ConfirmationResult) ); const mockFuiConfirmPhoneNumber = jasmine - .createSpy('fuiConfirmPhoneNumber') + .createSpy("fuiConfirmPhoneNumber") .and.returnValue(Promise.resolve({} as any)); // Mock Button component @Component({ - selector: 'fui-button', + selector: "fui-button", template: ``, standalone: true, }) class MockButtonComponent { - @Input() type: string = 'button'; + @Input() type: string = "button"; @Input() disabled: boolean = false; - @Input() variant: string = 'primary'; + @Input() variant: string = "primary"; } // Mock TermsAndPrivacy component @Component({ - selector: 'fui-terms-and-privacy', + selector: "fui-terms-and-privacy", template: `
`, standalone: true, }) @@ -77,7 +84,7 @@ class MockTermsAndPrivacyComponent {} // Mock CountrySelector component @Component({ - selector: 'fui-country-selector', + selector: "fui-country-selector", template: `