From 106c130d3258275091a3943e9adec60fd217c5cd Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 10 Nov 2026 12:07:06 +0200 Subject: [PATCH 1/2] fix(dashboard): removed collection image and link --- .../constants/dashboard-products.constants.ts | 7 ---- .../models/dashboard-product-link.model.ts | 2 - .../pages/dashboard/dashboard.component.html | 17 ++------ .../pages/dashboard/dashboard.component.ts | 37 +++++++++++------- src/assets/i18n/en.json | 1 - .../dashboard/products/osf-collections.png | Bin 14433 -> 0 bytes 6 files changed, 26 insertions(+), 38 deletions(-) delete mode 100644 src/assets/images/dashboard/products/osf-collections.png diff --git a/src/app/features/home/constants/dashboard-products.constants.ts b/src/app/features/home/constants/dashboard-products.constants.ts index 75ddab13e..aad039ae5 100644 --- a/src/app/features/home/constants/dashboard-products.constants.ts +++ b/src/app/features/home/constants/dashboard-products.constants.ts @@ -1,13 +1,6 @@ import { DashboardProductLink } from '../models/dashboard-product-link.model'; export const DASHBOARD_PRODUCT_LINKS: DashboardProductLink[] = [ - { - link: 'https://www.cos.io/products/osf-collections', - imageSrc: 'assets/images/dashboard/products/osf-collections.png', - altKey: 'home.loggedIn.dashboard.images.osfCollectionsImageAltText', - external: true, - testId: 'products-collections', - }, { link: '/institutions', imageSrc: 'assets/images/dashboard/products/osf-institutions.png', diff --git a/src/app/features/home/models/dashboard-product-link.model.ts b/src/app/features/home/models/dashboard-product-link.model.ts index 3f40c022c..d3997158b 100644 --- a/src/app/features/home/models/dashboard-product-link.model.ts +++ b/src/app/features/home/models/dashboard-product-link.model.ts @@ -2,6 +2,4 @@ export interface DashboardProductLink { imageSrc: string; altKey: string; link: string; - external?: boolean; - testId?: string; } diff --git a/src/app/features/home/pages/dashboard/dashboard.component.html b/src/app/features/home/pages/dashboard/dashboard.component.html index 9a5645c48..e7791102c 100644 --- a/src/app/features/home/pages/dashboard/dashboard.component.html +++ b/src/app/features/home/pages/dashboard/dashboard.component.html @@ -84,20 +84,9 @@

{{ 'home.loggedIn.latestResearch.title' | translate }}

@for (product of dashboardProducts; track product.imageSrc) { - @if (product.external) { - - - - } @else { - - - - } + + + }
} diff --git a/src/app/features/home/pages/dashboard/dashboard.component.ts b/src/app/features/home/pages/dashboard/dashboard.component.ts index 0e8a1730b..0eda1d14e 100644 --- a/src/app/features/home/pages/dashboard/dashboard.component.ts +++ b/src/app/features/home/pages/dashboard/dashboard.component.ts @@ -9,7 +9,17 @@ import { TablePageEvent } from 'primeng/table'; import { debounceTime, distinctUntilChanged, filter, tap } from 'rxjs'; import { isPlatformBrowser, NgOptimizedImage } from '@angular/common'; -import { Component, computed, DestroyRef, effect, inject, OnInit, PLATFORM_ID, signal } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + computed, + DestroyRef, + effect, + inject, + OnInit, + PLATFORM_ID, + signal, +} from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControl } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; @@ -50,6 +60,7 @@ import { DASHBOARD_PRODUCT_LINKS } from '../../constants/dashboard-products.cons ], templateUrl: './dashboard.component.html', styleUrl: './dashboard.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DashboardComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); @@ -57,8 +68,7 @@ export class DashboardComponent implements OnInit { private readonly route = inject(ActivatedRoute); private readonly customDialogService = inject(CustomDialogService); private readonly projectRedirectDialogService = inject(ProjectRedirectDialogService); - private readonly platformId = inject(PLATFORM_ID); - private readonly isBrowser = isPlatformBrowser(this.platformId); + private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID)); readonly searchControl = new FormControl(''); readonly activeProject = signal(null); @@ -83,17 +93,16 @@ export class DashboardComponent implements OnInit { return this.projects().filter((project) => project.title.toLowerCase().includes(search)); }); - readonly buttonTooltip = computed(() => { - return this.projectCreationDisabled() ? 'home.loggedIn.dashboard.createProjectDisabledTooltip' : ''; - }); - readonly existsProjects = computed(() => this.projects().length || !!this.searchControl.value?.length); - readonly noProjectsMessage = computed(() => { - if (this.projectCreationDisabled()) { - return 'home.loggedIn.dashboard.noCreatedProjectAndCreateProjectDisabled'; - } - return 'home.loggedIn.dashboard.noCreatedProject'; - }); + + readonly buttonTooltip = computed(() => + this.projectCreationDisabled() ? 'home.loggedIn.dashboard.createProjectDisabledTooltip' : '' + ); + readonly noProjectsMessage = computed(() => + this.projectCreationDisabled() + ? 'home.loggedIn.dashboard.noCreatedProjectAndCreateProjectDisabled' + : 'home.loggedIn.dashboard.noCreatedProject' + ); readonly subHeaderTitle = computed(() => this.existsProjects() ? 'home.loggedIn.dashboard.title' : 'home.loggedIn.dashboard.welcome' ); @@ -215,7 +224,7 @@ export class DashboardComponent implements OnInit { this.customDialogService .open(CreateProjectDialogComponent, { header: 'myProjects.header.createProject', width: '850px' }) .onClose.pipe( - filter((result) => result?.project.id), + filter((result: { project: MyResourcesItem }) => !!result?.project.id), tap((result) => this.projectRedirectDialogService.showProjectRedirectDialog(result.project.id)), takeUntilDestroyed(this.destroyRef) ) diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index d4e7e7ad4..d75329824 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -807,7 +807,6 @@ "createProjectDisabledTooltip": "Project creation is no longer available. OSF is transitioning away from Projects as part of a platform update. Your existing projects remain accessible.", "getStartedHelp": "Visit Get Started Help Guides", "images": { - "osfCollectionsImageAltText": "OSF Collections", "osfInstitutionsImageAltText": "OSF Institutions", "osfPreprintsImageAltTest": "OSF Preprints", "osfRegistriesImageAltTest": "OSF Registries" diff --git a/src/assets/images/dashboard/products/osf-collections.png b/src/assets/images/dashboard/products/osf-collections.png deleted file mode 100644 index c85939d6ef8834c37034a771712b5919abbeaabe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14433 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 Date: Tue, 10 Nov 2026 12:07:27 +0200 Subject: [PATCH 2/2] test(dashboard): updated tests --- .../dashboard/dashboard.component.spec.ts | 284 +++++++----------- 1 file changed, 103 insertions(+), 181 deletions(-) diff --git a/src/app/features/home/pages/dashboard/dashboard.component.spec.ts b/src/app/features/home/pages/dashboard/dashboard.component.spec.ts index 7e207830a..bbc04c67f 100644 --- a/src/app/features/home/pages/dashboard/dashboard.component.spec.ts +++ b/src/app/features/home/pages/dashboard/dashboard.component.spec.ts @@ -2,13 +2,16 @@ import { Store } from '@ngxs/store'; import { MockComponents, MockProvider } from 'ng-mocks'; +import { SortEvent } from 'primeng/api'; +import { TablePageEvent } from 'primeng/table'; + import { Subject } from 'rxjs'; import { Mock } from 'vitest'; import { PLATFORM_ID } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, provideRouter, Router } from '@angular/router'; import { ScheduledBannerComponent } from '@core/components/osf-banners/scheduled-banner/scheduled-banner.component'; import { UserSelectors } from '@osf/core/store/user'; @@ -19,13 +22,20 @@ import { SearchInputComponent } from '@osf/shared/components/search-input/search import { SubHeaderComponent } from '@osf/shared/components/sub-header/sub-header.component'; import { FEATURE_FLAGS } from '@osf/shared/constants/feature-flags.const'; import { SortOrder } from '@osf/shared/enums/sort-order.enum'; -import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources-item.model'; import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'; import { ProjectRedirectDialogService } from '@osf/shared/services/project-redirect-dialog.service'; import { ClearMyResources, GetMyProjects, MyResourcesSelectors } from '@osf/shared/stores/my-resources'; +import { + MOCK_MY_RESOURCES_ITEM_PROJECT, + MOCK_MY_RESOURCES_ITEM_PROJECT_PRIVATE, +} from '@testing/mocks/my-resources.mock'; import { provideOSFCore } from '@testing/osf.testing.provider'; import { CustomDialogServiceMock, CustomDialogServiceMockType } from '@testing/providers/custom-dialog-provider.mock'; +import { + ProjectRedirectDialogServiceMock, + ProjectRedirectDialogServiceMockType, +} from '@testing/providers/project-redirect-dialog.service.mock'; import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock'; import { RouterMockBuilder, RouterMockType } from '@testing/providers/router-provider.mock'; import { @@ -36,7 +46,6 @@ import { } from '@testing/providers/store-provider.mock'; import { WorkflowLauncherSectionComponent } from '../../components/workflow-launcher-section/workflow-launcher-section.component'; -import { DASHBOARD_PRODUCT_LINKS } from '../../constants/dashboard-products.constants'; import { DashboardComponent } from './dashboard.component'; @@ -46,27 +55,10 @@ describe('DashboardComponent', () => { let store: Store; let routerMock: RouterMockType; let customDialogService: CustomDialogServiceMockType; - let projectRedirectDialogService: { showProjectRedirectDialog: Mock }; - - const projectItem: MyResourcesItem = { - id: '1', - type: 'nodes', - title: 'Alpha project', - dateCreated: '2024-01-01', - dateModified: '2024-01-02', - isPublic: true, - contributors: [], - }; - - const secondProjectItem: MyResourcesItem = { - id: '2', - type: 'nodes', - title: 'Beta project', - dateCreated: '2024-01-03', - dateModified: '2024-01-04', - isPublic: false, - contributors: [], - }; + let projectRedirectDialogService: ProjectRedirectDialogServiceMockType; + + const projectItem = MOCK_MY_RESOURCES_ITEM_PROJECT; + const secondProjectItem = MOCK_MY_RESOURCES_ITEM_PROJECT_PRIVATE; const defaultSignals: SignalOverride[] = [ { selector: MyResourcesSelectors.getProjects, value: [] }, @@ -84,7 +76,7 @@ describe('DashboardComponent', () => { function setup(options: SetupOverrides = {}) { routerMock = RouterMockBuilder.create().build(); customDialogService = CustomDialogServiceMock.simple(); - projectRedirectDialogService = { showProjectRedirectDialog: vi.fn() }; + projectRedirectDialogService = ProjectRedirectDialogServiceMock.simple(); const routeMock = ActivatedRouteMockBuilder.create() .withQueryParams(options.routeQueryParams ?? {}) .build(); @@ -103,11 +95,12 @@ describe('DashboardComponent', () => { ], providers: [ provideOSFCore(), + provideRouter([]), MockProvider(ActivatedRoute, routeMock), MockProvider(Router, routerMock), MockProvider(CustomDialogService, customDialogService), MockProvider(ProjectRedirectDialogService, projectRedirectDialogService), - MockProvider(PLATFORM_ID, options?.platformId ?? 'browser'), + MockProvider(PLATFORM_ID, options.platformId ?? 'browser'), provideMockStore({ signals: mergeSignalOverrides(defaultSignals, options.selectorOverrides) }), ], }); @@ -118,6 +111,10 @@ describe('DashboardComponent', () => { fixture.detectChanges(); } + afterEach(() => { + vi.useRealTimers(); + }); + it('should create and fetch projects with default params on init', () => { setup(); @@ -132,14 +129,6 @@ describe('DashboardComponent', () => { ); }); - it('should disable project creation and show tooltip when isProjectCreationDisabled is true', () => { - setup({ - selectorOverrides: [{ selector: UserSelectors.isProjectCreationDisabled, value: true }], - }); - - expect(component.buttonTooltip()).toBe('home.loggedIn.dashboard.createProjectDisabledTooltip'); - }); - it('should read query params and fetch projects on init', () => { setup({ routeQueryParams: { @@ -166,12 +155,6 @@ describe('DashboardComponent', () => { ); }); - it('should sync total projects count into table params', () => { - setup({ selectorOverrides: [{ selector: MyResourcesSelectors.getTotalProjects, value: 42 }] }); - - expect(component.tableParams().totalRecords).toBe(42); - }); - it('should default sort order when query param sort order is invalid', () => { setup({ routeQueryParams: { @@ -183,20 +166,26 @@ describe('DashboardComponent', () => { expect(component.sortOrder()).toBe(SortOrder.Asc); }); - it('should use dashboard sub header when projects exist', () => { + it('should sync total projects count into table params', () => { + setup({ selectorOverrides: [{ selector: MyResourcesSelectors.getTotalProjects, value: 42 }] }); + + expect(component.tableParams().totalRecords).toBe(42); + }); + + it('should disable project creation messaging when isProjectCreationDisabled is true', () => { setup({ - selectorOverrides: [{ selector: MyResourcesSelectors.getProjects, value: [projectItem] }], + selectorOverrides: [{ selector: UserSelectors.isProjectCreationDisabled, value: true }], }); - expect(component.existsProjects()).toBeTruthy(); - expect(component.subHeaderTitle()).toBe('home.loggedIn.dashboard.title'); + expect(component.buttonTooltip()).toBe('home.loggedIn.dashboard.createProjectDisabledTooltip'); + expect(component.noProjectsMessage()).toBe('home.loggedIn.dashboard.noCreatedProjectAndCreateProjectDisabled'); }); it('should update query params on page change', () => { setup(); (routerMock.navigate as Mock).mockClear(); - component.onPageChange({ first: 20, rows: 10 } as never); + component.onPageChange({ first: 20, rows: 10 } as TablePageEvent); expect(routerMock.navigate).toHaveBeenCalledWith([], { relativeTo: TestBed.inject(ActivatedRoute), @@ -215,10 +204,10 @@ describe('DashboardComponent', () => { setup(); (routerMock.navigate as Mock).mockClear(); - component.onSort({ field: 'dateModified', order: -1 } as never); + component.onSort({ field: 'dateModified', order: SortOrder.Desc } as SortEvent); expect(component.sortColumn()).toBe('dateModified'); - expect(component.sortOrder()).toBe(-1); + expect(component.sortOrder()).toBe(SortOrder.Desc); expect(routerMock.navigate).toHaveBeenCalledWith([], { relativeTo: TestBed.inject(ActivatedRoute), queryParams: { @@ -226,7 +215,7 @@ describe('DashboardComponent', () => { rows: 10, search: undefined, sortField: 'dateModified', - sortOrder: -1, + sortOrder: SortOrder.Desc, }, queryParamsHandling: 'merge', }); @@ -236,85 +225,85 @@ describe('DashboardComponent', () => { setup(); (routerMock.navigate as Mock).mockClear(); - component.onSort({ field: undefined, order: SortOrder.Desc } as never); + component.onSort({ field: undefined, order: SortOrder.Desc } as SortEvent); expect(routerMock.navigate).not.toHaveBeenCalled(); }); - it('should dispatch fetch projects with current search and sort state', () => { + it('should update query params after search debounce', () => { + vi.useFakeTimers(); setup(); - (store.dispatch as Mock).mockClear(); - - component.searchControl.setValue('alp'); - component.sortColumn.set('title'); - component.sortOrder.set(-1); - - component.fetchProjects(); + (routerMock.navigate as Mock).mockClear(); - expect(store.dispatch).toHaveBeenCalledWith( - new GetMyProjects(1, 10, { - searchValue: 'alp', - searchFields: ['title'], - sortColumn: 'title', - sortOrder: -1, - }) - ); - }); + component.searchControl.setValue('alpha'); + vi.advanceTimersByTime(300); - it('should filter projects by search value', () => { - setup({ - routeQueryParams: { search: 'alp' }, - selectorOverrides: [ - { - selector: MyResourcesSelectors.getProjects, - value: [projectItem, secondProjectItem], - }, - ], + expect(routerMock.navigate).toHaveBeenCalledWith([], { + relativeTo: TestBed.inject(ActivatedRoute), + queryParams: { + page: 1, + rows: 10, + search: 'alpha', + sortField: undefined, + sortOrder: 1, + }, + queryParamsHandling: 'merge', }); - - expect(component.filteredProjects()).toEqual([projectItem]); - }); - - it('should treat search value as existing projects when list is empty', () => { - setup({ routeQueryParams: { search: 'query' } }); - - expect(component.existsProjects()).toBeTruthy(); - expect(component.subHeaderTitle()).toBe('home.loggedIn.dashboard.title'); }); - it('should use welcome sub header when no projects and no search', () => { + it('should not update query params when debounced search value is unchanged', () => { + vi.useFakeTimers(); setup(); + component.searchControl.setValue('alpha'); + vi.advanceTimersByTime(300); + (routerMock.navigate as Mock).mockClear(); - expect(component.existsProjects()).toBe(false); - expect(component.subHeaderTitle()).toBe('home.loggedIn.dashboard.welcome'); - }); - - it('should expose dashboard product links', () => { - setup(); + component.searchControl.setValue('alpha'); + vi.advanceTimersByTime(300); - expect(component.dashboardProducts).toBe(DASHBOARD_PRODUCT_LINKS); + expect(routerMock.navigate).not.toHaveBeenCalled(); }); - it('should update query params after search debounce', () => { - vi.useFakeTimers(); + it('should omit empty search from query params', () => { setup(); (routerMock.navigate as Mock).mockClear(); - component.searchControl.setValue('alpha'); - vi.advanceTimersByTime(300); + component.searchControl.setValue(''); + component.updateQueryParams(); expect(routerMock.navigate).toHaveBeenCalledWith([], { relativeTo: TestBed.inject(ActivatedRoute), queryParams: { page: 1, rows: 10, - search: 'alpha', + search: undefined, sortField: undefined, sortOrder: 1, }, queryParamsHandling: 'merge', }); - vi.useRealTimers(); + }); + + it('should filter projects by search value case insensitively', () => { + setup({ + routeQueryParams: { search: 'PRIVATE' }, + selectorOverrides: [ + { + selector: MyResourcesSelectors.getProjects, + value: [projectItem, secondProjectItem], + }, + ], + }); + + expect(component.filteredProjects()).toEqual([secondProjectItem]); + }); + + it('should return all projects when search is empty', () => { + setup({ + selectorOverrides: [{ selector: MyResourcesSelectors.getProjects, value: [projectItem, secondProjectItem] }], + }); + + expect(component.filteredProjects()).toEqual([projectItem, secondProjectItem]); }); it('should navigate to project and set active project', () => { @@ -343,12 +332,14 @@ describe('DashboardComponent', () => { it('should not redirect when create project dialog closes without project id', () => { setup(); - const onClose$ = new Subject(); + const onClose$ = new Subject<{ project?: { id?: string } } | null>(); customDialogService.open.mockReturnValue(CustomDialogServiceMock.dialogRefWithClose(onClose$.asObservable())); component.createProject(); onClose$.next(null); + expect(projectRedirectDialogService.showProjectRedirectDialog).not.toHaveBeenCalled(); + onClose$.next({ project: {} }); expect(projectRedirectDialogService.showProjectRedirectDialog).not.toHaveBeenCalled(); }); @@ -402,100 +393,31 @@ describe('DashboardComponent', () => { expect(fixture.nativeElement.querySelector('osf-workflow-launcher-section')).toBeFalsy(); }); - it('should return all projects when search is empty', () => { - setup({ - selectorOverrides: [{ selector: MyResourcesSelectors.getProjects, value: [projectItem, secondProjectItem] }], - }); - - expect(component.filteredProjects()).toEqual([projectItem, secondProjectItem]); - }); - - it('should filter projects case insensitively', () => { - setup({ - routeQueryParams: { search: 'ALPHA' }, - selectorOverrides: [{ selector: MyResourcesSelectors.getProjects, value: [projectItem, secondProjectItem] }], - }); - - expect(component.filteredProjects()).toEqual([projectItem]); - }); - - it('should omit empty search from query params', () => { + it('should use welcome state when no projects and no search', () => { setup(); - (routerMock.navigate as Mock).mockClear(); - - component.searchControl.setValue(''); - component.updateQueryParams(); - expect(routerMock.navigate).toHaveBeenCalledWith([], { - relativeTo: TestBed.inject(ActivatedRoute), - queryParams: { - page: 1, - rows: 10, - search: undefined, - sortField: undefined, - sortOrder: 1, - }, - queryParamsHandling: 'merge', - }); - }); - - it('should not update query params when debounced search value is unchanged', () => { - vi.useFakeTimers(); - setup(); - component.searchControl.setValue('alpha'); - vi.advanceTimersByTime(300); - (routerMock.navigate as Mock).mockClear(); - - component.searchControl.setValue('alpha'); - vi.advanceTimersByTime(300); - - expect(routerMock.navigate).not.toHaveBeenCalled(); - vi.useRealTimers(); + expect(component.existsProjects()).toBe(false); + expect(component.subHeaderTitle()).toBe('home.loggedIn.dashboard.welcome'); + expect(component.noProjectsMessage()).toBe('home.loggedIn.dashboard.noCreatedProject'); + expect(fixture.nativeElement.querySelector('osf-my-projects-table')).toBeFalsy(); + expect(fixture.nativeElement.querySelector('.latest-research-container')).toBeFalsy(); }); - it('should render empty state when no projects exist', () => { - setup(); + it('should treat search value as existing projects when list is empty', () => { + setup({ routeQueryParams: { search: 'query' } }); - expect(fixture.nativeElement.querySelector('.connected-record-image')).toBeTruthy(); - expect(fixture.nativeElement.querySelector('osf-my-projects-table')).toBeFalsy(); - expect(fixture.nativeElement.querySelector('.latest-research-container')).toBeFalsy(); + expect(component.existsProjects()).toBeTruthy(); + expect(component.subHeaderTitle()).toBe('home.loggedIn.dashboard.title'); }); - it('should render projects table and latest research when projects exist', () => { + it('should show projects table when projects exist', () => { setup({ selectorOverrides: [{ selector: MyResourcesSelectors.getProjects, value: [projectItem] }], }); + expect(component.existsProjects()).toBeTruthy(); + expect(component.subHeaderTitle()).toBe('home.loggedIn.dashboard.title'); expect(fixture.nativeElement.querySelector('osf-my-projects-table')).toBeTruthy(); expect(fixture.nativeElement.querySelector('.latest-research-container')).toBeTruthy(); - expect(fixture.nativeElement.querySelector('.connected-record-image')).toBeFalsy(); - }); - - it('should render dashboard product links', () => { - setup(); - - const productImages = fixture.nativeElement.querySelectorAll('.flex.flex-column.p-4 img'); - - expect(productImages.length).toBe(DASHBOARD_PRODUCT_LINKS.length); - }); - - it('should render external product link with blank target', () => { - setup(); - - const externalLink = fixture.nativeElement.querySelector('[data-test-products-collections]'); - - expect(externalLink?.getAttribute('target')).toBe('_blank'); - expect(externalLink?.getAttribute('rel')).toBe('noopener noreferrer'); - }); - - it('should not redirect when create project dialog closes with missing project id', () => { - setup(); - const onClose$ = new Subject<{ project: { id?: string } }>(); - customDialogService.open.mockReturnValue(CustomDialogServiceMock.dialogRefWithClose(onClose$.asObservable())); - - component.createProject(); - onClose$.next({ project: {} }); - - expect(projectRedirectDialogService.showProjectRedirectDialog).not.toHaveBeenCalled(); }); });