Skip to content

Commit 8e8fb9d

Browse files
CLARIN-DSpace v9/Port #1260 + #1241 (licence-contract and static-page spec parity) to the v9 base (#1499)
* Port #1260 to dtq-dev-9-base: License-contract page refactor - restore the dropped spec TESTS-ONLY. The runtime half of #1260 (list mode without a collectionId, PaginationService-driven paging, loadAuthorizedCollections, clearPagination on destroy) is already on the v9 base and matches the source hunk for hunk; the v9 squash dropped the spec file whole. This restores it, so the six behaviours the refactor introduced are pinned again. The six assertions are the fork's, unchanged: component creation, collectionRD$, licenseRD$, hasFailed on a bogus collectionId, list mode loading authorized collections when the query param is missing, and clearPagination on destroy in list mode. v9 notes: - declarations:[LicenseContractPageComponent, MockNgVarDirective] -> imports:[LicenseContractPageComponent]. The component is standalone and imports the real VarDirective, so the fork's *ngVar mock directive is dropped rather than adapted - keeping it would shadow the directive under test. - ErrorComponent, PaginationComponent and ThemedLoadingComponent are removed from the component's own imports via overrideComponent and NO_ERRORS_SCHEMA is added there, because two of the tests render list mode and PaginationComponent pulls in a provider graph this spec has no business standing up. - `of` instead of the fork's `of as observableOf` (dspace-angular-ts/alias-imports). - The getAuthorizedCollection assertion is new and v9-specific: it pins the 6-arg signature including the mandatory searchHref 'findSubmitAuthorized', which is exactly the one place the v9 runtime differs from the source commit. - routeStub/collectionService/paginationService are const (the fork's `let` + later assignment trips prefer-const on v9). Card PB-06 (tranche T3). Source: f2fe67b (dtq-dev PR #1260) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Port #1241 to dtq-dev-9-base: Static pages - SSR render and 404 - restore the two dropped specs TESTS-ONLY. The runtime half of #1241 is on the v9 base: HtmlContentService resolves namespace-aware URLs and, under SSR, absolute ones from the express REQUEST; StaticPageComponent tracks contentState and calls ServerResponseService.setNotFound() so a missing static page really answers 404. The v9 squash dropped both spec files whole. This restores them. html-content.service.spec.ts - the fork's 5 tests plus 5 that pin the v9-only half of the service: - namespaced URL for the default locale, localized URL for a non-default locale, fallback localized -> default (twice, with and without a trailing slash on the namespace), and getHtmlContent swallowing a failure into ''. - NEW: on the server with a REQUEST the request URL is absolute (https://host/repository/...), on the server without one it stays relative, in the browser the REQUEST is ignored, a URL that already carries the namespace is not prefixed twice, and an absolute http(s) URL is left alone. The first of these is the assertion the card asks for: it fails loudly if REQUEST is imported from the wrong module, because @optional() injection would then silently hand the service undefined and the URL would stay relative. The "no REQUEST" test is its counterpart, so the pair cannot both pass by accident. static-page.component.spec.ts - the fork's 19 tests unchanged in substance: creation, content load, file name from the route, the five OAI-link rewrite cases, the five contentState cases (including 'not-found' plus setNotFound()), the two change-detection cases and the four link-handling cases. v9 notes: - LocaleService.getCurrentLanguageCode() returns an Observable on v9, not a string, so the stub returns of(languageCode). That also puts a microtask between the call and the HTTP request, so the two non-fakeAsync tests await a settle() before expectOne; the fakeAsync ones tick(). Without this the fork's spec would fail here - it is a real behavioural difference, not a flake. - HttpClientTestingModule -> provideHttpClient() + provideHttpClientTesting() (the module is deprecated on Angular 20; this is what the other v9 specs use). - declarations:[StaticPageComponent, ClarinSafeHtmlPipe] -> imports:[StaticPageComponent]; the pipe is one of the component's own imports. RouterModule and ThemedLoadingComponent are removed from those imports via overrideComponent with NO_ERRORS_SCHEMA, because the loading and 404 branches render <ds-loading> and routerLink and would otherwise need the theme and router provider graphs. - submission-section-cc-licenses.component.spec.ts is deliberately NOT touched: the third hunk of #1241 added fakeAsync/tick(350) around a debounceTime that the v9 component does not have (grep debounceTime -> 0). Card PB-07 (tranche T3). Source: f8495ea (dtq-dev PR #1241) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent c65a7ff commit 8e8fb9d

3 files changed

Lines changed: 736 additions & 0 deletions

File tree

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
import { NO_ERRORS_SCHEMA } from '@angular/core';
2+
import {
3+
ComponentFixture,
4+
TestBed,
5+
} from '@angular/core/testing';
6+
import {
7+
ActivatedRoute,
8+
Params,
9+
} from '@angular/router';
10+
import { TranslateModule } from '@ngx-translate/core';
11+
import { of } from 'rxjs';
12+
13+
import {
14+
SortDirection,
15+
SortOptions,
16+
} from '../core/cache/models/sort-options.model';
17+
import { CollectionDataService } from '../core/data/collection-data.service';
18+
import { FindListOptions } from '../core/data/find-list-options.model';
19+
import { buildPaginatedList } from '../core/data/paginated-list.model';
20+
import { PaginationService } from '../core/pagination/pagination.service';
21+
import { Collection } from '../core/shared/collection.model';
22+
import { License } from '../core/shared/license.model';
23+
import { PageInfo } from '../core/shared/page-info.model';
24+
import { ErrorComponent } from '../shared/error/error.component';
25+
import { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
26+
import { PaginationComponent } from '../shared/pagination/pagination.component';
27+
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
28+
import {
29+
createFailedRemoteDataObject$,
30+
createSuccessfulRemoteDataObject$,
31+
} from '../shared/remote-data.utils';
32+
import { LicenseContractPageComponent } from './license-contract-page.component';
33+
34+
describe('LicenseContractPageComponent', () => {
35+
let component: LicenseContractPageComponent;
36+
let fixture: ComponentFixture<LicenseContractPageComponent>;
37+
38+
const paramCollectionId = 'collectionId';
39+
const paramCollectionIdValue = '1';
40+
41+
const paramObject: Params = {};
42+
paramObject[paramCollectionId] = paramCollectionIdValue;
43+
44+
const singleCollectionLicense = Object.assign(new License(), {
45+
text: 'Single collection license text',
46+
});
47+
48+
const collection = Object.assign(new Collection(), {
49+
uuid: 'fake-collection-id',
50+
name: 'Single collection',
51+
_links: {
52+
self: { href: 'collection-selflink' },
53+
license: { href: 'license-link' },
54+
},
55+
license: createSuccessfulRemoteDataObject$(singleCollectionLicense),
56+
});
57+
58+
const secondCollectionLicense = Object.assign(new License(), {
59+
text: 'Second collection license text',
60+
});
61+
62+
const authorizedCollections = [
63+
collection,
64+
Object.assign(new Collection(), {
65+
uuid: 'second-collection-id',
66+
name: 'Second collection',
67+
_links: {
68+
self: { href: 'second-collection-selflink' },
69+
license: { href: 'second-license-link' },
70+
},
71+
license: createSuccessfulRemoteDataObject$(secondCollectionLicense),
72+
}),
73+
];
74+
75+
const authorizedCollectionsRD$ = createSuccessfulRemoteDataObject$(
76+
buildPaginatedList(
77+
new PageInfo({
78+
currentPage: 1,
79+
elementsPerPage: 10,
80+
totalElements: authorizedCollections.length,
81+
totalPages: 1,
82+
}),
83+
authorizedCollections,
84+
),
85+
);
86+
87+
const routeStub: any = {
88+
snapshot: {
89+
queryParams: { ...paramObject },
90+
},
91+
};
92+
93+
const collectionService = jasmine.createSpyObj<CollectionDataService>('collectionService', {
94+
findById: createSuccessfulRemoteDataObject$(collection),
95+
getAuthorizedCollection: authorizedCollectionsRD$,
96+
});
97+
98+
const paginationService = jasmine.createSpyObj<PaginationService>('paginationService', {
99+
getFindListOptions: of(Object.assign(new FindListOptions(), {
100+
currentPage: 1,
101+
elementsPerPage: 10,
102+
})),
103+
getCurrentPagination: of(Object.assign(new PaginationComponentOptions(), {
104+
currentPage: 1,
105+
pageSize: 10,
106+
pageSizeOptions: [1, 5, 10, 20, 40, 60, 80, 100],
107+
})),
108+
getCurrentSort: of(new SortOptions('name', SortDirection.ASC)),
109+
clearPagination: undefined,
110+
});
111+
112+
beforeEach(async () => {
113+
await TestBed.configureTestingModule({
114+
imports: [
115+
TranslateModule.forRoot(),
116+
LicenseContractPageComponent,
117+
],
118+
providers: [
119+
{ provide: ActivatedRoute, useValue: routeStub },
120+
{ provide: CollectionDataService, useValue: collectionService },
121+
{ provide: PaginationService, useValue: paginationService },
122+
],
123+
})
124+
.overrideComponent(LicenseContractPageComponent, {
125+
remove: {
126+
imports: [ErrorComponent, PaginationComponent, ThemedLoadingComponent],
127+
},
128+
add: {
129+
schemas: [NO_ERRORS_SCHEMA],
130+
},
131+
})
132+
.compileComponents();
133+
});
134+
135+
beforeEach(() => {
136+
routeStub.snapshot.queryParams = { ...paramObject };
137+
collectionService.findById.and.returnValue(createSuccessfulRemoteDataObject$(collection));
138+
collectionService.findById.calls.reset();
139+
collectionService.getAuthorizedCollection.calls.reset();
140+
paginationService.getFindListOptions.calls.reset();
141+
paginationService.clearPagination.calls.reset();
142+
fixture = TestBed.createComponent(LicenseContractPageComponent);
143+
component = fixture.componentInstance;
144+
fixture.detectChanges();
145+
});
146+
147+
it('should create', () => {
148+
expect(component).toBeTruthy();
149+
});
150+
151+
it('should load collectionRD$', () => {
152+
expect(component.collectionRD$.value.payload).toEqual(collection);
153+
});
154+
155+
it('should load licenseRD$', () => {
156+
expect(component.licenseRD$.value.payload).toEqual(singleCollectionLicense);
157+
});
158+
159+
it('should set hasFailed on collectionRD$ when collectionId is bogus', () => {
160+
collectionService.findById.and.returnValue(createFailedRemoteDataObject$('Not Found', 404));
161+
collectionService.findById.calls.reset();
162+
163+
const failFixture = TestBed.createComponent(LicenseContractPageComponent);
164+
const failComponent = failFixture.componentInstance;
165+
failFixture.detectChanges();
166+
167+
expect(collectionService.findById).toHaveBeenCalled();
168+
expect(failComponent.collectionRD$.value.hasFailed).toBeTrue();
169+
});
170+
171+
it('should load authorized collections when collectionId is missing', () => {
172+
routeStub.snapshot.queryParams = {};
173+
collectionService.findById.calls.reset();
174+
collectionService.getAuthorizedCollection.calls.reset();
175+
176+
const listFixture = TestBed.createComponent(LicenseContractPageComponent);
177+
const listComponent = listFixture.componentInstance;
178+
listFixture.detectChanges();
179+
180+
expect(collectionService.findById).not.toHaveBeenCalled();
181+
expect(collectionService.getAuthorizedCollection).toHaveBeenCalled();
182+
// v9 signature: the searchHref argument ('findSubmitAuthorized') is mandatory
183+
expect(collectionService.getAuthorizedCollection).toHaveBeenCalledWith(
184+
'', jasmine.any(FindListOptions), true, true, 'findSubmitAuthorized', jasmine.anything(),
185+
);
186+
187+
listComponent.collectionsRD$.subscribe((collectionsRD) => {
188+
expect(collectionsRD.payload.page).toEqual(authorizedCollections);
189+
});
190+
});
191+
192+
it('should clear pagination state on destroy in list mode', () => {
193+
routeStub.snapshot.queryParams = {};
194+
paginationService.clearPagination.calls.reset();
195+
196+
const listFixture = TestBed.createComponent(LicenseContractPageComponent);
197+
const listComponent = listFixture.componentInstance;
198+
listFixture.detectChanges();
199+
listComponent.ngOnDestroy();
200+
201+
expect(paginationService.clearPagination).toHaveBeenCalledWith(listComponent.paginationId);
202+
});
203+
204+
});

0 commit comments

Comments
 (0)