export class UpdateUserDto {
  @IsNotEmpty()
  @IsMongoId()
  _id: string;

  @IsOptional()
  @MinLength(2)
  @IsString()
  name?: string;

  @IsOptional()
  @MinLength(2)
  @IsString()
  surname?: string;

  @IsOptional()
  @MinLength(2)
  @IsString()
  patronymic?: string;

  @IsOptional()
  @IsString()
  avatarId?: string;

  @IsOptional()
  @IsNumber()
  sex?: number;

  @IsOptional()
  @MinLength(2)
  @IsString()
  alias?: string;

  @IsOptional()
  @IsMongoId()
  divisionId?: string;

  @IsOptional()
  @IsMongoId()
  roleId?: string;

  @IsOptional()
  @IsDate()
  birthday?: Date;

  @IsOptional()
  @IsDate()
  employmentAt?: Date;

  @IsOptional()
  @IsDate()
  dismissalAt?: Date;

  @IsOptional()
  @IsString()
  hobbies?: string;

  @IsOptional()
  @IsString()
  professionalHobbies?: string;

  @IsOptional()
  @IsString()
  bik?: string;

  @IsOptional()
  @IsString()
  kpp?: string;

  @IsOptional()
  @IsString()
  correspondentAccount?: string;

  @IsOptional()
  @IsString()
  checkingAccount?: string;

  @IsOptional()
  @IsString()
  inn?: string;

  @IsOptional()
  @IsString()
  snils?: string;

  @IsOptional()
  @IsObject()
  passport?: UserPassport;
}


taskData => {
          return taskData.tasks.map(task => {
            return {
              title: task.title,
              content: task.content,
              status: task.status,
              assignedTo: task.assignedTo,
              createdBy: task.createdBy,
              _id: task._id
            };
          });
        }


task => {
            return {
              title: task.title,
              content: task.content,
              status: task.status,
              assignedTo: task.assignedTo,
              createdBy: task.createdBy,
              _id: task._id
            };
          }


transformedTasks => {
        console.log(transformedTasks);
        this.tasks = transformedTasks;
        this.tasksUpdated.next({
          tasks: [...this.tasks]
        });
      }


taskData => {
          return taskData.tasks.map(task => {
            return {
              _id: task._id,
              title: task.title,
              content: task.content,
              status: task.status,
              assignedTo: task.assignedTo,
              createdBy: task.createdBy
            };
          });
        }


task => {
            return {
              _id: task._id,
              title: task.title,
              content: task.content,
              status: task.status,
              assignedTo: task.assignedTo,
              createdBy: task.createdBy
            };
          }


responseData => {
        // tslint:disable-next-line:variable-name
        const _id = responseData.taskId;
        task._id = _id;
        this.tasks.push(task);
        this.tasksUpdated.next({
          tasks: [...this.tasks]
        });
        this.router.navigate(['/']);
      }


response => {
        if (response) {
          console.log(response);
          const updatedTasks = [...this.tasks];
          const oldTaskIndex = updatedTasks.findIndex(t => t._id === task._id);
          updatedTasks[oldTaskIndex] = task;
          this.tasks = updatedTasks;
          this.tasksUpdated.next({
            tasks: [...this.tasks]
          });
          this.router.navigate(['/']);
        } else {
          console.log('Problem with response');
        }
      }


t => t._id === task._id


err => {
        console.log(err);
      }


taskData => {}


() => {
      const updatedTasks = this.tasks.filter(t => t._id !== task._id);
      this.tasks = updatedTasks;
      this.tasksUpdated.next({
        tasks: [...this.tasks]
      });
      this.router.navigate(['/']);
    }


t => t._id !== task._id


@Injectable({
  providedIn: 'root'
})
export class TaskService {
  constructor(
    private http: HttpClient,
    private router: Router,
    private authService: AuthService
  ) {}
  private tasks: Task[] = [];
  private tasksUpdated = new Subject<{ tasks: Task[] }>();
  private baseUrl = 'api/v2/task';
  private boards$: Board[] = require('../../data.json');
  currentBoard = this.boards$[0];
  _;

  getBoards(): Board[] {
    return this.boards$;
  }

  getTasks() {
    this.http
      .get<{ message: string; tasks: any }>(`${this.baseUrl}`)
      .pipe(
        map(taskData => {
          return taskData.tasks.map(task => {
            return {
              title: task.title,
              content: task.content,
              status: task.status,
              assignedTo: task.assignedTo,
              createdBy: task.createdBy,
              _id: task._id
            };
          });
        })
      )
      .subscribe(transformedTasks => {
        console.log(transformedTasks);
        this.tasks = transformedTasks;
        this.tasksUpdated.next({
          tasks: [...this.tasks]
        });
      });
  }

  getEmpTask(assigned: string) {
    this.http
      .get<{ message: string; tasks: any }>(
        `${this.baseUrl}/emptask/${assigned}`
      )
      .pipe(
        map(taskData => {
          return taskData.tasks.map(task => {
            return {
              _id: task._id,
              title: task.title,
              content: task.content,
              status: task.status,
              assignedTo: task.assignedTo,
              createdBy: task.createdBy
            };
          });
        })
      )
      .subscribe(transformedTasks => {
        console.log(transformedTasks);
        this.tasks = transformedTasks;
        this.tasksUpdated.next({
          tasks: [...this.tasks]
        });
      });
  }

  getTaskUpdateListener() {
    return this.tasksUpdated.asObservable();
  }

  getTask(id: string) {
    return this.http.get<{
      _id: string;
      title: string;
      content: string;
      status: string;
      assignedTo: string;
      createdBy: string;
      lastModifiedBy: string;
    }>(`${this.baseUrl}/_id`);
  }

  addTask(
    title: string,
    content: string,
    assignedTo: string,
    createdBy: string,
    status: string
  ) {
    const task: Task = {
      _id: null,
      title,
      content,
      status,
      assignedTo,
      createdBy: this.authService.getEmpid()
    };
    this.http
      .post<{ message: string; taskId: string }>(`${this.baseUrl}`, task)
      .subscribe(responseData => {
        // tslint:disable-next-line:variable-name
        const _id = responseData.taskId;
        task._id = _id;
        this.tasks.push(task);
        this.tasksUpdated.next({
          tasks: [...this.tasks]
        });
        this.router.navigate(['/']);
      });
  }

  updateTask(task: Task) {
    console.log(task._id);
    this.http.put(`${this.baseUrl}/` + task._id, task).subscribe(
      response => {
        if (response) {
          console.log(response);
          const updatedTasks = [...this.tasks];
          const oldTaskIndex = updatedTasks.findIndex(t => t._id === task._id);
          updatedTasks[oldTaskIndex] = task;
          this.tasks = updatedTasks;
          this.tasksUpdated.next({
            tasks: [...this.tasks]
          });
          this.router.navigate(['/']);
        } else {
          console.log('Problem with response');
        }
      },
      err => {
        console.log(err);
      }
    );
  }

  updateTaskStatus(task: Task) {
    this.http
      .patch<{ message: string; task: Task }>(
        `${this.baseUrl}/task.id`,

        task
      )
      .subscribe(taskData => {});
    console.log(task._id);
  }

  deleteTask(task: Task) {
    this.http.delete(`${this.baseUrl}/` + task._id).subscribe(() => {
      const updatedTasks = this.tasks.filter(t => t._id !== task._id);
      this.tasks = updatedTasks;
      this.tasksUpdated.next({
        tasks: [...this.tasks]
      });
      this.router.navigate(['/']);
    });
  }
}


export interface Step {
  id: string;
  title: string;
  tasks: Task[];
}


export interface Board {
  steps: Step[];
}


getBoards(): Board[] {
    return this.boards$;
  }


getTasks() {
    this.http
      .get<{ message: string; tasks: any }>(`${this.baseUrl}`)
      .pipe(
        map(taskData => {
          return taskData.tasks.map(task => {
            return {
              title: task.title,
              content: task.content,
              status: task.status,
              assignedTo: task.assignedTo,
              createdBy: task.createdBy,
              _id: task._id
            };
          });
        })
      )
      .subscribe(transformedTasks => {
        console.log(transformedTasks);
        this.tasks = transformedTasks;
        this.tasksUpdated.next({
          tasks: [...this.tasks]
        });
      });
  }


getEmpTask(assigned: string) {
    this.http
      .get<{ message: string; tasks: any }>(
        `${this.baseUrl}/emptask/${assigned}`
      )
      .pipe(
        map(taskData => {
          return taskData.tasks.map(task => {
            return {
              _id: task._id,
              title: task.title,
              content: task.content,
              status: task.status,
              assignedTo: task.assignedTo,
              createdBy: task.createdBy
            };
          });
        })
      )
      .subscribe(transformedTasks => {
        console.log(transformedTasks);
        this.tasks = transformedTasks;
        this.tasksUpdated.next({
          tasks: [...this.tasks]
        });
      });
  }


getTaskUpdateListener() {
    return this.tasksUpdated.asObservable();
  }


getTask(id: string) {
    return this.http.get<{
      _id: string;
      title: string;
      content: string;
      status: string;
      assignedTo: string;
      createdBy: string;
      lastModifiedBy: string;
    }>(`${this.baseUrl}/_id`);
  }


addTask(
    title: string,
    content: string,
    assignedTo: string,
    createdBy: string,
    status: string
  ) {
    const task: Task = {
      _id: null,
      title,
      content,
      status,
      assignedTo,
      createdBy: this.authService.getEmpid()
    };
    this.http
      .post<{ message: string; taskId: string }>(`${this.baseUrl}`, task)
      .subscribe(responseData => {
        // tslint:disable-next-line:variable-name
        const _id = responseData.taskId;
        task._id = _id;
        this.tasks.push(task);
        this.tasksUpdated.next({
          tasks: [...this.tasks]
        });
        this.router.navigate(['/']);
      });
  }


updateTask(task: Task) {
    console.log(task._id);
    this.http.put(`${this.baseUrl}/` + task._id, task).subscribe(
      response => {
        if (response) {
          console.log(response);
          const updatedTasks = [...this.tasks];
          const oldTaskIndex = updatedTasks.findIndex(t => t._id === task._id);
          updatedTasks[oldTaskIndex] = task;
          this.tasks = updatedTasks;
          this.tasksUpdated.next({
            tasks: [...this.tasks]
          });
          this.router.navigate(['/']);
        } else {
          console.log('Problem with response');
        }
      },
      err => {
        console.log(err);
      }
    );
  }


updateTaskStatus(task: Task) {
    this.http
      .patch<{ message: string; task: Task }>(
        `${this.baseUrl}/task.id`,

        task
      )
      .subscribe(taskData => {});
    console.log(task._id);
  }


deleteTask(task: Task) {
    this.http.delete(`${this.baseUrl}/` + task._id).subscribe(() => {
      const updatedTasks = this.tasks.filter(t => t._id !== task._id);
      this.tasks = updatedTasks;
      this.tasksUpdated.next({
        tasks: [...this.tasks]
      });
      this.router.navigate(['/']);
    });
  }


@Entity()
export class Todo {

  @PrimaryGeneratedColumn()
  id: number

  @Column()
  content: string

  @Column()
  title: string

  @Column()
  done: boolean

  @CreateDateColumn()
  createdAt: Date

  @UpdateDateColumn()
  updateAt: Date

}


theme => ({
  root: { position: "fixed", left: 0, right: 0, top: 0, bottom: 0 },
  canvas: { width: "100%", height: "100%" }
})


() => {

  const cn = useStyles()
  const container = useRef<HTMLDivElement>(null)
  const canvas = useRef<HTMLCanvasElement>(null)

  useEffect(() => {
    if (!canvas.current) return
    if (!Music.musicfile) {
      window.history.back()
      setTimeout(() => {
        userMessage(i18n.t("No music selected"))
      }, 100)
      return
    }
    const musicSrc = URL.createObjectURL(Music.musicfile)
    const game = new Game(canvas.current, {
      judgeOffset: scope.settings.game.judge_offset,
      visualOffset: scope.settings.game.visual_offset,
      speed: scope.settings.game.speed,
      resolution: scope.settings.game.resolution,
      noteScale: scope.settings.game.note_scale,
      barOpacity: scope.settings.game.bar_opaciry,
      backgroundDim: scope.settings.general.background_dim,
      effectVolume: scope.settings.general.effect_volume,
      showSimLine: scope.settings.game.show_sim_line,
      laneEffect: scope.settings.game.lane_effect,
      mirror: scope.settings.game.mirror,
      beatNote: scope.settings.game.beat_note
    }, {
      musicSrc, backgroundSrc: Background.src, songName: scope.meta.name, skin: "/assets/skins/default",
      mapContent: () => toGameContent(scope.map as any)
    })
    game.ondestroyed = () => {
      window.history.back()
    }
    game.start()
    return () => {
      URL.revokeObjectURL(musicSrc)
    }
  }, [])

  return

    <canvas ref={canvas} className={cn.canvas} />

}


() => {
    if (!canvas.current) return
    if (!Music.musicfile) {
      window.history.back()
      setTimeout(() => {
        userMessage(i18n.t("No music selected"))
      }, 100)
      return
    }
    const musicSrc = URL.createObjectURL(Music.musicfile)
    const game = new Game(canvas.current, {
      judgeOffset: scope.settings.game.judge_offset,
      visualOffset: scope.settings.game.visual_offset,
      speed: scope.settings.game.speed,
      resolution: scope.settings.game.resolution,
      noteScale: scope.settings.game.note_scale,
      barOpacity: scope.settings.game.bar_opaciry,
      backgroundDim: scope.settings.general.background_dim,
      effectVolume: scope.settings.general.effect_volume,
      showSimLine: scope.settings.game.show_sim_line,
      laneEffect: scope.settings.game.lane_effect,
      mirror: scope.settings.game.mirror,
      beatNote: scope.settings.game.beat_note
    }, {
      musicSrc, backgroundSrc: Background.src, songName: scope.meta.name, skin: "/assets/skins/default",
      mapContent: () => toGameContent(scope.map as any)
    })
    game.ondestroyed = () => {
      window.history.back()
    }
    game.start()
    return () => {
      URL.revokeObjectURL(musicSrc)
    }
  }


() => {
        userMessage(i18n.t("No music selected"))
      }


() => toGameContent(scope.map as any)


() => {
      window.history.back()
    }


() => {
      URL.revokeObjectURL(musicSrc)
    }


/**
 * @license
 * Copyright 2018 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.
 */
export interface TokenDetails {
  token: string;
  createTime: number;
  /** Does not exist in Safari since it's not using Push API. */
  subscriptionOptions?: SubscriptionOptions;
}


/**
 * Additional options and values required by a Push API subscription.
 */
export interface SubscriptionOptions {
  vapidKey: string;
  swScope: string;
  endpoint: string;
  auth: string;
  p256dh: string;
}


export default function autoCssModulesHandler(
  opts: IEsbuildLoaderHandlerParams,
) {
  let { code } = opts;

  let offset = 0;
  opts.imports.forEach((i) => {
    if (i.d < 0 && isStyleFile({ filename: i.n })) {
      // import x from './index.less'
      //   => import x from '
      const importSegment = code.substring(i.ss + offset, i.s + offset);
      // is css module
      if (~importSegment.indexOf(' from')) {
        code = `${code.substring(
          0,
          i.e + offset,
        )}${CSS_MODULES_QUERY}${code.substring(i.e + offset)}`;
        offset += QUERY_LENGTH;
      }
    }
  });

  return code;
}


(i) => {
    if (i.d < 0 && isStyleFile({ filename: i.n })) {
      // import x from './index.less'
      //   => import x from '
      const importSegment = code.substring(i.ss + offset, i.s + offset);
      // is css module
      if (~importSegment.indexOf(' from')) {
        code = `${code.substring(
          0,
          i.e + offset,
        )}${CSS_MODULES_QUERY}${code.substring(i.e + offset)}`;
        offset += QUERY_LENGTH;
      }
    }
  }


export default class ClaimKeybase extends ClaimCommand {
  static description = 'Claim a keybase username and add the claim to a local metadata file'
  static flags = {
    ...ClaimCommand.flags,
    username: flags.string({
      required: true,
      description: 'The keybase username you want to claim',
    }),
  }
  static args = ClaimCommand.args
  static examples = [
    'claim-keybase ~/metadata.json --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95 --username myusername',
  ]
  self = ClaimKeybase

  async run() {
    const res = this.parse(ClaimKeybase)
    const username = res.flags.username
    const metadata = await this.readMetadata()
    const accountAddress = toChecksumAddress(metadata.data.meta.address)
    const claim = createKeybaseClaim(username)
    const signature = await this.signer.sign(hashOfClaim(claim))
    await this.addClaim(metadata, claim)
    this.writeMetadata(metadata)

    try {
      await this.uploadProof(claim, signature, username, accountAddress)
    } catch (error) {
      this.printManualInstruction(claim, signature, username, accountAddress)
    }
  }

  async attemptAutomaticProofUpload(
    claim: KeybaseClaim,
    signature: string,
    username: string,
    address: string
  ) {
    const signedClaim = { claim, signature }
    try {
      cli.action.start(`Attempting to automate keybase proof`)
      const publicFolderPrefix = `/keybase/public/${username}/`
      await this.ensureKeybaseFilePathToProof(publicFolderPrefix)
      const fileName = proofFileName(address)
      const tmpPath = `${tmpdir()}/${fileName}`
      writeFileSync(tmpPath, JSON.stringify(signedClaim))
      await execCmdWithError(
        'keybase',
        ['fs', 'cp', tmpPath, publicFolderPrefix + keybaseFilePathToProof + '/' + fileName],
        { silent: true }
      )
      cli.action.stop()

      cli.action.start(`Claim successfully copied to the keybase file system, verifying proof`)
      // Wait for changes to propagate
      await sleep(3000)
      const verificationError = await verifyKeybaseClaim(this.kit, claim, address)
      if (verificationError) {
        throw new Error(`Claim is not verifiable: ${verificationError}`)
      }
      cli.action.stop()
      console.info('Claim is verifiable!')
    } catch (error) {
      cli.action.stop(`Error: ${error}`)
      throw error
    }
  }
  async uploadProof(claim: KeybaseClaim, signature: string, username: string, address: string) {
    try {
      if (
        (await commandExists('keybase')) &&
        (await binaryPrompt(
          `Found keybase CLI. Do you want me to attempt to publish the claim onto the keybase fs?`
        ))
      ) {
        await this.attemptAutomaticProofUpload(claim, signature, username, address)
      } else {
        this.printManualInstruction(claim, signature, username, address)
      }
    } catch (error) {
      cli.action.stop('Error')
      console.error(
        'Could not automatically finish the proving, please complete this step manually.\n\n ' +
          error
      )
      this.printManualInstruction(claim, signature, username, address)
    }
  }

  printManualInstruction(
    claim: KeybaseClaim,
    signature: string,
    username: string,
    address: string
  ) {
    const fileName = proofFileName(address)
    writeFileSync(fileName, JSON.stringify({ claim, signature }))
    console.info(
      `\nProving a keybase claim requires you to publish the signed claim on your Keybase file system to prove ownership. We saved it for you under ${fileName}. It should be hosted in your public folder at ${keybaseFilePathToProof}/${fileName}, so that it is available under ${targetURL(
        username,
        address
      )}\n`
    )
  }

  async ensureKeybaseFilePathToProof(base: string) {
    const segments = keybaseFilePathToProof.split('/')
    let currentPath = base
    for (let i = 0; i < segments.length - 1; i++) {
      currentPath += segments[i] + '/'
      if (!(await execWith0Exit('keybase', ['fs', 'ls', currentPath], { silent: true }))) {
        await execCmdWithError('keybase', ['fs', 'mkdir', currentPath], { silent: true })
      }
    }
  }
}


async run() {
    const res = this.parse(ClaimKeybase)
    const username = res.flags.username
    const metadata = await this.readMetadata()
    const accountAddress = toChecksumAddress(metadata.data.meta.address)
    const claim = createKeybaseClaim(username)
    const signature = await this.signer.sign(hashOfClaim(claim))
    await this.addClaim(metadata, claim)
    this.writeMetadata(metadata)

    try {
      await this.uploadProof(claim, signature, username, accountAddress)
    } catch (error) {
      this.printManualInstruction(claim, signature, username, accountAddress)
    }
  }


async attemptAutomaticProofUpload(
    claim: KeybaseClaim,
    signature: string,
    username: string,
    address: string
  ) {
    const signedClaim = { claim, signature }
    try {
      cli.action.start(`Attempting to automate keybase proof`)
      const publicFolderPrefix = `/keybase/public/${username}/`
      await this.ensureKeybaseFilePathToProof(publicFolderPrefix)
      const fileName = proofFileName(address)
      const tmpPath = `${tmpdir()}/${fileName}`
      writeFileSync(tmpPath, JSON.stringify(signedClaim))
      await execCmdWithError(
        'keybase',
        ['fs', 'cp', tmpPath, publicFolderPrefix + keybaseFilePathToProof + '/' + fileName],
        { silent: true }
      )
      cli.action.stop()

      cli.action.start(`Claim successfully copied to the keybase file system, verifying proof`)
      // Wait for changes to propagate
      await sleep(3000)
      const verificationError = await verifyKeybaseClaim(this.kit, claim, address)
      if (verificationError) {
        throw new Error(`Claim is not verifiable: ${verificationError}`)
      }
      cli.action.stop()
      console.info('Claim is verifiable!')
    } catch (error) {
      cli.action.stop(`Error: ${error}`)
      throw error
    }
  }


async uploadProof(claim: KeybaseClaim, signature: string, username: string, address: string) {
    try {
      if (
        (await commandExists('keybase')) &&
        (await binaryPrompt(
          `Found keybase CLI. Do you want me to attempt to publish the claim onto the keybase fs?`
        ))
      ) {
        await this.attemptAutomaticProofUpload(claim, signature, username, address)
      } else {
        this.printManualInstruction(claim, signature, username, address)
      }
    } catch (error) {
      cli.action.stop('Error')
      console.error(
        'Could not automatically finish the proving, please complete this step manually.\n\n ' +
          error
      )
      this.printManualInstruction(claim, signature, username, address)
    }
  }


printManualInstruction(
    claim: KeybaseClaim,
    signature: string,
    username: string,
    address: string
  ) {
    const fileName = proofFileName(address)
    writeFileSync(fileName, JSON.stringify({ claim, signature }))
    console.info(
      `\nProving a keybase claim requires you to publish the signed claim on your Keybase file system to prove ownership. We saved it for you under ${fileName}. It should be hosted in your public folder at ${keybaseFilePathToProof}/${fileName}, so that it is available under ${targetURL(
        username,
        address
      )}\n`
    )
  }


async ensureKeybaseFilePathToProof(base: string) {
    const segments = keybaseFilePathToProof.split('/')
    let currentPath = base
    for (let i = 0; i < segments.length - 1; i++) {
      currentPath += segments[i] + '/'
      if (!(await execWith0Exit('keybase', ['fs', 'ls', currentPath], { silent: true }))) {
        await execCmdWithError('keybase', ['fs', 'mkdir', currentPath], { silent: true })
      }
    }
  }


@Injectable()
export class PostsService {
    constructor(
      @InjectModel('Post')
      private readonly postModel: Model<PostInterface>,
      private readonly userService: UsersService,
      private readonly promotionService: PromotionsService,
    ) {
    }

    // TODO:: handle exceptions using try-catch blocks (something like return error: error.message)
    async findAll(): Promise<PostInterface[]> {
        return await this.postModel.find().exec();
    }

    async findUserPosts(userId: string) {
        return await this.postModel.find({ author: userId }).exec();
    }

    async findById(id: string): Promise<PostInterface | undefined> {
        const post = await this.postModel.findById(id).exec();
        if (!post) {
            throw new NotFoundException('Post doesn\'t exist');
        }
        return post;
    }

    async findBySlug(slug: string): Promise<PostInterface | undefined> {
        const post = await this.postModel.findOne({ slug }).exec();
        if (!post) {
            throw new NotFoundException('Post doesn\'t exist');
        }
        return post;
    }

    async create(postData, id: string): Promise<PostInterface | undefined> {
        let newPost = new this.postModel(postData);
        newPost.author = id;
        newPost.slug = `${newPost.title.replace(/ /g, '_')}_${Date.now().toString()}`;
        newPost = await newPost.save();
        if (!newPost) {
            throw new InternalServerErrorException('Unable to create new Post');
        }
        return newPost;
    }

    async update(postData: CreatePostDto, id: string): Promise<PostInterface | undefined> {
        const post = await this.postModel.findById(id);
        if (!post) {
            throw new NotFoundException('Post doesn\'t exist');
        }
        post.body = postData.body;
        post.title = postData.title;
        const updatedPost = await post.save();
        if (!updatedPost) {
            throw new InternalServerErrorException('Unable to update post');
        }
        return updatedPost;
    }

    async delete(id: string) {
        const deleted = this.postModel.findByIdAndRemove(id);
        if (!deleted) {
            throw new InternalServerErrorException('Unable to delete post');
        }
        return deleted;
    }

    async promote(postId: string, medium: string, userId: string) {
        if (!medium) {
            throw new BadRequestException('Promotion medium is required');
        }
        // create promotion
        const promotion = await this.promotionService.new({
            postId,
            promoter: userId,
            medium,
        });
        // TODO:: use promise.all() for all async functions
        if (!promotion) {
            throw new InternalServerErrorException('Unable to create new promotion');
        }
        const post = await this.postModel.findById(postId);
        if (post) {
            post.promotions.push(promotion.id);
            await post.save();
        }
        const user = await this.userService.userModel.findById(userId);
        if (user) {
            user.promotions = user.promotions + 1;
            await user.save();
        }
        return promotion;
    }

    async getPromotionBasedPosts(): Promise<PostInterface[]> {
        try {
            return await this.postModel
              .find()
              .populate('author', ['firstname', 'lastname', 'promotions'])
              .sort({ 'author.promotions': 'desc' })
              .exec();
        } catch (error) {
            throw new InternalServerErrorException('Unable to fetch posts');

        }

    }
}


// TODO:: handle exceptions using try-catch blocks (something like return error: error.message)
async findAll(): Promise<PostInterface[]> {
        return await this.postModel.find().exec();
    }


async findUserPosts(userId: string) {
        return await this.postModel.find({ author: userId }).exec();
    }


async findById(id: string): Promise<PostInterface | undefined> {
        const post = await this.postModel.findById(id).exec();
        if (!post) {
            throw new NotFoundException('Post doesn\'t exist');
        }
        return post;
    }


async findBySlug(slug: string): Promise<PostInterface | undefined> {
        const post = await this.postModel.findOne({ slug }).exec();
        if (!post) {
            throw new NotFoundException('Post doesn\'t exist');
        }
        return post;
    }


async create(postData, id: string): Promise<PostInterface | undefined> {
        let newPost = new this.postModel(postData);
        newPost.author = id;
        newPost.slug = `${newPost.title.replace(/ /g, '_')}_${Date.now().toString()}`;
        newPost = await newPost.save();
        if (!newPost) {
            throw new InternalServerErrorException('Unable to create new Post');
        }
        return newPost;
    }


async update(postData: CreatePostDto, id: string): Promise<PostInterface | undefined> {
        const post = await this.postModel.findById(id);
        if (!post) {
            throw new NotFoundException('Post doesn\'t exist');
        }
        post.body = postData.body;
        post.title = postData.title;
        const updatedPost = await post.save();
        if (!updatedPost) {
            throw new InternalServerErrorException('Unable to update post');
        }
        return updatedPost;
    }


async delete(id: string) {
        const deleted = this.postModel.findByIdAndRemove(id);
        if (!deleted) {
            throw new InternalServerErrorException('Unable to delete post');
        }
        return deleted;
    }


async promote(postId: string, medium: string, userId: string) {
        if (!medium) {
            throw new BadRequestException('Promotion medium is required');
        }
        // create promotion
        const promotion = await this.promotionService.new({
            postId,
            promoter: userId,
            medium,
        });
        // TODO:: use promise.all() for all async functions
        if (!promotion) {
            throw new InternalServerErrorException('Unable to create new promotion');
        }
        const post = await this.postModel.findById(postId);
        if (post) {
            post.promotions.push(promotion.id);
            await post.save();
        }
        const user = await this.userService.userModel.findById(userId);
        if (user) {
            user.promotions = user.promotions + 1;
            await user.save();
        }
        return promotion;
    }


async getPromotionBasedPosts(): Promise<PostInterface[]> {
        try {
            return await this.postModel
              .find()
              .populate('author', ['firstname', 'lastname', 'promotions'])
              .sort({ 'author.promotions': 'desc' })
              .exec();
        } catch (error) {
            throw new InternalServerErrorException('Unable to fetch posts');

        }

    }


() => 0.5 - Math.random()


(logo, logoIndex) => (
                <div
                  key={`logo${logoIndex}`


(highlight, highlightIndex) => (
                <div
                  className='col-12 col-md-4'


(feature, featureIndex) => {
              return (
                <div
                  className='row py-5 py-lg-8 align-items-center justify-content-between'
                  key={`feature${featureIndex}`}


(testimonial, testimonialIndex) => {
                return (
                  testimonialIndex > 0 && (
                    <div
                      className='col-md-6 col-lg-4 col-xl-3 d-flex text-center'
                      key={`testimonial${testimonialIndex}`}


class Index extends React.Component<{}, { companyLogos: any }> {
  constructor(props) {
    super(props);
    this.state = { companyLogos };
  }

  componentDidMount() {
    // ensure that random things are on client only
    this.setState({
      companyLogos: companyLogos.sort(() => 0.5 - Math.random())
    });
  }

  render() {
    return (
      <Layout>
        <Meta title={meta.title} description={meta.description} />

        <Hero
          title="Create mock APIs in seconds"
          subtitle='Mockoon is the easiest and quickest way to design and run mock REST APIs.
No remote deployment, no account required, free and open-source.'
          cta={[
            {
              text: 'Download',
              link: '/download/'
            },
            {
              text: 'Documentation',
              link: '/docs/latest/about/'
            }
          ]}
          mainPicture='/images/hp-hero.png'
          mainPictureAlt='Mockoon screenshot with people working'
        />

              Trusted by awesome teams

              {this.state.companyLogos.slice(0, 6).map((logo, logoIndex) => (
                <div
                  key={`logo${logoIndex}`}
                  className='col-6 col-sm-3 col-lg-2 mb-4 mb-md-0 px-xl-8 text-center'
                >

              ))}


componentDidMount() {
    // ensure that random things are on client only
    this.setState({
      companyLogos: companyLogos.sort(() => 0.5 - Math.random())
    });
  }


render() {
    return (
      <Layout>
        <Meta title={meta.title} description={meta.description} />

        <Hero
          title="Create mock APIs in seconds"
          subtitle='Mockoon is the easiest and quickest way to design and run mock REST APIs.
No remote deployment, no account required, free and open-source.'
          cta={[
            {
              text: 'Download',
              link: '/download/'
            },
            {
              text: 'Documentation',
              link: '/docs/latest/about/'
            }
          ]}
          mainPicture='/images/hp-hero.png'
          mainPictureAlt='Mockoon screenshot with people working'
        />

              Trusted by awesome teams

              {this.state.companyLogos.slice(0, 6).map((logo, logoIndex) => (
                <div
                  key={`logo${logoIndex}`}
                  className='col-6 col-sm-3 col-lg-2 mb-4 mb-md-0 px-xl-8 text-center'
                >

              ))}


function initSidebar() {
  selectAll("aside a").forEach(el => {
    el.classList.remove("selected");
    const href = el.getAttribute("href");
    if (href && href.includes(window.location.pathname)) {
      el.classList.add("selected");
    }
  });
  const errors = select("#data-error-count").value;
  select("aside li.error").classList.toggle("hidden", errors === "0");
  select("aside li.error span").innerHTML = errors;
}


el => {
    el.classList.remove("selected");
    const href = el.getAttribute("href");
    if (href && href.includes(window.location.pathname)) {
      el.classList.add("selected");
    }
  }


() => {
  const asideButton = select("#aside-button");
  asideButton.addEventListener("click", () => {
    select("aside").classList.toggle("active");
    asideButton.classList.toggle("active");
  });
}


() => {
    select("aside").classList.toggle("active");
    asideButton.classList.toggle("active");
  }


() => {
  initSidebar();
}


() => {
  fetchAPI("errors").then(errors => {
    select("#data-error-count").value = errors;
    initSidebar();
  });
}


errors => {
    select("#data-error-count").value = errors;
    initSidebar();
  }


quickSearchListItem(itemName: string, itemType: string): string {
    return `[data-test="item-name-${itemName}-${itemType}"]`;
  }


({ card, onFinish }: Props) => {
  const { feature } = card;
  const [isOpen, openModal] = useState(false);
  const { getAllModules, loading, allModules } = useModules();

  useEffect(() => {
    if (allModules) openModal(true);
  }, [allModules]);

  const handleClose = () => {
    openModal(false);
    onFinish();
  };

  const renderModules = () =>
    feature?.modules && (
      <Panel.Content>
        {map(feature?.modules, ({ name })


() => {
    if (allModules) openModal(true);
  }


() => {
    openModal(false);
    onFinish();
  }


() =>
    feature?.modules && (
      <Panel.Content>
        {map(feature?.modules, ({ name })


interface Props {
  card: Card;
  onFinish: Function;
}


map(feature?.modules,


() => {
      TestBed.configureTestingModule({
        imports: [BidiModule, NzMenuModule, NoopAnimationsModule, NzIconTestModule],
        declarations: [
          NzTestBasicMenuHorizontalComponent,
          NzTestBasicMenuInlineComponent,
          NzTestMenuInlineCollapsedComponent,
          NzTestMenuSiderCurrentComponent,
          NzTestMenuThemeComponent,
          NzTestMenuSwitchModeComponent,
          NzTestMenuHorizontalComponent,
          NzTestMenuInlineComponent,
          NzDemoMenuNgForComponent,
          NzTestNgIfMenuComponent,
          NzTestSubMenuSelectedComponent,
          NzTestMenuRtlComponent
        ]
      });

      TestBed.compileComponents();

      inject([OverlayContainer], (oc: OverlayContainer) => {
        overlayContainer = oc;
        overlayContainerElement = oc.getContainerElement();
      })();
    }


(oc: OverlayContainer) => {
        overlayContainer = oc;
        overlayContainerElement = oc.getContainerElement();
      }


(currentOverlayContainer: OverlayContainer) => {
    currentOverlayContainer.ngOnDestroy();
    overlayContainer.ngOnDestroy();
  }


() => {
      let fixture: ComponentFixture<NzTestBasicMenuHorizontalComponent>;
      let items: DebugElement[];
      let submenu: DebugElement;
      let menu: DebugElement;
      beforeEach(() => {
        fixture = TestBed.createComponent(NzTestBasicMenuHorizontalComponent);
        items = fixture.debugElement.queryAll(By.directive(NzMenuItemDirective));
        submenu = fixture.debugElement.query(By.directive(NzSubMenuComponent));
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
      });
      it('should className correct', () => {
        fixture.detectChanges();
        expect(items.every(item => item.nativeElement.classList.contains('ant-menu-item'))).toBe(true);
        expect(items[1].nativeElement.classList.contains('ant-menu-item-disabled')).toBe(true);
        expect(submenu.nativeElement.classList.contains('ant-menu-submenu-horizontal')).toBe(true);
        expect(submenu.nativeElement.classList.contains('ant-menu-submenu')).toBe(true);
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-light ant-menu-horizontal');
      });
      it('should menu item select', () => {
        fixture.detectChanges();
        items[0].nativeElement.click();
        fixture.detectChanges();
        expect(items[0].nativeElement.classList.contains('ant-menu-item-selected')).toBe(true);
      });
      it('should menu disabled work', () => {
        fixture.detectChanges();
        items[1].nativeElement.click();
        fixture.detectChanges();
        expect(items[0].nativeElement.classList.contains('ant-menu-item-selected')).toBe(false);
      });
      it('should menu danger work', () => {
        fixture.detectChanges();
        expect(items[3].nativeElement.classList.contains('ant-menu-item-danger')).toBe(true);
      });
    }


() => {
        fixture = TestBed.createComponent(NzTestBasicMenuHorizontalComponent);
        items = fixture.debugElement.queryAll(By.directive(NzMenuItemDirective));
        submenu = fixture.debugElement.query(By.directive(NzSubMenuComponent));
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
      }


() => {
        fixture.detectChanges();
        expect(items.every(item => item.nativeElement.classList.contains('ant-menu-item'))).toBe(true);
        expect(items[1].nativeElement.classList.contains('ant-menu-item-disabled')).toBe(true);
        expect(submenu.nativeElement.classList.contains('ant-menu-submenu-horizontal')).toBe(true);
        expect(submenu.nativeElement.classList.contains('ant-menu-submenu')).toBe(true);
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-light ant-menu-horizontal');
      }


item => item.nativeElement.classList.contains('ant-menu-item')


() => {
        fixture.detectChanges();
        items[0].nativeElement.click();
        fixture.detectChanges();
        expect(items[0].nativeElement.classList.contains('ant-menu-item-selected')).toBe(true);
      }


() => {
        fixture.detectChanges();
        items[1].nativeElement.click();
        fixture.detectChanges();
        expect(items[0].nativeElement.classList.contains('ant-menu-item-selected')).toBe(false);
      }


() => {
        fixture.detectChanges();
        expect(items[3].nativeElement.classList.contains('ant-menu-item-danger')).toBe(true);
      }


() => {
      let fixture: ComponentFixture<NzTestBasicMenuInlineComponent>;
      let items: DebugElement[];
      let submenus: DebugElement[];
      let menu: DebugElement;
      beforeEach(() => {
        fixture = TestBed.createComponent(NzTestBasicMenuInlineComponent);
        items = fixture.debugElement.queryAll(By.directive(NzMenuItemDirective));
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
        submenus = fixture.debugElement.queryAll(By.directive(NzSubMenuComponent));
      });
      it('should className correct', () => {
        fixture.detectChanges();
        expect(submenus.every(subitem => subitem.nativeElement.classList.contains('ant-menu-submenu'))).toBe(true);
        expect(submenus.every(subitem => subitem.nativeElement.classList.contains('ant-menu-submenu-inline'))).toBe(
          true
        );
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-light ant-menu-inline');
      });
      it('should padding left work', () => {
        fixture.detectChanges();
        const firstLevelItems = items;
        const secondLevelItems = firstLevelItems.splice(6, 2);
        expect(firstLevelItems.every(item => item.nativeElement.style.paddingLeft === '48px')).toBe(true);
        expect(secondLevelItems.every(item => item.nativeElement.style.paddingLeft === '72px')).toBe(true);
      });
      it('should click expand', fakeAsync(() => {
        fixture.detectChanges();
        const ul = submenus[0].nativeElement.querySelector('.ant-menu');
        const title = submenus[0].nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).toBe('0px');
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).not.toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(false);
      }));
    }


() => {
        fixture = TestBed.createComponent(NzTestBasicMenuInlineComponent);
        items = fixture.debugElement.queryAll(By.directive(NzMenuItemDirective));
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
        submenus = fixture.debugElement.queryAll(By.directive(NzSubMenuComponent));
      }


() => {
        fixture.detectChanges();
        expect(submenus.every(subitem => subitem.nativeElement.classList.contains('ant-menu-submenu'))).toBe(true);
        expect(submenus.every(subitem => subitem.nativeElement.classList.contains('ant-menu-submenu-inline'))).toBe(
          true
        );
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-light ant-menu-inline');
      }


subitem => subitem.nativeElement.classList.contains('ant-menu-submenu')


subitem => subitem.nativeElement.classList.contains('ant-menu-submenu-inline')


() => {
        fixture.detectChanges();
        const firstLevelItems = items;
        const secondLevelItems = firstLevelItems.splice(6, 2);
        expect(firstLevelItems.every(item => item.nativeElement.style.paddingLeft === '48px')).toBe(true);
        expect(secondLevelItems.every(item => item.nativeElement.style.paddingLeft === '72px')).toBe(true);
      }


item => item.nativeElement.style.paddingLeft === '48px'


item => item.nativeElement.style.paddingLeft === '72px'


() => {
        fixture.detectChanges();
        const ul = submenus[0].nativeElement.querySelector('.ant-menu');
        const title = submenus[0].nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).toBe('0px');
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).not.toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(false);
      }


() => {
      let fixture: ComponentFixture<NzTestMenuInlineCollapsedComponent>;
      let testComponent: NzTestMenuInlineCollapsedComponent;
      let submenus: DebugElement[];
      let menu: DebugElement;
      beforeEach(() => {
        fixture = TestBed.createComponent(NzTestMenuInlineCollapsedComponent);
        testComponent = fixture.debugElement.componentInstance;
        submenus = fixture.debugElement.queryAll(By.directive(NzSubMenuComponent));
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
      });
      it('should className correct', () => {
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-dark ant-menu-inline');
        testComponent.isCollapsed = true;
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe(
          'ant-menu ant-menu-root ant-menu-dark ant-menu-vertical ant-menu-inline-collapsed'
        );
        testComponent.isCollapsed = false;
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-dark ant-menu-inline');
      });
      it('should keep open after change mode', () => {
        fixture.detectChanges();
        let ul = submenus[0].nativeElement.querySelector('.ant-menu');
        const title = submenus[0].nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).toBe('0px');
        testComponent.isCollapsed = true;
        fixture.detectChanges();
        testComponent.isCollapsed = false;
        fixture.detectChanges();
        ul = submenus[0].nativeElement.querySelector('.ant-menu');
        expect(ul.style.height).toBe('0px');
        title.click();
        fixture.detectChanges();
        expect(ul.style.height).not.toBe('0px');
        testComponent.isCollapsed = true;
        fixture.detectChanges();
        testComponent.isCollapsed = false;
        fixture.detectChanges();
        expect(ul.style.height).not.toBe('0px');
      });
    }


() => {
        fixture = TestBed.createComponent(NzTestMenuInlineCollapsedComponent);
        testComponent = fixture.debugElement.componentInstance;
        submenus = fixture.debugElement.queryAll(By.directive(NzSubMenuComponent));
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
      }


() => {
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-dark ant-menu-inline');
        testComponent.isCollapsed = true;
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe(
          'ant-menu ant-menu-root ant-menu-dark ant-menu-vertical ant-menu-inline-collapsed'
        );
        testComponent.isCollapsed = false;
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-dark ant-menu-inline');
      }


() => {
        fixture.detectChanges();
        let ul = submenus[0].nativeElement.querySelector('.ant-menu');
        const title = submenus[0].nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).toBe('0px');
        testComponent.isCollapsed = true;
        fixture.detectChanges();
        testComponent.isCollapsed = false;
        fixture.detectChanges();
        ul = submenus[0].nativeElement.querySelector('.ant-menu');
        expect(ul.style.height).toBe('0px');
        title.click();
        fixture.detectChanges();
        expect(ul.style.height).not.toBe('0px');
        testComponent.isCollapsed = true;
        fixture.detectChanges();
        testComponent.isCollapsed = false;
        fixture.detectChanges();
        expect(ul.style.height).not.toBe('0px');
      }


() => {
      let fixture: ComponentFixture<NzTestMenuSiderCurrentComponent>;
      let submenus: DebugElement[];
      beforeEach(() => {
        fixture = TestBed.createComponent(NzTestMenuSiderCurrentComponent);
        submenus = fixture.debugElement.queryAll(By.directive(NzSubMenuComponent));
      });
      it('should collapsed self work', fakeAsync(() => {
        fixture.detectChanges();
        const ul = submenus[0].nativeElement.querySelector('.ant-menu');
        const title = submenus[0].nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).not.toBe('0px');
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(false);
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).not.toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
      }));
      it('should collapsed other work', fakeAsync(() => {
        fixture.detectChanges();
        const firstUl = submenus[0].nativeElement.querySelector('.ant-menu');
        const secondUl = submenus[1].nativeElement.querySelector('.ant-menu');
        const secondTitle = submenus[1].nativeElement.querySelector('.ant-menu-submenu-title');
        expect(firstUl.style.height).not.toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
        secondTitle.click();
        fixture.detectChanges();
        tick(500);
        expect(firstUl.style.height).toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(false);
        expect(secondUl.style.height).not.toBe('0px');
        expect(submenus[1].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
      }));
    }


() => {
        fixture = TestBed.createComponent(NzTestMenuSiderCurrentComponent);
        submenus = fixture.debugElement.queryAll(By.directive(NzSubMenuComponent));
      }


() => {
        fixture.detectChanges();
        const ul = submenus[0].nativeElement.querySelector('.ant-menu');
        const title = submenus[0].nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).not.toBe('0px');
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(false);
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).not.toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
      }


() => {
        fixture.detectChanges();
        const firstUl = submenus[0].nativeElement.querySelector('.ant-menu');
        const secondUl = submenus[1].nativeElement.querySelector('.ant-menu');
        const secondTitle = submenus[1].nativeElement.querySelector('.ant-menu-submenu-title');
        expect(firstUl.style.height).not.toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
        secondTitle.click();
        fixture.detectChanges();
        tick(500);
        expect(firstUl.style.height).toBe('0px');
        expect(submenus[0].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(false);
        expect(secondUl.style.height).not.toBe('0px');
        expect(submenus[1].nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
      }


() => {
      let fixture: ComponentFixture<NzTestMenuThemeComponent>;
      let testComponent: NzTestMenuThemeComponent;
      let menu: DebugElement;
      beforeEach(() => {
        fixture = TestBed.createComponent(NzTestMenuThemeComponent);
        testComponent = fixture.debugElement.componentInstance;
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
      });
      it('should className correct', () => {
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-dark ant-menu-inline');
        testComponent.theme = false;
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-inline ant-menu-light');
      });
    }


() => {
        fixture = TestBed.createComponent(NzTestMenuThemeComponent);
        testComponent = fixture.debugElement.componentInstance;
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
      }


() => {
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-dark ant-menu-inline');
        testComponent.theme = false;
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-inline ant-menu-light');
      }


() => {
      let fixture: ComponentFixture<NzTestMenuSwitchModeComponent>;
      let testComponent: NzTestMenuSwitchModeComponent;
      let submenus: DebugElement[];
      let menu: DebugElement;
      beforeEach(() => {
        fixture = TestBed.createComponent(NzTestMenuSwitchModeComponent);
        testComponent = fixture.debugElement.componentInstance;
        submenus = fixture.debugElement.queryAll(By.directive(NzSubMenuComponent));
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
      });
      it('should className correct', () => {
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-light ant-menu-inline');
        expect(submenus.every(submenu => submenu.nativeElement.classList.contains('ant-menu-submenu-inline'))).toBe(
          true
        );
        testComponent.mode = true;
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-light ant-menu-vertical');
        expect(submenus.every(submenu => submenu.nativeElement.classList.contains('ant-menu-submenu-inline'))).toBe(
          false
        );
        expect(submenus.every(submenu => submenu.nativeElement.classList.contains('ant-menu-submenu-vertical'))).toBe(
          true
        );
      });
    }


() => {
        fixture = TestBed.createComponent(NzTestMenuSwitchModeComponent);
        testComponent = fixture.debugElement.componentInstance;
        submenus = fixture.debugElement.queryAll(By.directive(NzSubMenuComponent));
        menu = fixture.debugElement.query(By.directive(NzMenuDirective));
      }


() => {
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-light ant-menu-inline');
        expect(submenus.every(submenu => submenu.nativeElement.classList.contains('ant-menu-submenu-inline'))).toBe(
          true
        );
        testComponent.mode = true;
        fixture.detectChanges();
        expect(menu.nativeElement.className).toBe('ant-menu ant-menu-root ant-menu-light ant-menu-vertical');
        expect(submenus.every(submenu => submenu.nativeElement.classList.contains('ant-menu-submenu-inline'))).toBe(
          false
        );
        expect(submenus.every(submenu => submenu.nativeElement.classList.contains('ant-menu-submenu-vertical'))).toBe(
          true
        );
      }


submenu => submenu.nativeElement.classList.contains('ant-menu-submenu-inline')


submenu => submenu.nativeElement.classList.contains('ant-menu-submenu-vertical')


() => {
        fixture = TestBed.createComponent(NzTestMenuHorizontalComponent);
        testComponent = fixture.debugElement.componentInstance;
        submenu = fixture.debugElement.query(By.directive(NzSubMenuComponent));
      }


() => {
        fixture.detectChanges();
        expect(overlayContainerElement.textContent).toBe('');
        testComponent.open = true;
        fixture.detectChanges();
        expect(overlayContainerElement.textContent).not.toBe('');
      }


() => {
        fixture.detectChanges();
        const mouseenterCallback = jasmine.createSpy('mouseenter callback');
        const subs = testComponent.subs.toArray();
        const title = submenu.nativeElement.querySelector('.ant-menu-submenu-title');
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (subs[0].nzSubmenuService as any).isMouseEnterTitleOrOverlay$.subscribe(mouseenterCallback);
        dispatchFakeEvent(title, 'mouseenter');
        fixture.detectChanges();
        expect(mouseenterCallback).toHaveBeenCalledWith(true);
        expect(mouseenterCallback).toHaveBeenCalledTimes(1);
      }


() => {
        fixture.detectChanges();
        const mouseleaveCallback = jasmine.createSpy('mouseleave callback');
        const subs = testComponent.subs.toArray();
        const title = submenu.nativeElement.querySelector('.ant-menu-submenu-title');
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (subs[0].nzSubmenuService as any).isMouseEnterTitleOrOverlay$.subscribe(mouseleaveCallback);
        dispatchFakeEvent(title, 'mouseleave');
        fixture.detectChanges();
        expect(mouseleaveCallback).toHaveBeenCalledWith(false);
        expect(mouseleaveCallback).toHaveBeenCalledTimes(1);
      }


() => {
        testComponent.open = true;
        fixture.detectChanges();
        const nestedCallback = jasmine.createSpy('nested callback');
        const subs = testComponent.subs.toArray();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (subs[0].nzSubmenuService as any).isChildSubMenuOpen$.subscribe(nestedCallback);
        subs[1].nzOpen = true;
        subs[1].nzSubmenuService.isCurrentSubMenuOpen$.next(false);
        fixture.detectChanges();
        expect(nestedCallback).toHaveBeenCalledWith(false);
        expect(nestedCallback).toHaveBeenCalledTimes(1);
      }


() => {
        testComponent.open = true;
        testComponent.disabled = true;
        fixture.detectChanges();
        const nestedCallback = jasmine.createSpy('nested callback');
        const subs = testComponent.subs.toArray();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (subs[0].nzSubmenuService as any).isChildSubMenuOpen$.subscribe(nestedCallback);
        subs[1].nzOpen = true;
        subs[1].nzSubmenuService.isCurrentSubMenuOpen$.next(false);
        fixture.detectChanges();
        expect(nestedCallback).toHaveBeenCalledTimes(1);
      }


() => {
        testComponent.open = true;
        fixture.detectChanges();
        const subs = testComponent.subs.toArray();
        dispatchFakeEvent(testComponent.menuitem1.nativeElement, 'mouseenter');
        fixture.detectChanges();
        testComponent.menuitem1.nativeElement.click();
        fixture.detectChanges();
        tick(500);
        expect(subs[1].isActive).toBe(false);
      }


() => {
        testComponent.open = true;
        fixture.detectChanges();
        const nestedCallback = jasmine.createSpy('nested callback');
        const subs = testComponent.subs.toArray();
        subs[1].nzOpen = true;
        fixture.detectChanges();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (subs[1].nzSubmenuService as any).isChildSubMenuOpen$.subscribe(nestedCallback);
        testComponent.menuitem.nativeElement.click();
        fixture.detectChanges();
        expect(nestedCallback).toHaveBeenCalledWith(false);
        expect(nestedCallback).toHaveBeenCalledTimes(1);
      }


() => {
        testComponent.open = true;
        fixture.detectChanges();
        const nestedCallback = jasmine.createSpy('nested callback');
        const subs = testComponent.subs.toArray();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (subs[1].nzSubmenuService as any).isMouseEnterTitleOrOverlay$.subscribe(nestedCallback);
        subs[1].nzOpen = true;
        testComponent.disableditem.nativeElement.click();
        fixture.detectChanges();
        expect(nestedCallback).toHaveBeenCalledTimes(0);
      }


() => {
        fixture.detectChanges();
        testComponent.open = true;
        fixture.detectChanges();
        const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
        expect(overlayPane.style.width).toBe('200px');
        testComponent.open = false;
        fixture.detectChanges();
        testComponent.width = 300;
        fixture.detectChanges();
        testComponent.open = true;
        fixture.detectChanges();
        expect(overlayPane.style.width).toBe('300px');
      }


() => {
        const fakeLeftTopEvent = {
          connectionPair: {
            originX: 'start',
            originY: 'top',
            overlayX: 'end',
            overlayY: 'top'
          }
        } as ConnectedOverlayPositionChange;
        const fakeRightTopEvent = {
          connectionPair: {
            originX: 'end',
            originY: 'top',
            overlayX: 'start',
            overlayY: 'top'
          }
        } as ConnectedOverlayPositionChange;
        fixture.detectChanges();
        testComponent.open = true;
        const subs = testComponent.subs.toArray();
        subs[1].nzOpen = true;
        fixture.detectChanges();
        subs[1].onPositionChange(fakeLeftTopEvent);
        fixture.detectChanges();
        expect(subs[1].position).toBe('left');
        subs[1].onPositionChange(fakeRightTopEvent);
        fixture.detectChanges();
        expect(subs[1].position).toBe('right');
      }


() => {
        fixture.detectChanges();
        testComponent.open = true;
        fixture.detectChanges();
        expect((overlayContainerElement.querySelector('.submenu') as HTMLUListElement).classList).toContain(
          'ant-menu-sub'
        );
      }


() => {
        testComponent.open = true;
        fixture.detectChanges();
        const subs = testComponent.subs.toArray();
        subs[0].nzOpen = true;
        subs[1].nzOpen = true;
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (subs[1] as any).cdr.markForCheck();
        fixture.detectChanges();
        expect((overlayContainerElement.querySelector('.nested-submenu') as HTMLUListElement).classList).toContain(
          'ant-menu-sub'
        );
      }


() => {
      let fixture: ComponentFixture<NzTestMenuInlineComponent>;
      let testComponent: NzTestMenuInlineComponent;
      let submenu: DebugElement;
      beforeEach(() => {
        fixture = TestBed.createComponent(NzTestMenuInlineComponent);
        testComponent = fixture.debugElement.componentInstance;
        submenu = fixture.debugElement.query(By.directive(NzSubMenuComponent));
      });
      it('should click expand', fakeAsync(() => {
        fixture.detectChanges();
        const ul = submenu.nativeElement.querySelector('.ant-menu');
        const title = submenu.nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).toBe('0px');
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).not.toBe('0px');
        expect(submenu.nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
      }));
      it('should `nzMenuClassName` work', fakeAsync(() => {
        fixture.detectChanges();
        expect(submenu.nativeElement.querySelector('.ant-menu-sub').className).toContain('submenu');
      }));
      it('should `nzMenuClassName` multi class names work', fakeAsync(() => {
        fixture.detectChanges();
        testComponent.submenuClassName = 'submenu submenu-1';
        fixture.detectChanges();
        expect(submenu.nativeElement.querySelector('.ant-menu-sub').className).toContain('submenu');
        expect(submenu.nativeElement.querySelector('.ant-menu-sub').className).toContain('submenu-1');
      }));
      it('should disabled work', fakeAsync(() => {
        testComponent.disabled = true;
        fixture.detectChanges();
        const ul = submenu.nativeElement.querySelector('.ant-menu');
        const title = submenu.nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).toBe('0px');
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).toBe('0px');
        expect(submenu.nativeElement.classList.contains('ant-menu-submenu-open')).toBe(false);
      }));
    }


() => {
        fixture = TestBed.createComponent(NzTestMenuInlineComponent);
        testComponent = fixture.debugElement.componentInstance;
        submenu = fixture.debugElement.query(By.directive(NzSubMenuComponent));
      }


() => {
        fixture.detectChanges();
        const ul = submenu.nativeElement.querySelector('.ant-menu');
        const title = submenu.nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).toBe('0px');
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).not.toBe('0px');
        expect(submenu.nativeElement.classList.contains('ant-menu-submenu-open')).toBe(true);
      }


() => {
        fixture.detectChanges();
        expect(submenu.nativeElement.querySelector('.ant-menu-sub').className).toContain('submenu');
      }


() => {
        fixture.detectChanges();
        testComponent.submenuClassName = 'submenu submenu-1';
        fixture.detectChanges();
        expect(submenu.nativeElement.querySelector('.ant-menu-sub').className).toContain('submenu');
        expect(submenu.nativeElement.querySelector('.ant-menu-sub').className).toContain('submenu-1');
      }


() => {
        testComponent.disabled = true;
        fixture.detectChanges();
        const ul = submenu.nativeElement.querySelector('.ant-menu');
        const title = submenu.nativeElement.querySelector('.ant-menu-submenu-title');
        expect(ul.style.height).toBe('0px');
        title.click();
        fixture.detectChanges();
        tick(500);
        expect(ul.style.height).toBe('0px');
        expect(submenu.nativeElement.classList.contains('ant-menu-submenu-open')).toBe(false);
      }


() => {
      it('should ng for works fine', () => {
        expect(() => {
          TestBed.createComponent(NzDemoMenuNgForComponent).detectChanges();
        }).not.toThrowError();
      });
    }


() => {
        expect(() => {
          TestBed.createComponent(NzDemoMenuNgForComponent).detectChanges();
        }).not.toThrowError();
      }


() => {
          TestBed.createComponent(NzDemoMenuNgForComponent).detectChanges();
        }


() => {
      it('should ng if works fine', () => {
        expect(() => {
          TestBed.createComponent(NzTestNgIfMenuComponent).detectChanges();
        }).not.toThrowError();
      });
    }


() => {
        expect(() => {
          TestBed.createComponent(NzTestNgIfMenuComponent).detectChanges();
        }).not.toThrowError();
      }


() => {
          TestBed.createComponent(NzTestNgIfMenuComponent).detectChanges();
        }


() => {
      it('should default selected active submenu', () => {
        const fixture = TestBed.createComponent(NzTestSubMenuSelectedComponent);
        fixture.detectChanges();
        expect(fixture.debugElement.nativeElement.querySelector('.ant-menu-submenu').classList).toContain(
          'ant-menu-submenu-selected'
        );
      });
    }


() => {
        const fixture = TestBed.createComponent(NzTestSubMenuSelectedComponent);
        fixture.detectChanges();
        expect(fixture.debugElement.nativeElement.querySelector('.ant-menu-submenu').classList).toContain(
          'ant-menu-submenu-selected'
        );
      }


() => {
    let fixture: ComponentFixture<NzTestMenuRtlComponent>;
    let testComponent: NzTestMenuHorizontalComponent;
    let submenu: DebugElement;
    let menu: DebugElement;
    beforeEach(() => {
      fixture = TestBed.createComponent(NzTestMenuRtlComponent);
      testComponent = fixture.debugElement.query(By.directive(NzTestMenuHorizontalComponent)).componentInstance;
      submenu = fixture.debugElement.query(By.directive(NzSubMenuComponent));
      menu = fixture.debugElement.query(By.directive(NzMenuDirective));
    });
    it('should className correct on dir change', () => {
      fixture.detectChanges();
      expect(submenu.nativeElement.classList.contains('ant-menu-submenu-rtl')).toBe(true);
      expect(menu.nativeElement.classList.contains('ant-menu-rtl')).toBe(true);

      fixture.componentInstance.direction = 'ltr';
      fixture.detectChanges();

      expect(submenu.nativeElement.classList.contains('ant-menu-submenu-rtl')).toBe(false);
      expect(menu.nativeElement.classList.contains('ant-menu-rtl')).toBe(false);
    });

    it('should nested submenu work', () => {
      testComponent.open = true;
      fixture.detectChanges();
      const subs = testComponent.subs.toArray();
      subs[0].nzOpen = true;
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      (subs[1] as any).cdr.markForCheck();
      fixture.detectChanges();
      expect((overlayContainerElement.querySelector('.ant-menu-submenu') as HTMLUListElement).classList).toContain(
        'ant-menu-submenu-rtl'
      );
    });
  }


() => {
      fixture = TestBed.createComponent(NzTestMenuRtlComponent);
      testComponent = fixture.debugElement.query(By.directive(NzTestMenuHorizontalComponent)).componentInstance;
      submenu = fixture.debugElement.query(By.directive(NzSubMenuComponent));
      menu = fixture.debugElement.query(By.directive(NzMenuDirective));
    }


() => {
      fixture.detectChanges();
      expect(submenu.nativeElement.classList.contains('ant-menu-submenu-rtl')).toBe(true);
      expect(menu.nativeElement.classList.contains('ant-menu-rtl')).toBe(true);

      fixture.componentInstance.direction = 'ltr';
      fixture.detectChanges();

      expect(submenu.nativeElement.classList.contains('ant-menu-submenu-rtl')).toBe(false);
      expect(menu.nativeElement.classList.contains('ant-menu-rtl')).toBe(false);
    }


() => {
      testComponent.open = true;
      fixture.detectChanges();
      const subs = testComponent.subs.toArray();
      subs[0].nzOpen = true;
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      (subs[1] as any).cdr.markForCheck();
      fixture.detectChanges();
      expect((overlayContainerElement.querySelector('.ant-menu-submenu') as HTMLUListElement).classList).toContain(
        'ant-menu-submenu-rtl'
      );
    }


@Component({
  // eslint-disable-next-line
  selector: 'nz-test-menu-horizontal',
  template: `

          Navigation Three - Submenu

            Item 1

Option 1

Option 2

            Item 2

Option 3

Option 4

                Sub Menu

Option 5

Option 6

                Disabled Sub Menu

Option 5

Option 6

  `
})
export class NzTestMenuHorizontalComponent {
  width = 200;
  open = false;
  disabled = false;
  @ViewChildren(NzSubMenuComponent) subs!: QueryList<NzSubMenuComponent>;
  @ViewChild('menuitem', { static: false, read: ElementRef }) menuitem!: ElementRef;
  @ViewChild('menuitem1', { static: false, read: ElementRef }) menuitem1!: ElementRef;
  @ViewChild('disableditem', { static: false, read: ElementRef }) disableditem!: ElementRef;
}


@Component({
  template: `

          Navigation One

Option 1

Option 2

  `
})
export class NzTestMenuInlineComponent {
  disabled = false;
  collapse = false;
  submenuClassName = 'submenu';
  @ViewChild(NzSubMenuComponent, { static: true }) subsmenu!: NzSubMenuComponent;
  @ViewChild('menuitem', { static: false, read: ElementRef }) menuitem!: ElementRef;
}


@Component({
  template: `

          {{ l1.text }}

            {{ l2.text }}

{{ l3.text }}

  `
})
export class NzDemoMenuNgForComponent {
  menus = [
    {
      text: 'level1',
      children: [
        {
          text: 'level2',
          children: [{ text: 'level3' }]
        }
      ]
    }
  ];
}


@Component({
  template: `

        Navigation One

        Navigation Two

Option 1

Option 2

Option 3

Option 4

Option 5

Option 6

Option 5

Option 6

        Navigation Four - Link

Navigation Five

  `
})
export class NzTestBasicMenuHorizontalComponent {}


@Component({
  template: `

Option 1

Option 2

Option 3

Option 4

Option 5

Option 6

Option 7

Option 8

Option 9

Option 10

Option 11

Option 12

Option 13

  `
})
export class NzTestBasicMenuInlineComponent {}


// https://github.com/NG-ZORRO/ng-zorro-antd/issues/3023
@Component({
  template: `

        {{ text }}

item

  `
})
export class NzTestNgIfMenuComponent {
  text = 'text';
  display = true;
}


// https://github.com/NG-ZORRO/ng-zorro-antd/issues/3345
@Component({
  template: `

        Navigation One

Option 5

Option 6

  `
})
export class NzTestSubMenuSelectedComponent {}


@Component({
  template: `

      <button nz-button nzType="primary" (click)="toggleCollapsed()">

      </button>

          Navigation One

Option 5

Option 6

Option 7

Option 8

Option 9

Option 10

Option 11

  `,
  styles: [
    `
      .wrapper {
        width: 240px;
      }

      button {
        margin-bottom: 12px;
      }
    `
  ]
})
export class NzTestMenuInlineCollapsedComponent {
  isCollapsed = false;

  toggleCollapsed(): void {
    this.isCollapsed = !this.isCollapsed;
  }
}


@Component({
  template: `

Option 1

Option 2

Option 3

Option 4

Option 5

Option 6

Option 7

Option 8

Option 9

Option 10

Option 11

  `
})
export class NzTestMenuSiderCurrentComponent {
  openMap: { [name: string]: boolean } = {
    sub1: true,
    sub2: false,
    sub3: false
  };

  openHandler(value: string): void {
    for (const key in this.openMap) {
      if (key !== value) {
        this.openMap[key] = false;
      }
    }
  }
}


@Component({
  template: `

Option 1

Option 2

Option 3

Option 4

Option 5

Option 6

Option 7

Option 8

Option 9

Option 10

Option 11

  `,
  styles: [
    `
      [nz-menu] {
        width: 240px;
      }
    `
  ]
})
export class NzTestMenuSwitchModeComponent {
  mode = false;
  dark = false;
}


@Component({
  template: `

Option 1

Option 2

Option 3

Option 4

Option 5

Option 6

Option 7

Option 8

Option 9

Option 10

Option 11

  `
})
export class NzTestMenuThemeComponent {
  theme = true;
}


@Component({
  template: `
    <div [dir]="direction">
      <nz-test-menu-horizontal></nz-test-menu-horizontal>
    </div>
  `
})
export class NzTestMenuRtlComponent {
  @ViewChild(Dir) dir!: Dir;
  direction = 'rtl';
}


toggleCollapsed(): void {
    this.isCollapsed = !this.isCollapsed;
  }


openHandler(value: string): void {
    for (const key in this.openMap) {
      if (key !== value) {
        this.openMap[key] = false;
      }
    }
  }


(testType) =>
            typeFunc(testType) &&
            testType.testResult === enums.TEST_RESULT.PASS &&
            !testType.certificateNumber &&
            testStatus === enums.TEST_STATUS.SUBMITTED


(testType) =>
            ValidationUtil.isTestTypeAdr(testType) &&
            testType.testResult === enums.TEST_RESULT.PASS &&
            !testType.testExpiryDate &&
            testStatus === enums.TEST_STATUS.SUBMITTED


(testType) => ValidationUtil.isTestTypeLec(testType)


(testType) =>
          testType.testResult === enums.TEST_RESULT.PASS &&
          testStatus === enums.TEST_STATUS.SUBMITTED


(testType) => {
        const {
          testExpiryDate,
          modType,
          emissionStandard,
          fuelType,
          smokeTestKLimitApplied,
        } = testType;
        if (!testExpiryDate) {
          missingFields.push(enums.ERRORS.NoLECExpiryDate);
        }
        if (!modType) {
          missingFields.push(enums.ERRORS.NoModificationType);
        }
        if (!emissionStandard) {
          missingFields.push(enums.ERRORS.NoEmissionStandard);
        }
        if (!fuelType) {
          missingFields.push(enums.ERRORS.NoFuelType);
        }
        if (!smokeTestKLimitApplied) {
          missingFields.push(enums.ERRORS.NoSmokeTestKLimitApplied);
        }
      }


(testType: models.TestType) =>
          testType.testResult === enums.TEST_RESULT.ABANDONED


(testType) => {
      if (!testType.defects) {
        return missingFieldsString;
      }
      testType.defects.forEach((defect) => {
        const {
          deficiencyCategory,
          deficiencyText,
          stdForProhibition,
          additionalInformation,
        } = defect;
        if (deficiencyCategory === "advisory") {
          return missingFieldsString;
        }
        if (additionalInformation.location === null) {
          missingFieldsString = missingFieldsString + "/location";
        }
        if (deficiencyText === null) {
          missingFieldsString = missingFieldsString + "/deficiencyText";
        }
        if (stdForProhibition === null) {
          missingFieldsString = missingFieldsString + "/stdForProhibition";
        }
      });
    }


(defect) => {
        const {
          deficiencyCategory,
          deficiencyText,
          stdForProhibition,
          additionalInformation,
        } = defect;
        if (deficiencyCategory === "advisory") {
          return missingFieldsString;
        }
        if (additionalInformation.location === null) {
          missingFieldsString = missingFieldsString + "/location";
        }
        if (deficiencyText === null) {
          missingFieldsString = missingFieldsString + "/deficiencyText";
        }
        if (stdForProhibition === null) {
          missingFieldsString = missingFieldsString + "/stdForProhibition";
        }
      }


(testType) =>
            testType.testResult === enums.TEST_RESULT.ABANDONED &&
            !testType.reasonForAbandoning


public static getTestResultItems(data: models.ITestResultData) {
    if (!data || !data.Count) {
      throw new models.HTTPError(404, enums.ERRORS.NoResourceMatch);
    }
    return data.Items as models.ITestResult[];
  }


public static validateGetTestResultFilters(
    filters: models.ITestResultFilters
  ) {
    const result =
      filters &&
      ValidationUtil.validateDates(filters.fromDateTime, filters.toDateTime);
    if (!result) {
      console.log(
        "ValidationUtil.validateGetTestResultFilters: Invalid Filter -> ",
        filters
      );
    }
    return result;
  }


private static validateDates(
    fromDateTime: string | number | Date,
    toDateTime: string | number | Date
  ) {
    // TODO: Discuss date validation to add validation fromDate cannot be greater than toDate.
    return (
      fromDateTime !== undefined &&
      toDateTime !== undefined &&
      isDate(new Date(fromDateTime)) &&
      isDate(new Date(toDateTime)) &&
      isFinite(new Date(fromDateTime).getTime()) &&
      isFinite(new Date(toDateTime).getTime())
    );
  }


public static isMissingRequiredCertificateNumberOnAdr(
    payload: models.ITestResultPayload
  ): boolean {
    return ValidationUtil.isMissingRequiredCertificateNumber(
      ValidationUtil.isTestTypeAdr,
      payload
    );
  }


public static isMissingRequiredCertificateNumberOnTir(
    payload: models.ITestResultPayload
  ): boolean {
    return ValidationUtil.isMissingRequiredCertificateNumber(
      ValidationUtil.isTestTypeTir,
      payload
    );
  }


public static isMissingRequiredCertificateNumber(
    typeFunc: (testType: models.TestType) => boolean,
    payload: models.ITestResultPayload
  ): boolean {
    const { testTypes, testStatus } = payload;
    return testTypes
      ? testTypes.some(
          (testType) =>
            typeFunc(testType) &&
            testType.testResult === enums.TEST_RESULT.PASS &&
            !testType.certificateNumber &&
            testStatus === enums.TEST_STATUS.SUBMITTED
        )
      : false;
  }


public static isPassAdrTestTypeWithoutExpiryDate(
    payload: models.ITestResultPayload
  ): boolean {
    const { testTypes, testStatus } = payload;
    return testTypes
      ? testTypes.some(
          (testType) =>
            ValidationUtil.isTestTypeAdr(testType) &&
            testType.testResult === enums.TEST_RESULT.PASS &&
            !testType.testExpiryDate &&
            testStatus === enums.TEST_STATUS.SUBMITTED
        )
      : false;
  }


public static validateLecTestTypeFields(
    payload: models.ITestResultPayload
  ): string[] {
    const missingFields: string[] = [];
    const { testTypes, testStatus } = payload;
    testTypes
      .filter((testType) => ValidationUtil.isTestTypeLec(testType))
      .filter(
        (testType) =>
          testType.testResult === enums.TEST_RESULT.PASS &&
          testStatus === enums.TEST_STATUS.SUBMITTED
      )
      .forEach((testType) => {
        const {
          testExpiryDate,
          modType,
          emissionStandard,
          fuelType,
          smokeTestKLimitApplied,
        } = testType;
        if (!testExpiryDate) {
          missingFields.push(enums.ERRORS.NoLECExpiryDate);
        }
        if (!modType) {
          missingFields.push(enums.ERRORS.NoModificationType);
        }
        if (!emissionStandard) {
          missingFields.push(enums.ERRORS.NoEmissionStandard);
        }
        if (!fuelType) {
          missingFields.push(enums.ERRORS.NoFuelType);
        }
        if (!smokeTestKLimitApplied) {
          missingFields.push(enums.ERRORS.NoSmokeTestKLimitApplied);
        }
      });
    return missingFields;
  }


public static validateMandatoryTestResultFields(
    payload: models.ITestResultPayload
  ): string[] {
    const missingMandatoryFields: string[] = [];
    const {
      testTypes,
      testStatus,
      countryOfRegistration,
      euVehicleCategory,
      vehicleType,
      odometerReading,
      odometerReadingUnits,
    } = payload;
    if (
      testTypes.every(
        (testType: models.TestType) =>
          testType.testResult === enums.TEST_RESULT.ABANDONED
      ) ||
      testStatus !== enums.TEST_STATUS.SUBMITTED
    ) {
      return missingMandatoryFields;
    }

    if (!countryOfRegistration) {
      missingMandatoryFields.push(enums.ERRORS.CountryOfRegistrationMandatory);
    }
    if (!euVehicleCategory) {
      missingMandatoryFields.push(enums.ERRORS.EuVehicleCategoryMandatory);
    }

    if (
      ![enums.VEHICLE_TYPES.HGV, enums.VEHICLE_TYPES.PSV].includes(vehicleType)
    ) {
      return missingMandatoryFields;
    }
    // odometerReading and odoMeterReadingUnits are required only for HGV and PSV
    if (!odometerReading) {
      missingMandatoryFields.push(enums.ERRORS.OdometerReadingMandatory);
    }
    if (!odometerReadingUnits) {
      missingMandatoryFields.push(enums.ERRORS.OdometerReadingUnitsMandatory);
    }
    return missingMandatoryFields;
  }


public static isHGVTRLRoadworthinessTest(testTypeId: string): boolean {
    return enums.HGV_TRL_ROADWORTHINESS_TEST_TYPES.IDS.includes(testTypeId);
  }


public static isHgvOrTrl(vehicleType: string): boolean {
    return [enums.VEHICLE_TYPES.HGV, enums.VEHICLE_TYPES.TRL].includes(
      vehicleType
    );
  }


public static isPassedRoadworthinessTestForHgvTrl(
    vehicleType: string,
    testTypeId: string,
    testResult: string
  ): boolean {
    return (
      ValidationUtil.isHgvOrTrl(vehicleType) &&
      ValidationUtil.isHGVTRLRoadworthinessTest(testTypeId) &&
      testResult !== enums.TEST_RESULT.FAIL
    );
  }


public static isAnnualTestTypeClassificationWithoutAbandonedResult(
    testTypeClassification: string,
    testResult: string
  ): boolean {
    return (
      testTypeClassification ===
        enums.TEST_TYPE_CLASSIFICATION.ANNUAL_WITH_CERTIFICATE &&
      testResult !== enums.TEST_RESULT.ABANDONED
    );
  }


public static isValidTestCodeForExpiryCalculation(
    testCode?: string
  ): boolean {
    return (
      !!testCode &&
      enums.TEST_CODES_FOR_CALCULATING_EXPIRY.CODES.includes(
        testCode.toUpperCase()
      )
    );
  }


public static isNotAllowedVehicleTypeForExpiry(vehicleType: string) {
    return [
      enums.VEHICLE_TYPES.CAR,
      enums.VEHICLE_TYPES.LGV,
      enums.VEHICLE_TYPES.MOTORCYCLE,
    ].includes(vehicleType);
  }


public static isAllowedTestTypeForExpiry(testType: models.TestType) {
    const { testTypeClassification, testResult, testTypeId } = testType;
    return (
      testTypeClassification ===
        enums.TEST_TYPE_CLASSIFICATION.ANNUAL_WITH_CERTIFICATE &&
      [enums.TEST_RESULT.PASS, enums.TEST_RESULT.PRS].includes(testResult) &&
      !ValidationUtil.isHGVTRLRoadworthinessTest(testTypeId)
    );
  }


public static isTestTypeLec(testType: any): boolean {
    return enums.LEC_TEST_TYPES.IDS.includes(testType.testTypeId);
  }


public static isTestTypeAdr(testType: models.TestType): boolean {
    return enums.ADR_TEST_TYPES.IDS.includes(testType.testTypeId);
  }


public static isTestTypeTir(testType: models.TestType): boolean {
    return enums.TIR_TEST_TYPES.IDS.includes(testType.testTypeId);
  }


public static fieldsNullWhenDeficiencyCategoryIsOtherThanAdvisory(
    payload: models.ITestResultPayload
  ) {
    // let bool = false;
    let missingFieldsString = "";
    const { testTypes } = payload;
    if (!testTypes) {
      return missingFieldsString;
    }
    (testTypes as models.TestType[]).map((testType) => {
      if (!testType.defects) {
        return missingFieldsString;
      }
      testType.defects.forEach((defect) => {
        const {
          deficiencyCategory,
          deficiencyText,
          stdForProhibition,
          additionalInformation,
        } = defect;
        if (deficiencyCategory === "advisory") {
          return missingFieldsString;
        }
        if (additionalInformation.location === null) {
          missingFieldsString = missingFieldsString + "/location";
        }
        if (deficiencyText === null) {
          missingFieldsString = missingFieldsString + "/deficiencyText";
        }
        if (stdForProhibition === null) {
          missingFieldsString = missingFieldsString + "/stdForProhibition";
        }
      });
    });

    return missingFieldsString
      ? missingFieldsString.concat(
          " are null for a defect with deficiency category other than advisory"
        )
      : missingFieldsString;
  }


public static reasonForAbandoningPresentOnAllAbandonedTests(
    payload: models.ITestResultPayload
  ) {
    const { testTypes } = payload;
    return !testTypes || !testTypes.length
      ? true
      : !(testTypes as models.TestType[]).some(
          (testType) =>
            testType.testResult === enums.TEST_RESULT.ABANDONED &&
            !testType.reasonForAbandoning
        );
  }


public static getValidationSchema(vehicleType: string, testStatus: string) {
    if (!(vehicleType && testStatus)) {
      return null;
    }
    const validator =
      vehicleType + testStatus.charAt(0).toUpperCase() + testStatus.slice(1);
    if (validator in validators) {
      return validators[validator as keyof typeof validators];
    }
    return null;
  }


public static validateInsertTestResultPayload(
    payload: models.ITestResultPayload
  ) {
    if (Object.keys(payload).length === 0) {
      throw new models.HTTPError(400, enums.ERRORS.PayloadCannotBeEmpty);
    }
    const validationSchema = ValidationUtil.getValidationSchema(
      payload.vehicleType,
      payload.testStatus
    );
    const validation: ValidationResult<any> | any | null = validationSchema
      ? validate(payload, validationSchema)
      : null;

    if (
      !ValidationUtil.reasonForAbandoningPresentOnAllAbandonedTests(payload)
    ) {
      throw new models.HTTPError(
        400,
        enums.MESSAGES.REASON_FOR_ABANDONING_NOT_PRESENT
      );
    }

    const fieldsNullWhenDeficiencyCategoryIsOtherThanAdvisory = ValidationUtil.fieldsNullWhenDeficiencyCategoryIsOtherThanAdvisory(
      payload
    );
    if (fieldsNullWhenDeficiencyCategoryIsOtherThanAdvisory) {
      throw new models.HTTPError(
        400,
        fieldsNullWhenDeficiencyCategoryIsOtherThanAdvisory
      );
    }
    const missingFieldsForLecTestType: string[] = ValidationUtil.validateLecTestTypeFields(
      payload
    );
    if (missingFieldsForLecTestType && missingFieldsForLecTestType.length > 0) {
      throw new models.HTTPError(400, { errors: missingFieldsForLecTestType });
    }
    if (ValidationUtil.isMissingRequiredCertificateNumberOnAdr(payload)) {
      throw new models.HTTPError(400, enums.ERRORS.NoCertificateNumberOnAdr);
    }
    if (ValidationUtil.isMissingRequiredCertificateNumberOnTir(payload)) {
      throw new models.HTTPError(400, enums.ERRORS.NoCertificateNumberOnTir);
    }
    if (ValidationUtil.isPassAdrTestTypeWithoutExpiryDate(payload)) {
      throw new models.HTTPError(400, enums.ERRORS.NoExpiryDate);
    }

    const missingMandatoryTestResultFields: string[] = ValidationUtil.validateMandatoryTestResultFields(
      payload
    );
    if (missingMandatoryTestResultFields.length > 0) {
      throw new models.HTTPError(400, {
        errors: missingMandatoryTestResultFields,
      });
    }

    if (validation !== null && validation.error) {
      throw new models.HTTPError(400, {
        errors: MappingUtil.mapErrorMessage(validation),
      });
    }
  }


public static validateTestTypes(testResult: models.ITestResult) {
    let validationErrors;
    let validation: ValidationResult<any> | any;
    validation = validators.testTypesArray.validate({
      testTypes: testResult.testTypes,
    });
    if (validation.error) {
      validationErrors = MappingUtil.mapErrorMessage(validation);
      return validationErrors;
    }
    for (const testType of testResult.testTypes) {
      const options = {
        abortEarly: false,
        context: { hasTestResult: testType.testResult },
      };
      if (enums.TEST_TYPES_GROUP1.includes(testType.testTypeId)) {
        // tests for PSV - Annual test, Class 6A seatbelt installation check, Paid/Part paid annual test retest, Prohibition clearance
        validation = validators.testTypesGroup1.validate(testType, options);
      } else if (enums.TEST_TYPES_GROUP2.includes(testType.testTypeId)) {
        // tests for PSV - Paid/Part paid prohibition clearance(full/partial/retest without cert)
        validation = validators.testTypesGroup2.validate(testType, options);
      } else if (enums.TEST_TYPES_GROUP3_4_8.includes(testType.testTypeId)) {
        // Notifiable alteration and voluntary tests for HGV, PSV and TRL
        validation = validators.testTypesGroup3And4And8.validate(
          testType,
          options
        );
      } else if (enums.TEST_TYPES_GROUP5_13.includes(testType.testTypeId)) {
        // TIR tests for TRL and HGV
        validation = validators.testTypesGroup5And13.validate(
          testType,
          options
        );
      } else if (enums.TEST_TYPES_GROUP6_11.includes(testType.testTypeId)) {
        // HGV and TRL - Paid/Part paid roadworthiness retest, Voluntary roadworthiness test
        validation = validators.testTypesGroup6And11.validate(
          testType,
          options
        );
      } else if (enums.TEST_TYPES_GROUP7.includes(testType.testTypeId)) {
        // ADR tests for HGV and TRL
        validation = validators.testTypesGroup7.validate(testType, options);
      } else if (enums.TEST_TYPES_GROUP9_10.includes(testType.testTypeId)) {
        // tests for HGV and TRL - Annual tests, First tests, Annual retests, Paid/Part paid prohibition clearance
        validation = validators.testTypesGroup9And10.validate(
          testType,
          options
        );
      } else if (enums.TEST_TYPES_GROUP12_14.includes(testType.testTypeId)) {
        // tests for TRL - Paid/Part paid prohibition clearance(retest, full inspection, part inspection, without cert)
        validation = validators.testTypesGroup12And14.validate(
          testType,
          options
        );
      } else if (enums.TEST_TYPES_GROUP15_16.includes(testType.testTypeId)) {
        // LEC tests for HGV and PSV
        validation = validators.testTypesGroup15And16.validate(
          testType,
          options
        );
      } else if (
        enums.TEST_TYPES_GROUP1_SPEC_TEST.includes(testType.testTypeId)
      ) {
        // Test/Retest - Free/Paid - IVA inspection, MSVA inspection
        validation = validators.testTypesSpecialistGroup1.validate(
          testType,
          options
        );
      } else if (
        enums.TEST_TYPES_GROUP2_SPEC_TEST.includes(testType.testTypeId)
      ) {
        // Test/Retest COIF with annual test, Seatbelt installation check COIF with annual test
        validation = validators.testTypesSpecialistGroup2.validate(
          testType,
          options
        );
      } else if (
        enums.TEST_TYPES_GROUP3_SPEC_TEST.includes(testType.testTypeId)
      ) {
        // Test/Retest COIF without annual test, Type approved to bus directive COIF, Annex 7 COIF, TILT COIF retest
        validation = validators.testTypesSpecialistGroup3.validate(
          testType,
          options
        );
      } else if (
        enums.TEST_TYPES_GROUP4_SPEC_TEST.includes(testType.testTypeId)
      ) {
        // Test Seatbelt installation check COIF without annual test
        validation = validators.testTypesSpecialistGroup4.validate(
          testType,
          options
        );
      } else if (
        enums.TEST_TYPES_GROUP5_SPEC_TEST.includes(testType.testTypeId)
      ) {
        // Test/Retest Normal/Basic voluntary IVA inspection
        validation = validators.testTypesSpecialistGroup5.validate(
          testType,
          options
        );
      } else {
        validation = {
          error: {
            details: [{ message: "Unknown testTypeId" }],
          },
        };
      }
      if (validation.error) {
        validationErrors = MappingUtil.mapErrorMessage(validation);
        break;
      }
    }
    return validationErrors;
  }


@apiRouteConfig({
  service: 'pim-deployment',
  pathRoot: '/api/destination/',
  tags: ['api', 'destination'],
})
export class NoteRoutes {

  @apiRouteAction()
  get createNote(): IRouteParams {

    return {
      method: 'PUT',
      path: '/note',

      config: { // - TODO: could remove this extra level of nesting....

        description: 'create new note',
        validate: {
          params: CreateNoteRequest,
        },
        response: {
          schema: NoteResponse,
        },
        handler: createNoteHandler,
      }
    };
  }

  @apiRouteAction({ method: 'POST', path: '/note', simpleUrlTemplate: true, })
  get updateNote(): IRouteParams {

    return {
      config: { // - TODO: could remove this extra level of nesting....

        description: 'update existing note',
        validate: {
          params: CreateNoteRequest,
        },
        response: {
          schema: NoteResponse,
        },
        handler: updateNoteHandler,
      }
    };
  }
}


declare interface Window {
  jQuery: any;
  RetailUIVerticalScrollCounter: number;
  ReactTesting: any;
  __RetailUiZIndexes: number[];
}


(predeterminado: 9347 or testnet: 19347)


class MoveCarretAction extends EditorAction {

	private left: boolean;

	constructor(left: boolean, opts: IActionOptions) {
		super(opts);

		this.left = left;
	}

	public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {

		var commands: ICommand[] = [];
		var selections = editor.getSelections();

		for (var i = 0; i < selections.length; i++) {
			commands.push(new MoveCarretCommand(selections[i], this.left));
		}

		editor.executeCommands(this.id, commands);
	}
}


@editorAction
class MoveCarretLeftAction extends MoveCarretAction {
	constructor() {
		super(true, {
			id: 'editor.action.moveCarretLeftAction',
			label: nls.localize('carret.moveLeft', "Move Carret Left"),
			alias: 'Move Carret Left',
			precondition: EditorContextKeys.Writable
		});
	}
}


@editorAction
class MoveCarretRightAction extends MoveCarretAction {
	constructor() {
		super(false, {
			id: 'editor.action.moveCarretRightAction',
			label: nls.localize('carret.moveRight', "Move Carret Right"),
			alias: 'Move Carret Right',
			precondition: EditorContextKeys.Writable
		});
	}
}


public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {

		var commands: ICommand[] = [];
		var selections = editor.getSelections();

		for (var i = 0; i < selections.length; i++) {
			commands.push(new MoveCarretCommand(selections[i], this.left));
		}

		editor.executeCommands(this.id, commands);
	}


export default function FileListPane({
        title,
        expanded,
        buttons,
        children,
        onClick,
        className
     }: Props) {

        return  (
            <div
             className={`${className ?? ''} ${css(styles.container)}`}


type Props = {
    title: string;
    expanded: boolean;
    onClick: (event: React.MouseEvent<HTMLDivElement>) => void;
    buttons?: React.ReactNode[];
    children?: React.ReactNode;
    className?: string;
};