juice-shop/juice-shop

View on GitHub
frontend/src/app/score-board/score-board.component.spec.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { type ComponentFixture, TestBed } from '@angular/core/testing'
import { RouterTestingModule } from '@angular/router/testing'
import { MatDialogModule } from '@angular/material/dialog'
import { MatIconModule } from '@angular/material/icon'
import { TranslateModule } from '@ngx-translate/core'
import { of } from 'rxjs'

import { HackingChallengeProgressScoreCardComponent } from './components/hacking-challenge-progress-score-card/hacking-challenge-progress-score-card.component'
import { CodingChallengeProgressScoreCardComponent } from './components/coding-challenge-progress-score-card/coding-challenge-progress-score-card.component'
import { ChallengesUnavailableWarningComponent } from './components/challenges-unavailable-warning/challenges-unavailable-warning.component'
import { DifficultyOverviewScoreCardComponent } from './components/difficulty-overview-score-card/difficulty-overview-score-card.component'
import { TutorialModeWarningComponent } from './components/tutorial-mode-warning/tutorial-mode-warning.component'
import { WarningCardComponent } from './components/warning-card/warning-card.component'
import { ScoreCardComponent } from './components/score-card/score-card.component'
import { ScoreBoardComponent } from './score-board.component'
import { ConfigurationService } from '../Services/configuration.service'
import { CodeSnippetService } from '../Services/code-snippet.service'
import { ChallengeService } from '../Services/challenge.service'
import { type Challenge } from '../Models/challenge.model'

// allows to easily create a challenge with some overwrites
function createChallenge (challengeOverwrites: Partial<Challenge>): Challenge {
  return {
    name: 'foobar',
    key: 'challenge-1',
    category: 'category-blue',
    difficulty: 3,
    description: '',
    hint: '',
    tags: '',
    hintUrl: '',
    disabledEnv: null,
    solved: false,
    tutorialOrder: null,
    hasTutorial: false,
    hasSnippet: false,
    codingChallengeStatus: 0,
    mitigationUrl: '',
    ...challengeOverwrites
  }
}

describe('ScoreBoardPreviewComponent', () => {
  let component: ScoreBoardComponent
  let fixture: ComponentFixture<ScoreBoardComponent>
  let challengeService
  let codeSnippetService
  let configService

  beforeEach(async () => {
    challengeService = jasmine.createSpyObj('ChallengeService', ['find'])
    codeSnippetService = jasmine.createSpyObj('CodeSnippetService', [
      'challenges'
    ])
    configService = jasmine.createSpyObj('ConfigurationService', [
      'getApplicationConfiguration'
    ])
    await TestBed.configureTestingModule({
      declarations: [
        ScoreBoardComponent,
        HackingChallengeProgressScoreCardComponent,
        CodingChallengeProgressScoreCardComponent,
        DifficultyOverviewScoreCardComponent,
        WarningCardComponent,
        ChallengesUnavailableWarningComponent,
        TutorialModeWarningComponent,
        ScoreCardComponent
      ],
      imports: [
        TranslateModule.forRoot(),
        HttpClientTestingModule,
        RouterTestingModule,
        MatProgressSpinnerModule,
        MatDialogModule,
        MatIconModule
      ],
      providers: [
        { provide: ChallengeService, useValue: challengeService },
        { provide: CodeSnippetService, useValue: codeSnippetService },
        { provide: ConfigurationService, useValue: configService }
      ]
    }).compileComponents()

    challengeService.find.and.returnValue(
      of([
        createChallenge({
          name: 'Challenge 1',
          key: 'challenge-1',
          category: 'category-blue',
          difficulty: 1,
          solved: true
        }),
        createChallenge({
          name: 'Challenge 2',
          key: 'challenge-2',
          category: 'category-blue',
          difficulty: 5,
          solved: false,
          hasSnippet: true,
          codingChallengeStatus: 1
        }),
        createChallenge({
          name: 'Challenge 3',
          key: 'challenge-3',
          category: 'category-red',
          difficulty: 3,
          hasSnippet: true,
          solved: false
        })
      ])
    )
    codeSnippetService.challenges.and.returnValue(of(['challenge-2']))
    configService.getApplicationConfiguration.and.returnValue(
      of({
        challenges: {
          restrictToTutorialsFirst: false,
          codingChallengesEnabled: true,
          showHints: true,
          showMitigations: true
        }
      })
    )

    fixture = TestBed.createComponent(ScoreBoardComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
  })

  it('should not filter any challenges on default settings', (): void => {
    expect(component.filteredChallenges).toHaveSize(3)
  })

  it('should properly identify that a challenge has a associated coding challenge', (): void => {
    expect(
      component.filteredChallenges.find(
        (challenge) => challenge.key === 'challenge-2'
      ).hasCodingChallenge
    ).toBe(true)
  })

  it('should mark challenges as solved on "challenge solved" websocket', (): void => {
    expect(
      component.filteredChallenges.find(
        (challenge) => challenge.key === 'challenge-3'
      ).solved
    ).toBeFalse()

    component.onChallengeSolvedWebsocket({
      key: 'challenge-3',
      name: '',
      challenge: '',
      flag: '',
      hidden: false,
      isRestore: false
    })

    expect(
      component.filteredChallenges.find(
        (challenge) => challenge.key === 'challenge-3'
      ).solved
    ).toBeTrue()
  })

  it('should mark find it code challenges as solved on "code challenge solved" websocket', (): void => {
    expect(
      component.filteredChallenges.find(
        (challenge) => challenge.key === 'challenge-3'
      ).codingChallengeStatus
    ).toBe(0)

    component.onCodeChallengeSolvedWebsocket({
      key: 'challenge-3',
      codingChallengeStatus: 1
    })

    expect(
      component.filteredChallenges.find(
        (challenge) => challenge.key === 'challenge-3'
      ).codingChallengeStatus
    ).toBe(1)
  })

  it('should mark fix it code challenges as solved on "code challenge solved" websocket', (): void => {
    expect(
      component.filteredChallenges.find(
        (challenge) => challenge.key === 'challenge-2'
      ).codingChallengeStatus
    ).toBe(1)

    component.onCodeChallengeSolvedWebsocket({
      key: 'challenge-2',
      codingChallengeStatus: 2
    })

    expect(
      component.filteredChallenges.find(
        (challenge) => challenge.key === 'challenge-2'
      ).codingChallengeStatus
    ).toBe(2)
  })
})