diff --git a/apps/datahub/src/app/record/record-user-feedbacks/record-user-feedbacks.component.spec.ts b/apps/datahub/src/app/record/record-user-feedbacks/record-user-feedbacks.component.spec.ts index 41202324ff..282a87f9b0 100644 --- a/apps/datahub/src/app/record/record-user-feedbacks/record-user-feedbacks.component.spec.ts +++ b/apps/datahub/src/app/record/record-user-feedbacks/record-user-feedbacks.component.spec.ts @@ -17,9 +17,10 @@ import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform. import { DATASET_RECORDS, SOME_USER_FEEDBACKS, - A_USER, + USER_FIXTURE, } from '@geonetwork-ui/common/fixtures' import { UserFeedbackViewModel } from '@geonetwork-ui/common/domain/model/record' +import { Gn4PlatformMapper } from '@geonetwork-ui/api/repository' describe('RelatedRecordsComponent', () => { const allUserFeedbacks = SOME_USER_FEEDBACKS @@ -30,6 +31,9 @@ describe('RelatedRecordsComponent', () => { isAddUserFeedbackLoading$: new BehaviorSubject(false), loadUserFeedbacks: jest.fn(), userFeedbacks$: of(allUserFeedbacks), + } + + const gn4PlatformMapperMock: Partial = { createUserFeedbackViewModel: (baseUserFeedback) => { return Promise.resolve({ ...baseUserFeedback, @@ -44,7 +48,7 @@ describe('RelatedRecordsComponent', () => { const platformServiceInterfaceMock: Partial = { getUserFeedbacks: jest.fn(), - getMe: jest.fn(() => new BehaviorSubject(A_USER)), + getMe: jest.fn(() => new BehaviorSubject(USER_FIXTURE())), } let component: RecordUserFeedbacksComponent @@ -70,6 +74,10 @@ describe('RelatedRecordsComponent', () => { provide: PlatformServiceInterface, useValue: platformServiceInterfaceMock, }, + { + provide: Gn4PlatformMapper, + useValue: gn4PlatformMapperMock, + }, ], }) .overrideComponent(RecordUserFeedbacksComponent, { @@ -108,7 +116,7 @@ describe('RelatedRecordsComponent', () => { it('should set active user', fakeAsync(() => { component.ngOnInit() tick() - expect(component.activeUser).toEqual(A_USER) + expect(component.activeUser).toEqual(USER_FIXTURE()) })) it('should fetch user feedbacks and sort them correctly', async () => { component.ngOnInit() diff --git a/apps/datahub/src/app/record/record-user-feedbacks/record-user-feedbacks.component.ts b/apps/datahub/src/app/record/record-user-feedbacks/record-user-feedbacks.component.ts index 28243f541a..9492858153 100644 --- a/apps/datahub/src/app/record/record-user-feedbacks/record-user-feedbacks.component.ts +++ b/apps/datahub/src/app/record/record-user-feedbacks/record-user-feedbacks.component.ts @@ -17,7 +17,7 @@ import { UserModel } from '@geonetwork-ui/common/domain/model/user' import { DropdownChoice } from '@geonetwork-ui/ui/inputs' import { MdViewFacade } from '@geonetwork-ui/feature/record' import { TranslateService } from '@ngx-translate/core' -import { AuthService } from '@geonetwork-ui/api/repository' +import { AuthService, Gn4PlatformMapper } from '@geonetwork-ui/api/repository' type UserFeedbackSortingFunction = ( userFeedbackA: UserFeedback, @@ -72,11 +72,12 @@ export class RecordUserFeedbacksComponent implements OnInit, OnDestroy { isAddUserFeedbackLoading = false constructor( - private translate: TranslateService, - private authService: AuthService, - private metadataViewFacade: MdViewFacade, - private cdr: ChangeDetectorRef, - private platformServiceInterface: PlatformServiceInterface + private readonly translate: TranslateService, + private readonly authService: AuthService, + private readonly metadataViewFacade: MdViewFacade, + private readonly cdr: ChangeDetectorRef, + private readonly mapper: Gn4PlatformMapper, + private readonly platformServiceInterface: PlatformServiceInterface ) { this.activeUser$ = this.platformServiceInterface.getMe() } @@ -124,13 +125,13 @@ export class RecordUserFeedbacksComponent implements OnInit, OnDestroy { const userFeedbacksParentsViewModels = await Promise.all( userFeedbacksParents.map((feedback) => - this.metadataViewFacade.createUserFeedbackViewModel(feedback) + this.mapper.createUserFeedbackViewModel(feedback) ) ) const userFeedbacksAnswersViewModels = await Promise.all( userFeedbacksAnswers.map((feedback) => - this.metadataViewFacade.createUserFeedbackViewModel(feedback) + this.mapper.createUserFeedbackViewModel(feedback) ) ) diff --git a/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.ts b/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.ts index eda00e26cb..cc301e4be8 100644 --- a/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.ts +++ b/libs/api/repository/src/lib/gn4/platform/gn4-platform.mapper.ts @@ -9,7 +9,7 @@ import { AvatarServiceInterface } from '../auth' import { map } from 'rxjs/operators' import { Observable, of } from 'rxjs' import { ThesaurusModel } from '@geonetwork-ui/common/domain/model/thesaurus/thesaurus.model' -import { UserFeedback } from '@geonetwork-ui/common/domain/model/record' +import { UserFeedback, UserFeedbackViewModel } from '@geonetwork-ui/common/domain/model/record' @Injectable() export class Gn4PlatformMapper { @@ -99,4 +99,17 @@ export class Gn4PlatformMapper { date: userFeedback.date.getTime().toString(), } } + + async createUserFeedbackViewModel( + baseUserFeedback: UserFeedback + ): Promise { + const userAvatarUrl = await this.avatarService.getProfileIconUrl( + baseUserFeedback.authorUserId?.toString() + ) + + return { + ...baseUserFeedback, + avatarUrl: userAvatarUrl, + } + } } diff --git a/libs/feature/record/src/lib/state/mdview.actions.ts b/libs/feature/record/src/lib/state/mdview.actions.ts index 3caa4cfae7..9cc84be989 100644 --- a/libs/feature/record/src/lib/state/mdview.actions.ts +++ b/libs/feature/record/src/lib/state/mdview.actions.ts @@ -61,7 +61,7 @@ export const addUserFeedbackSuccess = createAction( export const addUserFeedbackFailure = createAction( '[Metadata view] Add UserFeedback Failure', - props<{ error: any }>() + props<{ otherError?: string; notFound?: boolean }>() ) export const loadUserFeedbacks = createAction( @@ -76,5 +76,5 @@ export const loadUserFeedbacksSuccess = createAction( export const loadUserFeedbacksFailure = createAction( '[Metadata view] Load UserFeedbacks Failure', - props<{ error: any }>() + props<{ otherError?: string; notFound?: boolean }>() ) diff --git a/libs/feature/record/src/lib/state/mdview.effects.spec.ts b/libs/feature/record/src/lib/state/mdview.effects.spec.ts index 25ee91c225..066c5abe60 100644 --- a/libs/feature/record/src/lib/state/mdview.effects.spec.ts +++ b/libs/feature/record/src/lib/state/mdview.effects.spec.ts @@ -1,8 +1,10 @@ import { TestBed } from '@angular/core/testing' import { + A_USER_FEEDBACK, DATASET_RECORDS, SAMPLE_AGGREGATIONS_RESULTS, SAMPLE_SEARCH_RESULTS, + SOME_USER_FEEDBACKS, } from '@geonetwork-ui/common/fixtures' import { provideMockActions } from '@ngrx/effects/testing' @@ -15,6 +17,7 @@ import { MdViewEffects } from './mdview.effects' import { hot } from 'jasmine-marbles' import { CatalogRecord } from '@geonetwork-ui/common/domain/model/record' import { RecordsRepositoryInterface } from '@geonetwork-ui/common/domain/repository/records-repository.interface' +import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface' const full = { uniqueIdentifier: '1231321321', @@ -29,10 +32,16 @@ class RecordsRepositoryMock { getSimilarRecords = jest.fn(() => of(DATASET_RECORDS)) } +class PlatformServiceInterfaceMock { + getUserFeedbacks = jest.fn(() => of(SOME_USER_FEEDBACKS)) + postUserFeedbacks = jest.fn(() => of(undefined)) +} + describe('MdViewEffects', () => { let actions: Observable let effects: MdViewEffects let repository: RecordsRepositoryInterface + let platform: PlatformServiceInterface beforeEach(() => { TestBed.configureTestingModule({ @@ -45,14 +54,19 @@ describe('MdViewEffects', () => { provide: RecordsRepositoryInterface, useClass: RecordsRepositoryMock, }, + { + provide: PlatformServiceInterface, + useClass: PlatformServiceInterfaceMock, + }, ], }) repository = TestBed.inject(RecordsRepositoryInterface) effects = TestBed.inject(MdViewEffects) + platform = TestBed.inject(PlatformServiceInterface) }) - describe('loadFullRecord$', () => { + describe('loadFullMetadata$', () => { describe('when api success and at least one record found', () => { it('dispatch loadFullSuccess', () => { actions = hot('-a-|', { @@ -126,4 +140,122 @@ describe('MdViewEffects', () => { }) }) }) + + describe('loadUserFeedbacks$', () => { + describe('when loadUserFeedbacks success', () => { + it('should dispatch loadUserFeedbacksSuccess when API call is successful', () => { + actions = hot('-a-', { + a: MdViewActions.loadUserFeedbacks({ datasetUuid: '12345' }), + }) + const expected = hot('-a-', { + a: MdViewActions.loadUserFeedbacksSuccess({ + userFeedbacks: SOME_USER_FEEDBACKS, + }), + }) + + expect(effects.loadUserFeedbacks$).toBeObservable(expected) + }) + }) + + describe('when api fails', () => { + const error = 'API error' + + beforeEach(() => { + platform.getUserFeedbacks = jest.fn(() => + throwError(() => new Error(error)) + ) + }) + + it('should dispatch loadUserFeedbacksFailure when API call fails', () => { + actions = hot('-a|', { + a: MdViewActions.loadUserFeedbacks({ datasetUuid: '12345' }), + }) + const expected = hot('-a|', { + a: MdViewActions.loadUserFeedbacksFailure({ otherError: error }), + }) + + expect(effects.loadUserFeedbacks$).toBeObservable(expected) + }) + }) + }) + + describe('reloadUserFeedbacks$', () => { + describe('when addUserFeedbackSuccess', () => { + it('should dispatch loadUserFeedbacksSuccess when API call is successful', () => { + actions = hot('-a-', { + a: MdViewActions.addUserFeedbackSuccess({ datasetUuid: '12345' }), + }) + const expected = hot('-a', { + a: MdViewActions.loadUserFeedbacksSuccess({ + userFeedbacks: SOME_USER_FEEDBACKS, + }), + }) + + expect(effects.reloadUserFeedbacks$).toBeObservable(expected) + }) + }) + + describe('when api fails', () => { + const error = 'API error' + + beforeEach(() => { + platform.getUserFeedbacks = jest.fn(() => + throwError(() => new Error(error)) + ) + }) + + it('should dispatch loadUserFeedbacksFailure when API call fails', () => { + const error = 'API error' + + actions = hot('-a-', { + a: MdViewActions.addUserFeedbackSuccess({ datasetUuid: '12345' }), + }) + const expected = hot('-a', { + a: MdViewActions.loadUserFeedbacksFailure({ otherError: error }), + }) + + expect(effects.reloadUserFeedbacks$).toBeObservable(expected) + }) + }) + }) + + describe('addUserFeedback$', () => { + describe('when addUserFeedback success', () => { + it('should dispatch addUserFeedbackSuccess when API call is successful', () => { + actions = hot('-a-', { + a: MdViewActions.addUserFeedback({ userFeedback: A_USER_FEEDBACK }), + }) + const expected = hot('-a-', { + a: MdViewActions.addUserFeedbackSuccess({ + datasetUuid: A_USER_FEEDBACK.metadataUUID, + }), + }) + + expect(effects.addUserFeedback$).toBeObservable(expected) + }) + }) + + describe('when api fails', () => { + const error = 'API error' + + beforeEach(() => { + platform.postUserFeedbacks = jest.fn(() => + throwError(() => new Error(error)) + ) + }) + + it('should dispatch addUserFeedbackFailure when API call fails', () => { + const error = 'API error' + + actions = hot('-a-', { + a: MdViewActions.addUserFeedback({ userFeedback: A_USER_FEEDBACK }), + }) + const expected = hot('-a', { + a: MdViewActions.addUserFeedbackFailure({ otherError: error }), + }) + + expect(effects.addUserFeedback$).toBeObservable(expected) + }) + }) + }) }) diff --git a/libs/feature/record/src/lib/state/mdview.effects.ts b/libs/feature/record/src/lib/state/mdview.effects.ts index 6d91e80db8..276d3d0106 100644 --- a/libs/feature/record/src/lib/state/mdview.effects.ts +++ b/libs/feature/record/src/lib/state/mdview.effects.ts @@ -52,22 +52,18 @@ export class MdViewEffects { /* UserFeedback effects */ - addUserFeedback$ = createEffect(() => + loadUserFeedbacks$ = createEffect(() => this.actions$.pipe( - ofType(MdViewActions.addUserFeedback), - mergeMap((action) => - this.platformServiceInterface - .postUserFeedbacks(action.userFeedback) - .pipe( - map(() => - MdViewActions.addUserFeedbackSuccess({ - datasetUuid: action.userFeedback.metadataUUID, - }) - ), - catchError((error) => { - return of(MdViewActions.addUserFeedbackFailure({ error })) - }) + ofType(MdViewActions.loadUserFeedbacks), + exhaustMap(({ datasetUuid }) => + this.platformServiceInterface.getUserFeedbacks(datasetUuid).pipe( + map((userFeedbacks) => + MdViewActions.loadUserFeedbacksSuccess({ userFeedbacks }) + ), + catchError((error) => + of(MdViewActions.loadUserFeedbacksFailure({ otherError: error.message })) ) + ) ) ) ) @@ -81,25 +77,29 @@ export class MdViewEffects { MdViewActions.loadUserFeedbacksSuccess({ userFeedbacks }) ), catchError((error) => - of(MdViewActions.loadUserFeedbacksFailure({ error })) + of(MdViewActions.loadUserFeedbacksFailure({ otherError: error.message })) ) ) ) ) ) - loadUserFeedbacks$ = createEffect(() => + addUserFeedback$ = createEffect(() => this.actions$.pipe( - ofType(MdViewActions.loadUserFeedbacks), - exhaustMap(({ datasetUuid }) => - this.platformServiceInterface.getUserFeedbacks(datasetUuid).pipe( - map((userFeedbacks) => - MdViewActions.loadUserFeedbacksSuccess({ userFeedbacks }) - ), - catchError((error) => - of(MdViewActions.loadUserFeedbacksFailure({ error })) + ofType(MdViewActions.addUserFeedback), + mergeMap((action) => + this.platformServiceInterface + .postUserFeedbacks(action.userFeedback) + .pipe( + map(() => + MdViewActions.addUserFeedbackSuccess({ + datasetUuid: action.userFeedback.metadataUUID, + }) + ), + catchError((error) => { + return of(MdViewActions.addUserFeedbackFailure({ otherError: error.message })) + }) ) - ) ) ) ) diff --git a/libs/feature/record/src/lib/state/mdview.facade.spec.ts b/libs/feature/record/src/lib/state/mdview.facade.spec.ts index d1b21834f2..486471884b 100644 --- a/libs/feature/record/src/lib/state/mdview.facade.spec.ts +++ b/libs/feature/record/src/lib/state/mdview.facade.spec.ts @@ -7,17 +7,27 @@ import { import { MdViewFacade } from './mdview.facade' import * as MdViewActions from './mdview.actions' import { hot } from 'jasmine-marbles' -import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' +import { A_USER_FEEDBACK, DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' +import { DatavizConfigurationModel } from '@geonetwork-ui/common/domain/model/dataviz/dataviz-configuration.model' +import { AvatarServiceInterface } from '@geonetwork-ui/api/repository' describe('MdViewFacade', () => { let store: MockStore let facade: MdViewFacade + const chartConfigMock: DatavizConfigurationModel = { + aggregation: 'sum', + xProperty: 'anneeappro', + yProperty: 'nbre_com', + chartType: 'bar', + } + beforeEach(() => { TestBed.configureTestingModule({ imports: [], providers: [ MdViewFacade, + AvatarServiceInterface, provideMockStore({ initialState: { [METADATA_VIEW_FEATURE_STATE_KEY]: initialMetadataViewState, @@ -28,11 +38,13 @@ describe('MdViewFacade', () => { store = TestBed.inject(MockStore) facade = TestBed.inject(MdViewFacade) }) + describe('isPresent$', () => { it('emits false if no metadata', () => { const expected = hot('a', { a: false }) expect(facade.isPresent$).toBeObservable(expected) }) + it('emits true if metadata', () => { store.setState({ [METADATA_VIEW_FEATURE_STATE_KEY]: { @@ -44,11 +56,13 @@ describe('MdViewFacade', () => { expect(facade.isPresent$).toBeObservable(expected) }) }) + describe('metadata$', () => { it('does not emit if no metadata', () => { const expected = hot('-') expect(facade.metadata$).toBeObservable(expected) }) + it('emits metadata if present', () => { store.setState({ [METADATA_VIEW_FEATURE_STATE_KEY]: { @@ -60,11 +74,13 @@ describe('MdViewFacade', () => { expect(facade.metadata$).toBeObservable(expected) }) }) + describe('allLinks$', () => { it('does not emit if no links', () => { const expected = hot('-') expect(facade.allLinks$).toBeObservable(expected) }) + it('emits allLinks if present', () => { store.setState({ [METADATA_VIEW_FEATURE_STATE_KEY]: { @@ -76,6 +92,7 @@ describe('MdViewFacade', () => { expect(facade.allLinks$).toBeObservable(expected) }) }) + describe('isIncomplete$', () => { it('emits true if full record is loading', () => { store.setState({ @@ -88,6 +105,7 @@ describe('MdViewFacade', () => { const expected = hot('a', { a: true }) expect(facade.isIncomplete$).toBeObservable(expected) }) + it('emits false if full metadata loaded', () => { store.setState({ [METADATA_VIEW_FEATURE_STATE_KEY]: { @@ -99,17 +117,21 @@ describe('MdViewFacade', () => { const expected = hot('a', { a: false }) expect(facade.isIncomplete$).toBeObservable(expected) }) + it('does not emit if no metadata', () => { const expected = hot('-') expect(facade.isIncomplete$).toBeObservable(expected) }) }) + describe('error$', () => { let values + beforeEach(() => { values = [] facade.error$.subscribe((v) => values.push(v)) }) + it('emits the error if any', () => { store.setState({ [METADATA_VIEW_FEATURE_STATE_KEY]: { @@ -119,9 +141,11 @@ describe('MdViewFacade', () => { }) expect(values).toEqual([null, 'something went wrong']) }) + it('emits null if no error', () => { expect(values).toEqual([null]) }) + it('emits the error and null', () => { store.setState({ [METADATA_VIEW_FEATURE_STATE_KEY]: { @@ -138,6 +162,7 @@ describe('MdViewFacade', () => { expect(values).toEqual([null, 'something went wrong', null]) }) }) + describe('setIncompleteMetadata', () => { it('dispatches a setIncompleteMetadata action', () => { facade.setIncompleteMetadata(DATASET_RECORDS[0]) @@ -149,7 +174,8 @@ describe('MdViewFacade', () => { expect(store.scannedActions$).toBeObservable(expected) }) }) - describe('close', () => { + + describe('closeMetadata', () => { it('dispatches a close action', () => { facade.closeMetadata() const expected = hot('a', { @@ -158,11 +184,32 @@ describe('MdViewFacade', () => { expect(store.scannedActions$).toBeObservable(expected) }) }) + describe('setChartConfig', () => { it('dispatches a setChartConfig action', () => { - facade.setChartConfig() + facade.setChartConfig(chartConfigMock) + const expected = hot('a', { + a: MdViewActions.setChartConfig( { chartConfig: chartConfigMock }), + }) + expect(store.scannedActions$).toBeObservable(expected) + }) + }) + + describe('addUserFeedback', () => { + it('dispatches a addUserFeedback action', () => { + facade.addUserFeedback(A_USER_FEEDBACK) + const expected = hot('a', { + a: MdViewActions.addUserFeedback( { userFeedback: A_USER_FEEDBACK }), + }) + expect(store.scannedActions$).toBeObservable(expected) + }) + }) + + describe('loadUserFeedbacks', () => { + it('dispatches a loadUserFeedbacks action', () => { + facade.loadUserFeedbacks(expect.any(Number)) const expected = hot('a', { - a: MdViewActions.setChartConfig(), + a: MdViewActions.loadUserFeedbacks( { datasetUuid: expect.any(Number) }), }) expect(store.scannedActions$).toBeObservable(expected) }) diff --git a/libs/feature/record/src/lib/state/mdview.facade.ts b/libs/feature/record/src/lib/state/mdview.facade.ts index 7e785f4b50..c8aa010cbb 100644 --- a/libs/feature/record/src/lib/state/mdview.facade.ts +++ b/libs/feature/record/src/lib/state/mdview.facade.ts @@ -144,17 +144,4 @@ export class MdViewFacade { loadUserFeedbacks(datasetUuid: string) { this.store.dispatch(MdViewActions.loadUserFeedbacks({ datasetUuid })) } - - async createUserFeedbackViewModel( - baseUserFeedback: UserFeedback - ): Promise { - const userAvatarUrl = await this.avatarService.getProfileIconUrl( - baseUserFeedback.authorUserId?.toString() - ) - - return { - ...baseUserFeedback, - avatarUrl: userAvatarUrl, - } - } } diff --git a/libs/feature/record/src/lib/state/mdview.reducer.spec.ts b/libs/feature/record/src/lib/state/mdview.reducer.spec.ts index 1f53bcf2ba..a66378a1d5 100644 --- a/libs/feature/record/src/lib/state/mdview.reducer.spec.ts +++ b/libs/feature/record/src/lib/state/mdview.reducer.spec.ts @@ -1,21 +1,18 @@ import * as MdViewActions from './mdview.actions' import { initialMetadataViewState, reducer } from './mdview.reducer' -import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures' +import { A_USER_FEEDBACK, DATASET_RECORDS, SOME_USER_FEEDBACKS } from '@geonetwork-ui/common/fixtures' +import { DatavizConfigurationModel } from '@geonetwork-ui/common/domain/model/dataviz/dataviz-configuration.model' -const chartConfigMock = { +const chartConfigMock: DatavizConfigurationModel = { aggregation: 'sum', xProperty: 'anneeappro', yProperty: 'nbre_com', chartType: 'bar', } -const withErrorMdViewState = { - ...initialMetadataViewState, - error: { otherError: 'Some error' }, -} - -describe('MdView Reducer', () => { +describe('metadataViewReducer', () => { describe('undefined action', () => { + it('should return the default state', () => { const action = {} as any const state = reducer(undefined, action) @@ -26,22 +23,26 @@ describe('MdView Reducer', () => { describe('loadFullMetadata', () => { let action + beforeEach(() => { action = MdViewActions.loadFullMetadata({ - uniqueIdentifier: '123132132132132132', + uuid: '123132132132132132', }) }) + it('store the loading state', () => { - const state = reducer(withErrorMdViewState, action) + const state = reducer(initialMetadataViewState, action) expect(state).toEqual({ - ...withErrorMdViewState, + ...initialMetadataViewState, error: null, loadingFull: true, }) }) }) + describe('setIncompleteMetadata', () => { let action + beforeEach(() => { const { uniqueIdentifier, title, ...rest } = DATASET_RECORDS[0] action = MdViewActions.setIncompleteMetadata({ @@ -51,10 +52,11 @@ describe('MdView Reducer', () => { }, }) }) + it('saves incomplete metadata', () => { - const state = reducer(withErrorMdViewState, action) + const state = reducer(initialMetadataViewState, action) expect(state).toEqual({ - ...withErrorMdViewState, + ...initialMetadataViewState, error: null, metadata: { title: @@ -64,34 +66,40 @@ describe('MdView Reducer', () => { }) }) }) - describe('loadFullRecordSuccess', () => { + + describe('loadFullMetadataSuccess', () => { let action + beforeEach(() => { action = MdViewActions.loadFullMetadataSuccess({ full: DATASET_RECORDS[0], }) }) + it('saves full metadata ', () => { const state = reducer( - { ...withErrorMdViewState, loadingFull: true }, + { ...initialMetadataViewState, loadingFull: true }, action ) expect(state).toEqual({ - ...withErrorMdViewState, + ...initialMetadataViewState, error: null, loadingFull: false, metadata: DATASET_RECORDS[0], }) }) }) + describe('loadFullRecordFailure', () => { let action + beforeEach(() => { action = MdViewActions.loadFullMetadataFailure({ - otherError: 'error', + otherError: 'Some error', notFound: true, }) }) + it('set error', () => { const state = reducer( { ...initialMetadataViewState, loadingFull: true }, @@ -100,17 +108,20 @@ describe('MdView Reducer', () => { expect(state).toEqual({ ...initialMetadataViewState, loadingFull: false, - error: { otherError: 'error', notFound: true }, + error: { otherError: 'Some error', notFound: true }, }) }) }) + describe('setRelated', () => { let action + beforeEach(() => { action = MdViewActions.setRelated({ related: [DATASET_RECORDS[1]], }) }) + it('set related records', () => { const state = reducer({ ...initialMetadataViewState }, action) expect(state).toEqual({ @@ -119,26 +130,32 @@ describe('MdView Reducer', () => { }) }) }) + describe('setChartConfig', () => { let action + beforeEach(() => { action = MdViewActions.setChartConfig({ - chartConfig: [chartConfigMock], + chartConfig: chartConfigMock, }) }) + it('set chart config', () => { const state = reducer({ ...initialMetadataViewState }, action) expect(state).toEqual({ ...initialMetadataViewState, - chartConfig: [chartConfigMock], + chartConfig: chartConfigMock, }) }) }) - describe('close', () => { + + describe('closeMetadata', () => { let action + beforeEach(() => { action = MdViewActions.closeMetadata() }) + it('set error', () => { const state = reducer( { @@ -152,4 +169,90 @@ describe('MdView Reducer', () => { expect(state).toEqual(initialMetadataViewState) }) }) + + describe('loadUserFeedbacks', () => { + let action + + beforeEach(() => { + action = MdViewActions.loadUserFeedbacks({ + datasetUuid: expect.any(Number), + }) + }) + + it('return states without error and with allUserFeedbacksLoading true', () => { + const state = reducer(initialMetadataViewState, action) + expect(state).toEqual({ + ...initialMetadataViewState, + error: null, + loadingFull: false, + allUserFeedbacksLoading: true, + addUserFeedbackLoading: false, + }) + }) + }) + + describe('addUserFeedback', () => { + let action + + beforeEach(() => { + action = MdViewActions.addUserFeedback({ + userFeedback: A_USER_FEEDBACK, + }) + }) + + it('return states without error and with addUserFeedbackLoading true', () => { + const state = reducer({ ...initialMetadataViewState }, action) + expect(state).toEqual({ + ...initialMetadataViewState, + addUserFeedbackLoading: true, + }) + }) + }) + + describe('loadUserFeedbacksSuccess', () => { + let action + + beforeEach(() => { + action = MdViewActions.loadUserFeedbacksSuccess({ + userFeedbacks: SOME_USER_FEEDBACKS, + }) + }) + + it('return states without error and with userfeedbacks', () => { + const state = reducer( + { ...initialMetadataViewState, allUserFeedbacksLoading: true }, + action + ) + expect(state).toEqual({ + ...initialMetadataViewState, + error: null, + addUserFeedbackLoading: false, + allUserFeedbacksLoading: false, + loadingFull: false, + userFeedbacks: SOME_USER_FEEDBACKS, + }) + }) + }) + + describe('loadUserFeedbacksFailure', () => { + let action + + beforeEach(() => { + action = MdViewActions.loadUserFeedbacksFailure({ + otherError: 'Some error', + notFound: true, + }) + }) + + it('set error', () => { + const state = reducer( + { ...initialMetadataViewState }, + action + ) + expect(state).toEqual({ + ...initialMetadataViewState, + error: { otherError: 'Some error', notFound: true }, + }) + }) + }) }) diff --git a/libs/feature/record/src/lib/state/mdview.reducer.ts b/libs/feature/record/src/lib/state/mdview.reducer.ts index 8f168e3aba..074670b2b8 100644 --- a/libs/feature/record/src/lib/state/mdview.reducer.ts +++ b/libs/feature/record/src/lib/state/mdview.reducer.ts @@ -93,14 +93,15 @@ const metadataViewReducer = createReducer( MetadataViewActions.loadUserFeedbacksSuccess, (state, { userFeedbacks }) => ({ ...state, + error: null, userFeedbacks: userFeedbacks, addUserFeedbackLoading: false, allUserFeedbacksLoading: false, }) ), - on(MetadataViewActions.loadUserFeedbacksFailure, (state, { error }) => ({ + on(MetadataViewActions.loadUserFeedbacksFailure, (state, { otherError, notFound }) => ({ ...state, - error: { otherError: error.message }, + error: { otherError, notFound }, addUserFeedbackLoading: false, allUserFeedbacksLoading: false, })) diff --git a/libs/feature/record/src/lib/state/mdview.selectors.spec.ts b/libs/feature/record/src/lib/state/mdview.selectors.spec.ts index 54b9ef8f59..23524200d6 100644 --- a/libs/feature/record/src/lib/state/mdview.selectors.spec.ts +++ b/libs/feature/record/src/lib/state/mdview.selectors.spec.ts @@ -34,20 +34,25 @@ describe('MdView Selectors', () => { const results = MdViewSelectors.getMetadataUuid.projector(state) expect(results).toBe('321321321321') }) + it('returns null if no metadata in the state', () => { const results = MdViewSelectors.getMetadataUuid.projector({ loadingFull: false, error: null, + allUserFeedbacksLoading: false, + addUserFeedbackLoading: false, }) expect(results).toBe(null) }) }) + describe('getMetadata', () => { it('returns the metadata in the state', () => { const results = MdViewSelectors.getMetadata.projector(state) expect(results).toBe(state.metadata) }) }) + describe('getMetadataIsIncomplete', () => { it('returns true when incomplete', () => { const results = MdViewSelectors.getMetadataIsIncomplete.projector({ @@ -56,23 +61,29 @@ describe('MdView Selectors', () => { }) expect(results).toBe(true) }) + it('returns false when complete', () => { const results = MdViewSelectors.getMetadataIsIncomplete.projector(state) expect(results).toBe(false) }) + it('returns null if no metadata', () => { const results = MdViewSelectors.getMetadataIsIncomplete.projector({ loadingFull: false, error: null, + allUserFeedbacksLoading: false, + addUserFeedbackLoading: false, }) expect(results).toBe(null) }) }) + describe('getMetadataIsLoading', () => { it('returns false if not loading', () => { const results = MdViewSelectors.getMetadataIsLoading.projector(state) expect(results).toBe(false) }) + it('returns true if loading', () => { const results = MdViewSelectors.getMetadataIsLoading.projector({ ...state, @@ -81,6 +92,7 @@ describe('MdView Selectors', () => { expect(results).toBe(true) }) }) + describe('getMetadataError', () => { it('returns error if present', () => { const results = MdViewSelectors.getMetadataError.projector({ @@ -89,14 +101,18 @@ describe('MdView Selectors', () => { }) expect(results).toBe('ouch') }) + it('returns null if no error', () => { const results = MdViewSelectors.getMetadataError.projector({ loadingFull: false, error: null, + allUserFeedbacksLoading: false, + addUserFeedbackLoading: false, }) expect(results).toBe(null) }) }) + describe('getRelated', () => { it('returns related records', () => { const results = MdViewSelectors.getRelated.projector({ @@ -106,6 +122,7 @@ describe('MdView Selectors', () => { expect(results).toEqual([relatedRecord]) }) }) + describe('getChartConfig', () => { it('returns chart config', () => { const results = MdViewSelectors.getChartConfig.projector({