FirebaseError:使用无效数据调用函数 Query.where()。不支持的字段值:在进行 Karma 测试时未定义

Posted

技术标签:

【中文标题】FirebaseError:使用无效数据调用函数 Query.where()。不支持的字段值:在进行 Karma 测试时未定义【英文标题】:FirebaseError: Function Query.where() called with invalid data. Unsupported field value: undefined while doing Karma testing 【发布时间】:2021-12-16 10:10:26 【问题描述】:

当我通过karmaangular cli中的jasmine测试应用程序时,出现了这个错误:

FirebaseError:使用无效数据调用函数 Query.where()。 不支持的字段值:未定义

我应该采取什么步骤来解决这个问题?它是在组件的spec.ts 文件中还是已经在项目的后端代码中?请注意,这是我第一次处理使用Firebase的项目

task-settings.component.ts

import  Component, OnInit  from '@angular/core';
import  Router  from '@angular/router';
import  Params, ActivatedRoute  from '@angular/router';
import  AngularFireAuth  from '@angular/fire/auth';
import  AuthService  from 'src/app/services/auth.service';
import  TaskService  from 'src/app/services/task.service';
import  switchMap  from 'rxjs/operators';
import  FormBuilder, Validators  from '@angular/forms';
import  ToastService  from 'src/app/services/toast.service';
// modal dialog import
@Component(
  selector: 'task-settings',
  templateUrl: './task-settings.component.html',
  styleUrls: ['./task-settings.component.css'],
)
export class TaskSettingsComponent implements OnInit 
  term!: string;
  dateToday = new Date();
  userData: any;
  fsData: any;
  id!: any;
  taskData: any;
  taskStatus: any = ['Pending', 'Completed'];
  taskScopeArray!: string[];
  newTaskScopeArray!: string[];
  addTaskForm!: any;
  newTaskForm!: any;
  taskRecipients: 
    uid: any;
    status: string;
    section: any;
    submissionLink: string;
    displayName: any;
  [] = [];
  userPushTokens:  pushToken: string [] = [];
  addTaskModal!: boolean;
  addScopeModal!: boolean;
  editTaskScope!: any;
  verifyTasks$: any;
  deleteTaskForm!: any;
  deleteTaskModal!: boolean;

  updateTaskForm!: any;
  updateTaskConfirm!: boolean;

  // editTaskScope! : any;
  // end of edit task import

  constructor(
    public auth: AuthService,
    private route: ActivatedRoute,
    private router: Router,
    readonly fire: AngularFireAuth,
    private taskService: TaskService,
    private fb: FormBuilder,
    public toastService: ToastService
  ) 

  ngOnInit(): void 
    var d = new Date();
    var y = d.getFullYear();
    var n = d.getMonth();
    console.log(n);
    console.log(y);
    if (n >= 1 && n <= 6) 
      console.log('January to June');
      console.log('2nd Term SY ' + y + '-' + (y + 1));
      this.term = '2nd Term SY ' + y + '-' + (y + 1);
     else if (n >= 8 && n <= 12) 
      console.log('August to December');
      console.log('1st Term SY ' + y + '-' + (y + 1));
      this.term = '1st Term SY ' + y + '-' + (y + 1);
     else 
      console.log('Summer Term' + y + '-' + (y + 1));
      this.term = 'Summer Term' + y + '-' + (y + 1);
    
    this.updateTaskForm = this.fb.group(
      title: ['', Validators.required],
      description: ['', Validators.required],
      deadline: ['', Validators.required],
    );
    this.fire.user.subscribe((user: any) => 
      this.userData = user;
      this.auth.getUserData(user?.uid).subscribe((res) => 
        this.fsData = res;
      );
    );
    this.route.params
      .pipe(
        switchMap((params: Params) => 
          console.log(params['id']);
          return this.taskService.getTask(params['id']);
        )
      )
      .subscribe((res) => 
        this.updateTaskForm.controls.title.setValue(res.title);
        this.updateTaskForm.controls.description.setValue(res.description);
        this.updateTaskForm.controls.deadline.setValue(res.deadline);
        this.taskData = res;
        console.log(res);
      );

    this.taskScopeArray = [];
  

  public triggerDeleteTaskModal() 
    this.deleteTaskModal = !this.deleteTaskModal;
  

  public triggerUpdateTask() 
    this.updateTaskForm.controls.title.setValue(this.taskData.title);
    this.updateTaskForm.controls.description.setValue(
      this.taskData.description
    );
    this.updateTaskForm.controls.deadline.setValue(this.taskData.deadline);
  

  public updateTask() 
    if (this.updateTaskForm.valid) 
      this.taskService.updateTask(
        this.taskData.recipients,
        this.taskData.taskId,
        this.updateTaskForm.controls['title'].value,
        this.updateTaskForm.controls['description'].value,
        new Date (this.updateTaskForm.controls['deadline'].value),
      ).then(() => this.triggerUpdateTask())
      .finally(() => this.updateTaskForm.reset())
    
    else if (this.updateTaskForm.invalid) 
      this.updateTaskForm.controls['title'].markAsTouched();
      this.updateTaskForm.controls['description'].markAsTouched();
      this.updateTaskForm.controls['deadline'].markAsTouched();


      this.toastService.publish("Please fillup all the requirements","formSuccess");

    
  

  changeTaskScope(e: any) 
    console.log(e.target.value);
    console.log(typeof e.target.value);
  

  public deleteTask() 
    this.taskService.deleteTask(this.taskData.taskId).then(() => 
      this.router.navigate(['/dashboard']);
    );
  

task-settings.component.spec.ts

import  ComponentFixture, TestBed  from '@angular/core/testing';
import  Component, OnInit  from '@angular/core';
import  Router  from '@angular/router';
import  Params, ActivatedRoute  from '@angular/router';
import  AngularFireAuth  from '@angular/fire/auth';
import  AuthService  from 'src/app/services/auth.service';
import  TaskService  from 'src/app/services/task.service';
import  switchMap  from 'rxjs/operators';
import  FormBuilder, FormsModule, ReactiveFormsModule, Validators  from '@angular/forms';
import  ToastService  from 'src/app/services/toast.service';
import  TaskSettingsComponent  from './task-settings.component';
import  RouterTestingModule  from '@angular/router/testing';
import  AngularFireModule  from '@angular/fire';
import  environment  from 'src/environments/environment';
import  AngularFireDatabaseModule  from '@angular/fire/database';
import  HttpClientModule  from '@angular/common/http';

describe('TaskSettingsComponent', () => 
  let component: TaskSettingsComponent;
  let fixture: ComponentFixture<TaskSettingsComponent>;

  beforeEach(async () => 
    await TestBed.configureTestingModule(
      declarations: [ TaskSettingsComponent ],
      imports: [ RouterTestingModule, AngularFireModule.initializeApp(environment.firebase), AngularFireDatabaseModule, HttpClientModule, ReactiveFormsModule, FormsModule ],
      providers: [ AuthService, TaskService, ToastService ]
    )
    .compileComponents();
  );

  beforeEach(() => 
    fixture = TestBed.createComponent(TaskSettingsComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  );

  it('should create', () => 
    expect(component).toBeTruthy();
  );
);

app.module.ts

import  NgModule  from '@angular/core';
import  BrowserModule  from '@angular/platform-browser';
import  AngularFireModule  from "@angular/fire";
import  AngularFirestoreModule  from "@angular/fire/firestore";
import  AngularFireAuthModule  from '@angular/fire/auth';
import  AngularFireStorageModule  from '@angular/fire/storage';

import  AppRoutingModule  from './app-routing.module';
import  AppComponent  from './app.component';
import  DashboardComponent  from '../app/dashboard-components/dashboard/dashboard.component'
import  LoginComponent  from './auth-components/login/login.component';
import  PagenotfoundComponent  from './pagenotfound/pagenotfound.component';
import  ForgotPasswordComponent  from './auth-components/forgot-password/forgot-password.component';
import  environment  from "src/environments/environment";
import  FormsModule  from '@angular/forms';
import  ReactiveFormsModule  from '@angular/forms';
import  NavbarComponent  from './navbar/navbar.component';
import  MyProfileComponent  from './my-profile-components/my-profile/my-profile.component';
import  UserManagementComponent  from './user-management-components/user-management/user-management.component';
import  NgxPaginationModule  from 'ngx-pagination'; // <-- import the module
import  AngularFireFunctionsModule  from '@angular/fire/functions';
import  HttpClientModule  from '@angular/common/http';
import  UserComponent  from './user-management-components/user/user.component';
import  TaskComponent  from './dashboard-components/task/task.component';
import  TaskSettingsComponent  from './dashboard-components/task-settings/task-settings.component';
import  ReportComponent  from './dashboard-components/report/report.component';
import  ChartModule  from 'angular2-chartjs';
import  Ng2SearchPipeModule  from 'ng2-search-filter';
import  VerifyTaskComponent  from './user-management-components/verify-task/verify-task.component';

import  UserManualComponent  from './user-manual/user-manual.component';
import  ArchiveComponent  from './archive/archive.component';
import  AngularFireDatabaseModule  from '@angular/fire/database';

@NgModule(
  declarations: [
    AppComponent,
    DashboardComponent,
    LoginComponent,
    PagenotfoundComponent,
    ForgotPasswordComponent,
    NavbarComponent,
    MyProfileComponent,
    UserManagementComponent,
    UserComponent,
    TaskComponent,
    TaskSettingsComponent,
    ReportComponent,
    VerifyTaskComponent,
    UserManualComponent,
    ArchiveComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule,
    ChartModule,
    AngularFireAuthModule,
    ReactiveFormsModule,
    NgxPaginationModule,
    AngularFirestoreModule, //Firebase imports
    AngularFireFunctionsModule,
    AngularFireStorageModule,
    Ng2SearchPipeModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireDatabaseModule
  ],
  providers: [],
  bootstrap: [AppComponent]
)
export class AppModule  

task.service.ts

public setRecipients(scope:string):Observable<any> 
    return this.afs.collection('users',ref => ref.where('section','==',scope))
    .snapshotChanges()
    .pipe(
      map((doc: any) => 
        // console.log(doc)
        return doc.map(
          (c:  payload:  doc:  data: () => any; id: any; ; ; ) => 
            const data = c.payload.doc.data();
            const id = c.payload.doc.id;
            return  id, ...data ;
          
        ))
    );
  

  public getTask(id:string):Observable<any> 
    return this.afs.collection('tasks',ref => ref.where('taskId','==',id))
    .doc(id)
    .snapshotChanges()
    .pipe(
      map((doc: any) => 
        // console.log(doc)
        return  id: doc.payload.id, ...doc.payload.data() ;
      )
    );
  
  public getCompletedTask(): Observable<any> 
    return this.afs
      .collection('tasks', (ref) =>
        ref.where('status', '==', 'Completed').orderBy('createdAt')
      )
      .snapshotChanges()  
      .pipe(
        map((doc: any) => 
          // console.log(doc)
          return doc.map(
            (c:  payload:  doc:  data: () => any; id: any   ) => 
              const data = c.payload.doc.data();
              const id = c.payload.doc.id;
              return  id, ...data ;
            
          );
        )
      );
  

user.service.ts

public getDeptHeadUsers():Observable<any> 
    return this.afs.collection('users', ref => ref.where('role','==','Department Head').orderBy('createdAt','desc'))
    .snapshotChanges()
    .pipe(
      map((doc: any) => 
        // console.log(doc)
        return doc.map(
          (c:  payload:  doc:  data: () => any; id: any; ; ; ) => 
            const data = c.payload.doc.data();
            const id = c.payload.doc.id;
            return  id, ...data ;
          
        ))
    );
  
  public getStudentUsers():Observable<any> 
    return this.afs.collection('users', ref => ref.where('role','==','Student',).orderBy('createdAt','desc'))
    .snapshotChanges()
    .pipe(
      map((doc: any) => 
        // console.log(doc)
        return doc.map(
          (c:  payload:  doc:  data: () => any; id: any; ; ; ) => 
            const data = c.payload.doc.data();
            const id = c.payload.doc.id;
            return  id, ...data ;
          
        ))
    );
  
  public getAdminUsers():Observable<any> 
    return this.afs.collection('users', ref => ref.where('role','==','CICS Office Staff').orderBy('createdAt','desc'))
    .snapshotChanges()
    .pipe(
      map((doc: any) => 
        // console.log(doc)
        return doc.map(
          (c:  payload:  doc:  data: () => any; id: any; ; ; ) => 
            const data = c.payload.doc.data();
            const id = c.payload.doc.id;
            return  id, ...data ;
          
        ))
    );
  

auth.service.ts

import  Injectable  from '@angular/core';
import  AngularFireAuth   from '@angular/fire/auth';
import  AngularFirestore  from '@angular/fire/firestore';
import  AngularFireFunctions  from '@angular/fire/functions';
import  Observable  from 'rxjs';
import  map  from 'rxjs/operators';
import  Router  from '@angular/router';
import  ToastService  from './toast.service';

@Injectable(
  providedIn: 'root'
)
export class AuthService 

  constructor(
    private afs: AngularFirestore,
    private fns: AngularFireFunctions,
    readonly fire: AngularFireAuth, 
    public router: Router,
    public toastService: ToastService
  ) 
    this.fire.authState.subscribe(user => 
      if (user) 
        this.getUserData(user.uid).subscribe(res => 
          if (res.role != 'CICS Office Staff' && res.role != 'Department Head') 
            this.signOut().then(() => 
              this.toastService.publish('You are not allowed to access the web system. Please use the mobile application.','userDoesNotExist');
            )
           else if (res.role == 'CICS Office Staff' || res.role == 'Department Head') 
              console.log('Authenticated')
          
        )
       else 
        console.log('logged out');
      
    )
  

  public signup(email:string, password:string,displayName:string,contactNumber:string) 
    return this.fire.createUserWithEmailAndPassword(email, password)
      // Update the user info with the given name
      .then(res=> 
        res.user?.updateProfile(displayName: displayName)
        .then(() => 
          const data = 
            uid: res.user?.uid,
            contactNumber: contactNumber,
            email: email,
            displayName: displayName,
            createdAt: Date.now(),
            role: 'CICS Office Staff'
          
          this.afs.collection('users')
          .doc(res.user?.uid).set(data)
          .catch(error => console.log(error));
        )
        .then(() => res.user,console.log(res.user))
      )
  

  public signOut(): Promise<void> 
    console.log('Signing-out');
    //this.guard.prompt('signIn').then(user => )
    return this.fire.signOut()
    .then(() => this.router.navigate(['/login']))
  

  public signIn(email: string, password: string) 
    return this.fire.setPersistence('local').then(()=> 
      this.fire.signInWithEmailAndPassword(email,password)
      .then(a => console.log('logged in!'))
      .then(a => this.router.navigate(['/dashboard']))
      .catch((err) => 
        this.toastService.publish('The credentials you have entered does not match any user in our database','userDoesNotExist');
      )
    )
  

  public getUserData(id:string):Observable<any> 
    return this.afs.collection('users')
    .doc(id)
    .snapshotChanges()
    .pipe(
      map((doc: any) => 
        // console.log(doc)
        return  id: doc.payload.id, ...doc.payload.data() ;
      )
    );
  

  public sendPasswordReset(email: string) 
    return this.fire.sendPasswordResetEmail(email).then(res =>  console.log(res), this.toastService.publish('Email has been sent to ' + email,'formSuccess')).catch((err) => 
      this.toastService.publish('The credentials you have entered does not match any user in our database','userDoesNotExist');)
  
//firstName:string,lastName:string,email:string,contactNumber:string

  public changeEmail(currentEmail:string, newEmail:string, password:string,id:string): Promise<any> 
    return this.fire.signInWithEmailAndPassword(currentEmail,password).then((res) => 
      res.user?.updateEmail(newEmail).then(() => 
        this.afs.collection('users').doc(id).set(
          email: newEmail
        ,  merge: true ) )
        .catch(() => 
          this.toastService.publish('The email ' + newEmail + ' is already registered in our database!','userDoesNotExist')
        )
    )
    .then(() => this.toastService.publish('Your email has been updated to ' + newEmail,'formSuccess'))
    .catch(() => this.toastService.publish('The credentials you have entered does not match any user in our database','userDoesNotExist'))
  

 public editMyProfile(displayName:string, contactNumber:string, currentEmail:string, password:string,id:string): Promise<any> 
    return this.fire.signInWithEmailAndPassword(currentEmail,password).then((res) => 
      res.user?.updateProfile(displayName: displayName)
    ).then(() => 
      this.afs.collection('users').doc(id).set(
        contactNumber: contactNumber,
        displayName: displayName,
      ,  merge: true )
    )
    .then(() => this.toastService.publish('Your profile has been updated','formSuccess'))
    .catch(() => this.toastService.publish('The password you have entered is incorrect','userDoesNotExist'))
  
  public deleteMyProfile(email:string,password:string,id:string): Promise<any> 
    return this.fire.signInWithEmailAndPassword(email,password).then((res) => 
      this.afs.collection('users').doc(res.user?.uid).delete()
      .then(() => 
        res.user?.delete()
      )
      .then(() => 
        this.router.navigate(['/login'])
      )
      .then(() => 
        this.toastService.publish('Your account with the credentials ' + email + " has been deleted from our system",'userDoesNotExist')      
      )
    ).catch(() => 
      this.toastService.publish('The credentials you have entered does not match any user in our database','userDoesNotExist');
    )
  

  public changeMyPassword(email:string, oldPassword:string, newPassword:string): Promise<any> 
    return this.fire.signInWithEmailAndPassword(email,oldPassword).then((res) => 
      res.user?.updatePassword(newPassword)
      .then(() => 
        this.toastService.publish('Your password has been updated','formSuccess')
      )
    ).catch(() => 
      this.toastService.publish('The credentials you have entered does not match any user in our database','userDoesNotExist');
    )
  
  

【问题讨论】:

【参考方案1】:

此错误通常是由于您的查询接收到nullundefined 参数值,这可能来自您的应用程序而不是checking for null values 在执行查询之前,或者应用程序在查询之前没有等待authentication to be completed火库。查看您的代码,我会检查 AuthServiceTaskService 模块,因为它们似乎处理查询和身份验证。

该文档提供了一个page,它解释了如何在继续或设置将用于查询的值之前侦听身份验证状态更改:

import  getAuth, onAuthStateChanged  from "firebase/auth";

const auth = getAuth();
onAuthStateChanged(auth, (user) => 
  if (user) 
    // User is signed in, see docs for a list of available properties
    // https://firebase.google.com/docs/reference/js/firebase.User
    const uid = user.uid;
    // ...
   else 
    // User is signed out
    // ...
  
);

否则,您可以共享处理查询的模块,以详细了解查询是如何执行的,以及身份验证是如何发生的。

【讨论】:

你好,我已经添加了处理查询的模块 查看您的taskauth 服务模块后,来自此answer 的建议可能会阻止此错误出现,因为您的查询正在接收未定义的数据。至于为什么数据首先是undefined,这似乎并不清楚。除了可以帮助设置测试环境的其他测试repository 之外,AngularFire repository 确实包含一个示例测试应用程序。 但问题是,auth 服务并没有真正的.where() 功能,只有在taskuser 服务模块中,我该如何应用更改那些服务模块?

以上是关于FirebaseError:使用无效数据调用函数 Query.where()。不支持的字段值:在进行 Karma 测试时未定义的主要内容,如果未能解决你的问题,请参考以下文章

FirebaseError:使用无效数据调用函数 Query.where()。不支持的字段值:在进行 Karma 测试时未定义

未捕获的 FirebaseError:使用无效数据调用的函数 DocumentReference.set()。不支持的字段值:未定义

如何使用 Cordova 将动态数据从表单传递到 Firestore?

Flutter, FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - 调用 Firebase App.ini

FirebaseError:Firebase:未创建 Firebase 应用“[DEFAULT]” - 调用 Firebase App.initializeApp() (app/no-app)。 颤动

将数组上传到 Firestore