Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { of as observableOf } from 'rxjs';
import { AuthService } from '../../core/auth/auth.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { Collection } from '../../core/shared/collection.model';
import { UsageReport } from '../../core/statistics/models/usage-report.model';
import { UsageReportDataService } from '../../core/statistics/usage-report-data.service';
Expand Down Expand Up @@ -82,6 +83,7 @@ describe('CollectionStatisticsPageComponent', () => {
{ provide: DSpaceObjectDataService, useValue: {} },
{ provide: DSONameService, useValue: nameService },
{ provide: AuthService, useValue: authService },
{ provide: PaginationService, useValue: { getCurrentPagination: () => observableOf({ currentPage: 1, pageSize: 10 }) } },
],
})
.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { of as observableOf } from 'rxjs';
import { AuthService } from '../../core/auth/auth.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { Community } from '../../core/shared/community.model';
import { UsageReport } from '../../core/statistics/models/usage-report.model';
import { UsageReportDataService } from '../../core/statistics/usage-report-data.service';
Expand Down Expand Up @@ -82,6 +83,7 @@ describe('CommunityStatisticsPageComponent', () => {
{ provide: DSpaceObjectDataService, useValue: {} },
{ provide: DSONameService, useValue: nameService },
{ provide: AuthService, useValue: authService },
{ provide: PaginationService, useValue: { getCurrentPagination: () => observableOf({ currentPage: 1, pageSize: 10 }) } },
],
})
.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { of as observableOf } from 'rxjs';
import { AuthService } from '../../core/auth/auth.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { Item } from '../../core/shared/item.model';
import { UsageReport } from '../../core/statistics/models/usage-report.model';
import { UsageReportDataService } from '../../core/statistics/usage-report-data.service';
Expand Down Expand Up @@ -82,6 +83,7 @@ describe('ItemStatisticsPageComponent', () => {
{ provide: DSpaceObjectDataService, useValue: {} },
{ provide: DSONameService, useValue: nameService },
{ provide: AuthService, useValue: authService },
{ provide: PaginationService, useValue: { getCurrentPagination: () => observableOf({ currentPage: 1, pageSize: 10 }) } },
],
})
.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { AuthService } from '../../core/auth/auth.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { SiteDataService } from '../../core/data/site-data.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { Site } from '../../core/shared/site.model';
import { UsageReport } from '../../core/statistics/models/usage-report.model';
import { UsageReportDataService } from '../../core/statistics/usage-report-data.service';
Expand Down Expand Up @@ -83,6 +84,7 @@ describe('SiteStatisticsPageComponent', () => {
{ provide: DSONameService, useValue: nameService },
{ provide: SiteDataService, useValue: siteService },
{ provide: AuthService, useValue: authService },
{ provide: PaginationService, useValue: { getCurrentPagination: () => observableOf({ currentPage: 1, pageSize: 10 }) } },
],
})
.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,41 @@ <h2 class="m-1">
{{ 'statistics.table.title.' + report.reportType | translate }}
</h2>

<table class="table table-striped" [attr.data-test]="report.reportType">

<tbody>

<tr>
<td></td>
<th scope="col"
*ngFor="let header of headers"
class="{{header}}-header">
{{ header }}
</th>
</tr>

<tr *ngFor="let point of report.points"
class="{{point.id}}-data">
<th scope="row" data-test="statistics-label">
{{ getLabel(point) | async }}
</th>
<td *ngFor="let header of headers"
class="{{point.id}}-{{header}}-data">
{{ point.values[header] }}
</td>
</tr>

</tbody>

</table>
<ds-pagination [paginationOptions]="paginationOptions"
[collectionSize]="report.points.length"
[hideGear]="report.points.length <= pageSize"
[hidePaginationDetail]="report.points.length <= pageSize"
[hideSortOptions]="true"
[retainScrollPosition]="true">

Comment thread
milanmajchrak marked this conversation as resolved.
<table class="table table-striped" [attr.data-test]="report.reportType">

<tbody>

<tr>
<td></td>
<th scope="col"
*ngFor="let header of headers"
class="{{header}}-header">
{{ header }}
</th>
</tr>

<tr *ngFor="let point of (paginatedPoints$ | async)"
class="{{point.id}}-data">
<th scope="row" data-test="statistics-label">
{{ getLabel(point) | async }}
</th>
<td *ngFor="let header of headers"
class="{{point.id}}-{{header}}-data">
{{ point.values[header] }}
</td>
</tr>

</tbody>

</table>

</ds-pagination>

</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ th, td {
padding: 0.5rem;
}

// Space below the pagination top bar (results-per-page gear + "showing" detail)
// so it isn't glued to the table above it.
:host ::ng-deep ds-pagination .pagination-masked.top {
margin-bottom: 0.75rem;
}

td {
width: 50px;
max-width: 50px;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,61 @@
import { DebugElement } from '@angular/core';
import {
Component,
DebugElement,
Input,
} from '@angular/core';
import {
ComponentFixture,
TestBed,
waitForAsync,
} from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { TranslateModule } from '@ngx-translate/core';
import { BehaviorSubject } from 'rxjs';

import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { UsageReport } from '../../core/statistics/models/usage-report.model';
import { PaginationComponent } from '../../shared/pagination/pagination.component';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { StatisticsTableComponent } from './statistics-table.component';

/**
* Lightweight stand-in for ds-pagination so the table can be tested in isolation; the current page is
* driven through the (mocked) PaginationService, exactly as the real component does via the URL.
*/
@Component({
selector: 'ds-pagination',
standalone: true,
template: '<ng-content></ng-content>',
})
class MockPaginationComponent {
@Input() paginationOptions: PaginationComponentOptions;
@Input() collectionSize: number;
@Input() hideGear: boolean;
@Input() hidePaginationDetail: boolean;
@Input() hideSortOptions: boolean;
@Input() retainScrollPosition: boolean;
}

describe('StatisticsTableComponent', () => {

let component: StatisticsTableComponent;
let de: DebugElement;
let fixture: ComponentFixture<StatisticsTableComponent>;
let currentPagination$: BehaviorSubject<PaginationComponentOptions>;

const paginationService = {
getCurrentPagination: (_id: string, _options: PaginationComponentOptions) => currentPagination$.asObservable(),
};

const setPage = (currentPage: number, pageSize = 10) => {
currentPagination$.next(Object.assign(new PaginationComponentOptions(), { currentPage, pageSize }));
};

beforeEach(waitForAsync(() => {
currentPagination$ = new BehaviorSubject(Object.assign(new PaginationComponentOptions(), { currentPage: 1, pageSize: 10 }));

TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
Expand All @@ -27,8 +64,13 @@ describe('StatisticsTableComponent', () => {
providers: [
{ provide: DSpaceObjectDataService, useValue: {} },
{ provide: DSONameService, useValue: {} },
{ provide: PaginationService, useValue: paginationService },
],
})
.overrideComponent(StatisticsTableComponent, {
remove: { imports: [PaginationComponent] },
add: { imports: [MockPaginationComponent] },
})
.compileComponents();
}));

Expand All @@ -51,6 +93,10 @@ describe('StatisticsTableComponent', () => {
it ('should not display a table', () => {
expect(de.query(By.css('table'))).toBeNull();
});

it('should not display a pagination control', () => {
expect(de.query(By.directive(MockPaginationComponent))).toBeNull();
});
});

describe('when the storage report has data', () => {
Expand Down Expand Up @@ -96,5 +142,83 @@ describe('StatisticsTableComponent', () => {
expect(de.query(By.css('td.item_2-downloads-data')).nativeElement.innerText)
.toEqual('8');
});

it('should wrap the table in a ds-pagination control with the report size', () => {
const pagination = de.query(By.directive(MockPaginationComponent));
expect(pagination).toBeTruthy();
expect(pagination.componentInstance.collectionSize).toEqual(2);
});

it('should hide the page-size selector when everything fits on a single page', () => {
expect(de.query(By.directive(MockPaginationComponent)).componentInstance.hideGear).toBeTrue();
});
});

describe('when the report has more points than the page size', () => {

const numberOfPoints = 25;

beforeEach(() => {
const points = [];
for (let i = 0; i < numberOfPoints; i++) {
points.push({
id: `item_${i}`,
label: `item_${i}`,
values: {
views: i,
},
});
}
component.report = Object.assign(new UsageReport(), { points });
component.ngOnInit();
fixture.detectChanges();
});

it('should pass the full report size to the pagination control', () => {
expect(de.query(By.directive(MockPaginationComponent)).componentInstance.collectionSize)
.toEqual(numberOfPoints);
});

it('should offer the page-size selector with its options when there is more than one page', () => {
const pagination = de.query(By.directive(MockPaginationComponent)).componentInstance;
expect(pagination.hideGear).toBeFalse();
expect(pagination.paginationOptions.pageSizeOptions).toEqual([10, 20, 40, 60, 80, 100]);
});

it('should render more rows when a larger page size is selected', () => {
setPage(1, 20);
fixture.detectChanges();

expect(de.queryAll(By.css('[data-test="statistics-label"]')).length).toEqual(20);
expect(de.query(By.css('td.item_19-views-data'))).toBeTruthy();
});

it('should only render the first page of points', () => {
expect(de.queryAll(By.css('[data-test="statistics-label"]')).length)
.toEqual(component.pageSize);
expect(de.query(By.css('td.item_0-views-data'))).toBeTruthy();
expect(de.query(By.css('td.item_10-views-data'))).toBeNull();
});

it('should render the next page of points when the current page changes', () => {
setPage(2);
fixture.detectChanges();

expect(de.query(By.css('td.item_0-views-data'))).toBeNull();
expect(de.query(By.css('td.item_10-views-data'))).toBeTruthy();
expect(de.queryAll(By.css('[data-test="statistics-label"]')).length)
.toEqual(component.pageSize);
});

it('should render the remaining points on the last page', () => {
const lastPage = Math.ceil(numberOfPoints / component.pageSize);
setPage(lastPage);
fixture.detectChanges();

const remaining = numberOfPoints - (lastPage - 1) * component.pageSize;
expect(de.queryAll(By.css('[data-test="statistics-label"]')).length)
.toEqual(remaining);
expect(de.query(By.css(`td.item_${numberOfPoints - 1}-views-data`))).toBeTruthy();
});
});
});
Loading
Loading