From ddc57ee1b63f94b89b1ee57a6dd016a6a2e3bb40 Mon Sep 17 00:00:00 2001 From: nsemets Date: Fri, 10 Oct 2025 15:21:11 +0300 Subject: [PATCH] fix(files): fixed files name validation --- .../scheduled-banner.component.spec.ts | 2 +- .../application.initialization.provider.ts | 2 +- src/app/core/services/auth.service.ts | 2 +- .../auth/pages/sign-up/sign-up.component.html | 2 +- .../auth/pages/sign-up/sign-up.component.ts | 4 +-- .../create-folder-dialog.component.ts | 1 + .../rename-file-dialog.component.ts | 1 + .../file-detail/file-detail.component.spec.ts | 1 - .../features/preprints/components/index.ts | 2 +- .../preprint-warning-banner.component.ts | 4 +-- .../share-and-download.component.html | 0 .../share-and-download.component.scss | 0 .../share-and-download.component.spec.ts | 0 .../share-and-download.component.ts | 0 .../preprint-details.component.spec.ts | 2 +- .../configure-addon.component.spec.ts | 5 ++-- .../helpers/custom-form-validators.helper.ts | 16 ++++++++++++ src/app/shared/helpers/state-error.handler.ts | 4 +-- .../datacite/datacite.service.spec.ts | 4 --- ...oogle-file-picker.download.service.spec.ts | 10 +++---- src/app/shared/services/meta-tags.service.ts | 26 ++++++++----------- .../activity-logs/activity-logs.state.spec.ts | 1 - 22 files changed, 44 insertions(+), 45 deletions(-) rename src/app/features/preprints/components/preprint-details/{share-and-downlaod => share-and-download}/share-and-download.component.html (100%) rename src/app/features/preprints/components/preprint-details/{share-and-downlaod => share-and-download}/share-and-download.component.scss (100%) rename src/app/features/preprints/components/preprint-details/{share-and-downlaod => share-and-download}/share-and-download.component.spec.ts (100%) rename src/app/features/preprints/components/preprint-details/{share-and-downlaod => share-and-download}/share-and-download.component.ts (100%) diff --git a/src/app/core/components/osf-banners/scheduled-banner/scheduled-banner.component.spec.ts b/src/app/core/components/osf-banners/scheduled-banner/scheduled-banner.component.spec.ts index 7379b2013..a72198b04 100644 --- a/src/app/core/components/osf-banners/scheduled-banner/scheduled-banner.component.spec.ts +++ b/src/app/core/components/osf-banners/scheduled-banner/scheduled-banner.component.spec.ts @@ -87,7 +87,7 @@ describe('Component: Scheduled Banner', () => { expect(showBanner()).toBeTruthy(); const parent = fixture.nativeElement.querySelector('[data-test-banner-parent]'); const link = fixture.nativeElement.querySelector('[data-test-banner-href]'); - expect(parent.style.backgroundColor).toBe('rgb(18, 52, 86)'); // hex to rgb + expect(parent.style.backgroundColor).toBe('rgb(18, 52, 86)'); expect(link.getAttribute('href')).toBe(''); }); diff --git a/src/app/core/provider/application.initialization.provider.ts b/src/app/core/provider/application.initialization.provider.ts index db22ec5bc..9d7c7a406 100644 --- a/src/app/core/provider/application.initialization.provider.ts +++ b/src/app/core/provider/application.initialization.provider.ts @@ -37,7 +37,7 @@ export function initializeApplication() { dsn, environment: environment.production ? 'production' : 'development', maxBreadcrumbs: 50, - sampleRate: 1.0, // error sample rate + sampleRate: 1.0, integrations: [], }); } diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts index 68d497761..7da6a169e 100644 --- a/src/app/core/services/auth.service.ts +++ b/src/app/core/services/auth.service.ts @@ -39,7 +39,7 @@ export class AuthService { window.location.href = loginUrl; } - navigateToOrcidSingIn(): void { + navigateToOrcidSignIn(): void { const loginUrl = `${this.casUrl}/login?${urlParam({ redirectOrcid: 'true', service: `${this.webUrl}/login/?next=${encodeURIComponent(this.webUrl)}`, diff --git a/src/app/features/auth/pages/sign-up/sign-up.component.html b/src/app/features/auth/pages/sign-up/sign-up.component.html index 549b114db..0a303cb2f 100644 --- a/src/app/features/auth/pages/sign-up/sign-up.component.html +++ b/src/app/features/auth/pages/sign-up/sign-up.component.html @@ -9,7 +9,7 @@

{{ 'auth.signUp.title' | translate }}

[label]="'auth.signUp.social.orcid' | translate" variant="text" raised - (onClick)="navigateToOrcidSingIn()" + (onClick)="navigateToOrcidSignIn()" > Orcid icon diff --git a/src/app/features/auth/pages/sign-up/sign-up.component.ts b/src/app/features/auth/pages/sign-up/sign-up.component.ts index 1d841c18a..47c5c5b14 100644 --- a/src/app/features/auth/pages/sign-up/sign-up.component.ts +++ b/src/app/features/auth/pages/sign-up/sign-up.component.ts @@ -110,8 +110,8 @@ export class SignUpComponent implements OnInit { }); } - navigateToOrcidSingIn(): void { - this.authService.navigateToOrcidSingIn(); + navigateToOrcidSignIn(): void { + this.authService.navigateToOrcidSignIn(); } navigateToInstitutionSingIn(): void { diff --git a/src/app/features/files/components/create-folder-dialog/create-folder-dialog.component.ts b/src/app/features/files/components/create-folder-dialog/create-folder-dialog.component.ts index 2d24747de..76f9f8a3a 100644 --- a/src/app/features/files/components/create-folder-dialog/create-folder-dialog.component.ts +++ b/src/app/features/files/components/create-folder-dialog/create-folder-dialog.component.ts @@ -26,6 +26,7 @@ export class CreateFolderDialogComponent { validators: [ CustomValidators.requiredTrimmed(), CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters), + CustomValidators.noPeriodAtEnd(), ], }), }); diff --git a/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts index 01a04057b..e7d2dd0eb 100644 --- a/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts +++ b/src/app/features/files/components/rename-file-dialog/rename-file-dialog.component.ts @@ -29,6 +29,7 @@ export class RenameFileDialogComponent { validators: [ CustomValidators.requiredTrimmed(), CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters), + CustomValidators.noPeriodAtEnd(), ], }), }); diff --git a/src/app/features/files/pages/file-detail/file-detail.component.spec.ts b/src/app/features/files/pages/file-detail/file-detail.component.spec.ts index 32e73ecf4..ec5fd7d46 100644 --- a/src/app/features/files/pages/file-detail/file-detail.component.spec.ts +++ b/src/app/features/files/pages/file-detail/file-detail.component.spec.ts @@ -1,4 +1,3 @@ -// Dependencies import { Store } from '@ngxs/store'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; diff --git a/src/app/features/preprints/components/index.ts b/src/app/features/preprints/components/index.ts index 3c13eac87..c8c2a5544 100644 --- a/src/app/features/preprints/components/index.ts +++ b/src/app/features/preprints/components/index.ts @@ -4,7 +4,7 @@ export { AdditionalInfoComponent } from './preprint-details/additional-info/addi export { GeneralInformationComponent } from './preprint-details/general-information/general-information.component'; export { ModerationStatusBannerComponent } from './preprint-details/moderation-status-banner/moderation-status-banner.component'; export { PreprintFileSectionComponent } from './preprint-details/preprint-file-section/preprint-file-section.component'; -export { ShareAndDownloadComponent } from './preprint-details/share-and-downlaod/share-and-download.component'; +export { ShareAndDownloadComponent } from './preprint-details/share-and-download/share-and-download.component'; export { StatusBannerComponent } from './preprint-details/status-banner/status-banner.component'; export { PreprintProviderFooterComponent } from './preprint-provider-footer/preprint-provider-footer.component'; export { PreprintProviderHeroComponent } from './preprint-provider-hero/preprint-provider-hero.component'; diff --git a/src/app/features/preprints/components/preprint-details/preprint-warning-banner/preprint-warning-banner.component.ts b/src/app/features/preprints/components/preprint-details/preprint-warning-banner/preprint-warning-banner.component.ts index c37710873..fc55f6f4c 100644 --- a/src/app/features/preprints/components/preprint-details/preprint-warning-banner/preprint-warning-banner.component.ts +++ b/src/app/features/preprints/components/preprint-details/preprint-warning-banner/preprint-warning-banner.component.ts @@ -10,6 +10,4 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; templateUrl: './preprint-warning-banner.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PreprintWarningBannerComponent { - // -} +export class PreprintWarningBannerComponent {} diff --git a/src/app/features/preprints/components/preprint-details/share-and-downlaod/share-and-download.component.html b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html similarity index 100% rename from src/app/features/preprints/components/preprint-details/share-and-downlaod/share-and-download.component.html rename to src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html diff --git a/src/app/features/preprints/components/preprint-details/share-and-downlaod/share-and-download.component.scss b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.scss similarity index 100% rename from src/app/features/preprints/components/preprint-details/share-and-downlaod/share-and-download.component.scss rename to src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.scss diff --git a/src/app/features/preprints/components/preprint-details/share-and-downlaod/share-and-download.component.spec.ts b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.spec.ts similarity index 100% rename from src/app/features/preprints/components/preprint-details/share-and-downlaod/share-and-download.component.spec.ts rename to src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.spec.ts diff --git a/src/app/features/preprints/components/preprint-details/share-and-downlaod/share-and-download.component.ts b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.ts similarity index 100% rename from src/app/features/preprints/components/preprint-details/share-and-downlaod/share-and-download.component.ts rename to src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.ts diff --git a/src/app/features/preprints/pages/preprint-details/preprint-details.component.spec.ts b/src/app/features/preprints/pages/preprint-details/preprint-details.component.spec.ts index 5c52bb1d1..3d8f60121 100644 --- a/src/app/features/preprints/pages/preprint-details/preprint-details.component.spec.ts +++ b/src/app/features/preprints/pages/preprint-details/preprint-details.component.spec.ts @@ -16,7 +16,7 @@ import { AdditionalInfoComponent } from '@osf/features/preprints/components/prep import { GeneralInformationComponent } from '@osf/features/preprints/components/preprint-details/general-information/general-information.component'; import { PreprintFileSectionComponent } from '@osf/features/preprints/components/preprint-details/preprint-file-section/preprint-file-section.component'; import { PreprintWarningBannerComponent } from '@osf/features/preprints/components/preprint-details/preprint-warning-banner/preprint-warning-banner.component'; -import { ShareAndDownloadComponent } from '@osf/features/preprints/components/preprint-details/share-and-downlaod/share-and-download.component'; +import { ShareAndDownloadComponent } from '@osf/features/preprints/components/preprint-details/share-and-download/share-and-download.component'; import { ReviewsState } from '@osf/features/preprints/enums'; import { PreprintSelectors } from '@osf/features/preprints/store/preprint'; import { PreprintProvidersSelectors } from '@osf/features/preprints/store/preprint-providers'; diff --git a/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.spec.ts b/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.spec.ts index 219e20e40..0dae909d5 100644 --- a/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.spec.ts +++ b/src/app/features/project/project-addons/components/configure-addon/configure-addon.component.spec.ts @@ -39,14 +39,13 @@ describe.skip('Component: Configure Addon', () => { }, paramMap: of(new Map()), queryParamMap: of(new Map()), - // ... if you use `route.root`, add a dummy root too root: {}, }; describe('addon present', () => { beforeEach(async () => { const mockRouter = { - url: '/project/abc123/addons/configure', // mock the actual URL used in your component + url: '/project/abc123/addons/configure', navigate: jest.fn(), getCurrentNavigation: jest.fn().mockReturnValue({ extras: { @@ -141,7 +140,7 @@ describe.skip('Component: Configure Addon', () => { describe('addon not-present', () => { beforeEach(async () => { const mockRouter = { - url: '/project/abc123/addons/configure', // mock the actual URL used in your component + url: '/project/abc123/addons/configure', navigate: jest.fn(), getCurrentNavigation: jest.fn().mockReturnValue({ extras: { diff --git a/src/app/shared/helpers/custom-form-validators.helper.ts b/src/app/shared/helpers/custom-form-validators.helper.ts index b4f665435..deb0e218e 100644 --- a/src/app/shared/helpers/custom-form-validators.helper.ts +++ b/src/app/shared/helpers/custom-form-validators.helper.ts @@ -110,4 +110,20 @@ export class CustomValidators { return hasForbiddenCharacters ? { forbiddenCharacters: true } : null; }; } + + static noPeriodAtEnd(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value = control.value; + if (!value || typeof value !== 'string') { + return null; + } + + const trimmedValue = value.trim(); + if (trimmedValue.endsWith('.')) { + return { periodAtEnd: true }; + } + + return null; + }; + } } diff --git a/src/app/shared/helpers/state-error.handler.ts b/src/app/shared/helpers/state-error.handler.ts index 73e60b995..ad78ff982 100644 --- a/src/app/shared/helpers/state-error.handler.ts +++ b/src/app/shared/helpers/state-error.handler.ts @@ -5,7 +5,6 @@ import { throwError } from 'rxjs'; import * as Sentry from '@sentry/angular'; export function handleSectionError(ctx: StateContext, section: keyof T, error: Error) { - // Report error to Sentry Sentry.captureException(error, { tags: { 'state.section': section.toString(), @@ -13,7 +12,6 @@ export function handleSectionError(ctx: StateContext, section: keyof T, er }, }); - // Patch the state to update loading/submitting flags and set the error message ctx.patchState({ [section]: { ...ctx.getState()[section], @@ -22,6 +20,6 @@ export function handleSectionError(ctx: StateContext, section: keyof T, er error: error.message, }, } as Partial); - // Rethrow the error as an observable + return throwError(() => error); } diff --git a/src/app/shared/services/datacite/datacite.service.spec.ts b/src/app/shared/services/datacite/datacite.service.spec.ts index 2aff40242..6f13a7da6 100644 --- a/src/app/shared/services/datacite/datacite.service.spec.ts +++ b/src/app/shared/services/datacite/datacite.service.spec.ts @@ -143,7 +143,6 @@ describe('DataciteService', () => { service.logFileView(targetId, targetType).subscribe(); - // First request: GET identifiers const reqGet = httpMock.expectOne(`${apiDomainUrl}/v2/${targetType}/${targetId}/identifiers`); expect(reqGet.request.method).toBe('GET'); reqGet.flush({ @@ -156,7 +155,6 @@ describe('DataciteService', () => { ], }); - // Second request: POST to datacite tracker assertSuccess(httpMock, dataciteTrackerAddress, dataciteTrackerRepoId, doi, DataciteEvent.VIEW); }); @@ -167,7 +165,6 @@ describe('DataciteService', () => { service.logFileDownload(targetId, targetType).subscribe(); - // First request: GET identifiers const reqGet = httpMock.expectOne(`${apiDomainUrl}/v2/${targetType}/${targetId}/identifiers`); expect(reqGet.request.method).toBe('GET'); reqGet.flush({ @@ -180,7 +177,6 @@ describe('DataciteService', () => { ], }); - // Second request: POST to datacite tracker assertSuccess(httpMock, dataciteTrackerAddress, dataciteTrackerRepoId, doi, DataciteEvent.DOWNLOAD); }); diff --git a/src/app/shared/services/google-file-picker.download.service.spec.ts b/src/app/shared/services/google-file-picker.download.service.spec.ts index 9ba6fc0d1..79a6624ee 100644 --- a/src/app/shared/services/google-file-picker.download.service.spec.ts +++ b/src/app/shared/services/google-file-picker.download.service.spec.ts @@ -61,12 +61,10 @@ describe('Service: Google File Picker Download', () => { it('should emit error when script fails to load', (done) => { const mockScriptElement: Partial = {}; - // Mock document const mockDocument = { createElement: jest.fn(() => mockScriptElement), body: { appendChild: jest.fn(() => { - // Simulate async error after appendChild setTimeout(() => { mockScriptElement.onerror?.(new Event('error')); }, 0); @@ -75,7 +73,6 @@ describe('Service: Google File Picker Download', () => { querySelector: jest.fn(() => null), }; - // Re-instantiate service with mocked document const service = new GoogleFilePickerDownloadService(mockDocument as unknown as Document); service.loadScript().subscribe({ @@ -89,7 +86,6 @@ describe('Service: Google File Picker Download', () => { describe('loadGapiModules', () => { beforeEach(() => { - // Mock window.gapi (globalThis as any).gapi = { load: jest.fn(), }; @@ -118,7 +114,7 @@ describe('Service: Google File Picker Download', () => { }); const config = (globalThis.gapi.load as jest.Mock).mock.calls[0][1]; - config.callback(); // simulate success + config.callback(); }); it('should emit error when GAPI fails to load', (done) => { @@ -131,7 +127,7 @@ describe('Service: Google File Picker Download', () => { }); const config = (globalThis.gapi.load as jest.Mock).mock.calls[0][1]; - config.onerror(); // simulate failure + config.onerror(); }); it('should emit error on GAPI timeout', (done) => { @@ -144,7 +140,7 @@ describe('Service: Google File Picker Download', () => { }); const config = (globalThis.gapi.load as jest.Mock).mock.calls[0][1]; - config.ontimeout(); // simulate timeout + config.ontimeout(); }); }); }); diff --git a/src/app/shared/services/meta-tags.service.ts b/src/app/shared/services/meta-tags.service.ts index 36cfcdc13..02443477d 100644 --- a/src/app/shared/services/meta-tags.service.ts +++ b/src/app/shared/services/meta-tags.service.ts @@ -48,7 +48,6 @@ export class MetaTagsService { private readonly metaTagClass = 'osf-dynamic-meta'; - // data from all active routed components that set meta tags private metaTagStack: { metaTagsData: MetaTagsData; componentDestroyRef: DestroyRef }[] = []; constructor( @@ -83,12 +82,10 @@ export class MetaTagsService { } private metaTagStackWithout(destroyRefToRemove: DestroyRef) { - // get a copy of `this.metaTagStack` minus any entries with the given destroyRef return this.metaTagStack.filter(({ componentDestroyRef }) => componentDestroyRef !== destroyRefToRemove); } private applyNearestMetaTags() { - // apply the meta tags for the nearest active route that called `updateMetaTags` (if any) const nearest = this.metaTagStack.at(-1); if (nearest) { this.applyMetaTagsData(nearest.metaTagsData); @@ -102,18 +99,17 @@ export class MetaTagsService { const headTags = this.getHeadTags(combinedData); of(metaTagsData.osfGuid) .pipe( - switchMap( - (osfid) => - osfid // with an osf id, try getting schema.org json-ld from backend - ? this.getSchemaDotOrgJsonLdHeadTag(osfid).pipe( - tap((jsonLdHeadTag) => { - if (jsonLdHeadTag) { - headTags.push(jsonLdHeadTag); - } - }), - catchError(() => of(null)) // if it doesn't work, ignore and continue with given head tags - ) - : of(null) // without osfid, continue with only given head tags + switchMap((osfid) => + osfid + ? this.getSchemaDotOrgJsonLdHeadTag(osfid).pipe( + tap((jsonLdHeadTag) => { + if (jsonLdHeadTag) { + headTags.push(jsonLdHeadTag); + } + }), + catchError(() => of(null)) + ) + : of(null) ), tap(() => this.applyHeadTags(headTags)), tap(() => this.dispatchZoteroEvent()) diff --git a/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts b/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts index cfc01332e..95ff16568 100644 --- a/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts +++ b/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts @@ -65,7 +65,6 @@ describe.skip('State: ActivityLogs', () => { (httpMock: HttpTestingController) => { store.dispatch(new GetRegistrationActivityLogs('reg500', 1, 10)).subscribe(); - // loading true expect(store.selectSnapshot((s: any) => s.activityLogs.activityLogs.isLoading)).toBe(true); const req = httpMock.expectOne(buildRegistrationLogsUrl('reg500', 1, 10, environment.useValue.apiDomainUrl));