Merge pull request #2468 from tdonohue/cypress_accessibility_testing

Minor Accessibility Fixes & Enable accessibility scan on more pages
This commit is contained in:
Alan Orth
2023-08-30 17:40:30 +03:00
committed by GitHub
18 changed files with 130 additions and 93 deletions

View File

@@ -1,4 +1,3 @@
import { Options } from 'cypress-axe';
import { testA11y } from 'cypress/support/utils';
describe('Community List Page', () => {
@@ -13,13 +12,6 @@ describe('Community List Page', () => {
cy.get('[data-test="expand-button"]').click({ multiple: true });
// Analyze <ds-community-list-page> for accessibility issues
// Disable heading-order checks until it is fixed
testA11y('ds-community-list-page',
{
rules: {
'heading-order': { enabled: false }
}
} as Options
);
testA11y('ds-community-list-page');
});
});

View File

@@ -11,8 +11,7 @@ describe('Header', () => {
testA11y({
include: ['ds-header'],
exclude: [
['#search-navbar-container'], // search in navbar has duplicative ID. Will be fixed in #1174
['.dropdownLogin'] // "Log in" link has color contrast issues. Will be fixed in #1149
['#search-navbar-container'] // search in navbar has duplicative ID. Will be fixed in #1174
],
});
});

View File

@@ -1,4 +1,3 @@
import { Options } from 'cypress-axe';
import { TEST_ENTITY_PUBLICATION } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils';
@@ -19,13 +18,16 @@ describe('Item Page', () => {
cy.get('ds-item-page').should('be.visible');
// Analyze <ds-item-page> for accessibility issues
// Disable heading-order checks until it is fixed
testA11y('ds-item-page',
{
rules: {
'heading-order': { enabled: false }
}
} as Options
);
testA11y('ds-item-page');
});
it('should pass accessibility tests on full item page', () => {
cy.visit(ENTITYPAGE + '/full');
// <ds-full-item-page> tag must be loaded
cy.get('ds-full-item-page').should('be.visible');
// Analyze <ds-full-item-page> for accessibility issues
testA11y('ds-full-item-page');
});
});

View File

@@ -1,4 +1,5 @@
import { TEST_ADMIN_PASSWORD, TEST_ADMIN_USER, TEST_ENTITY_PUBLICATION } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils';
const page = {
openLoginMenu() {
@@ -123,4 +124,15 @@ describe('Login Modal', () => {
cy.location('pathname').should('eq', '/forgot');
cy.get('ds-forgot-email').should('exist');
});
it('should pass accessibility tests', () => {
cy.visit('/');
page.openLoginMenu();
cy.get('ds-log-in').should('exist');
// Analyze <ds-log-in> for accessibility issues
testA11y('ds-log-in');
});
});

View File

@@ -19,21 +19,7 @@ describe('My DSpace page', () => {
cy.get('.filter-toggle').click({ multiple: true });
// Analyze <ds-my-dspace-page> for accessibility issues
testA11y(
{
include: ['ds-my-dspace-page'],
exclude: [
['nouislider'] // Date filter slider is missing ARIA labels. Will be fixed by #1175
],
},
{
rules: {
// Search filters fail these two "moderate" impact rules
'heading-order': { enabled: false },
'landmark-unique': { enabled: false }
}
} as Options
);
testA11y('ds-my-dspace-page');
});
it('should have a working detailed view that passes accessibility tests', () => {

View File

@@ -1,8 +1,13 @@
import { testA11y } from 'cypress/support/utils';
describe('PageNotFound', () => {
it('should contain element ds-pagenotfound when navigating to page that doesnt exist', () => {
// request an invalid page (UUIDs at root path aren't valid)
cy.visit('/e9019a69-d4f1-4773-b6a3-bd362caa46f2', { failOnStatusCode: false });
cy.get('ds-pagenotfound').should('be.visible');
// Analyze <ds-pagenotfound> for accessibility issues
testA11y('ds-pagenotfound');
});
it('should not contain element ds-pagenotfound when navigating to existing page', () => {

View File

@@ -27,21 +27,7 @@ describe('Search Page', () => {
cy.get('[data-test="filter-toggle"]').click({ multiple: true });
// Analyze <ds-search-page> for accessibility issues
testA11y(
{
include: ['ds-search-page'],
exclude: [
['nouislider'] // Date filter slider is missing ARIA labels. Will be fixed by #1175
],
},
{
rules: {
// Search filters fail these two "moderate" impact rules
'heading-order': { enabled: false },
'landmark-unique': { enabled: false }
}
} as Options
);
testA11y('ds-search-page');
});
it('should have a working grid view that passes accessibility tests', () => {

View File

@@ -116,12 +116,12 @@
"morgan": "^1.10.0",
"ng-mocks": "^14.10.0",
"ng2-file-upload": "1.4.0",
"ng2-nouislider": "^1.8.3",
"ng2-nouislider": "^2.0.0",
"ngx-infinite-scroll": "^15.0.0",
"ngx-pagination": "6.0.3",
"ngx-sortablejs": "^11.1.0",
"ngx-ui-switch": "^14.0.3",
"nouislider": "^14.6.3",
"nouislider": "^15.7.1",
"pem": "1.14.7",
"prop-types": "^15.8.1",
"react-copy-to-clipboard": "^5.1.0",
@@ -159,11 +159,11 @@
"@types/sanitize-html": "^2.9.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"axe-core": "^4.7.0",
"axe-core": "^4.7.2",
"compression-webpack-plugin": "^9.2.0",
"copy-webpack-plugin": "^6.4.1",
"cross-env": "^7.0.3",
"cypress": "12.10.0",
"cypress": "12.17.4",
"cypress-axe": "^1.4.0",
"deep-freeze": "0.0.1",
"eslint": "^8.39.0",

View File

@@ -1,6 +1,6 @@
<h2 class="item-page-title-field">
<h1 class="item-page-title-field">
<div *ngIf="item.firstMetadataValue('dspace.entity.type') as type" class="d-inline">
{{ type.toLowerCase() + '.page.titleprefix' | translate }}
</div>
<span class="dont-break-out">{{ dsoNameService.getName(item) }}</span>
</h2>
</h1>

View File

@@ -1,5 +1,5 @@
<div class="simple-view-element" [class.d-none]="hideIfNoTextContent && content.textContent.trim().length === 0">
<h5 class="simple-view-element-header" *ngIf="label">{{ label }}</h5>
<h2 class="simple-view-element-header" *ngIf="label">{{ label }}</h2>
<div #content class="simple-view-element-body">
<ng-content></ng-content>
</div>

View File

@@ -2,4 +2,7 @@
.simple-view-element {
margin-bottom: 15px;
}
.simple-view-element-header {
font-size: 1.25rem;
}
}

View File

@@ -6,9 +6,9 @@
[attr.aria-label]="(((collapsed$ | async) ? 'search.filters.filter.expand' : 'search.filters.filter.collapse') | translate) + ' ' + (('search.filters.filter.' + filter.name + '.head') | translate | lowercase)"
[attr.data-test]="'filter-toggle' | dsBrowserOnly"
>
<h5 class="d-inline-block mb-0">
<h4 class="d-inline-block mb-0">
{{'search.filters.filter.' + filter.name + '.head'| translate}}
</h5>
</h4>
<span class="filter-toggle flex-grow-1 fas p-auto"
[ngClass]="(collapsed$ | async) ? 'fa-plus' : 'fa-minus'"
[title]="((collapsed$ | async) ? 'search.filters.filter.expand' : 'search.filters.filter.collapse') | translate">

View File

@@ -9,8 +9,8 @@
</span>
<input type="text" [(ngModel)]="range[0]" [name]="filterConfig.paramName + '.min'"
class="form-control" (blur)="onSubmit()"
aria-label="Mininum value"
[placeholder]="'search.filters.filter.' + filterConfig.name + '.min.placeholder' | translate"
[attr.aria-label]="minLabel"
[placeholder]="minLabel"
/>
</label>
</div>
@@ -21,8 +21,8 @@
</span>
<input type="text" [(ngModel)]="range[1]" [name]="filterConfig.paramName + '.max'"
class="form-control" (blur)="onSubmit()"
aria-label="Maximum value"
[placeholder]="'search.filters.filter.' + filterConfig.name + '.max.placeholder' | translate"
[attr.aria-label]="maxLabel"
[placeholder]="maxLabel"
/>
</label>
</div>
@@ -33,7 +33,7 @@
</form>
<ng-container *ngIf="shouldShowSlider()">
<nouislider [connect]="true" [min]="min" [max]="max" [step]="1"
<nouislider [connect]="true" [config]="config" [min]="min" [max]="max" [step]="1"
[dsDebounce]="250" (onDebounce)="onSubmit()"
(keydown)="startKeyboardControl()" (keyup)="stopKeyboardControl()"
[(ngModel)]="range" ngDefaultControl>

View File

@@ -2,6 +2,7 @@ import { BehaviorSubject, combineLatest as observableCombineLatest, Subscription
import { map, startWith } from 'rxjs/operators';
import { isPlatformBrowser } from '@angular/common';
import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service';
import { FilterType } from '../../../models/filter-type.model';
import { renderFacetFor } from '../search-filter-type-decorator';
@@ -53,11 +54,27 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
*/
min = 1950;
/**
* i18n Label to use for minimum field
*/
minLabel: string;
/**
* Fallback maximum for the range
*/
max = new Date().getUTCFullYear();
/**
* i18n Label to use for maximum field
*/
maxLabel: string;
/**
* Base configuration for nouislider
* https://refreshless.com/nouislider/slider-options/
*/
config = {};
/**
* The current range of the filter
*/
@@ -78,6 +95,7 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
protected filterService: SearchFilterService,
protected router: Router,
protected rdbs: RemoteDataBuildService,
private translateService: TranslateService,
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
@Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean,
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig,
@@ -96,6 +114,8 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
super.ngOnInit();
this.min = yearFromString(this.filterConfig.minValue) || this.min;
this.max = yearFromString(this.filterConfig.maxValue) || this.max;
this.minLabel = this.translateService.instant('search.filters.filter.' + this.filterConfig.name + '.min.placeholder');
this.maxLabel = this.translateService.instant('search.filters.filter.' + this.filterConfig.name + '.max.placeholder');
const iniMin = this.route.getQueryParameterValue(this.filterConfig.paramName + RANGE_FILTER_MIN_SUFFIX).pipe(startWith(undefined));
const iniMax = this.route.getQueryParameterValue(this.filterConfig.paramName + RANGE_FILTER_MAX_SUFFIX).pipe(startWith(undefined));
this.sub = observableCombineLatest(iniMin, iniMax).pipe(
@@ -105,6 +125,15 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
return [minimum, maximum];
})
).subscribe((minmax) => this.range = minmax);
// Default/base config for nouislider
this.config = {
// Ensure draggable handles have labels
handleAttributes: [
{ 'aria-label': this.minLabel },
{ 'aria-label': this.maxLabel },
],
};
}
/**

View File

@@ -1,5 +1,5 @@
<div class="setting-option mb-3 p-3">
<h5><label for="{{id}}">{{label | translate}}</label></h5>
<h4><label for="{{id}}">{{label | translate}}</label></h4>
<select id="{{id}}" class="form-control" (change)="change.emit($event)">
<ng-content></ng-content>
</select>

View File

@@ -1,5 +1,5 @@
// node_modules imports meant for all the themes
@import '~node_modules/bootstrap/scss/bootstrap.scss';
@import '~node_modules/nouislider/distribute/nouislider.min';
@import '~node_modules/nouislider/dist/nouislider.min';
@import '~node_modules/ngx-ui-switch/ui-switch.component.scss';

View File

@@ -17,7 +17,7 @@
background-color: var(--bs-primary);
}
h5 {
h4 {
font-size: 1.1rem
}
}

View File

@@ -1551,10 +1551,10 @@
resolved "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz"
integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==
"@cypress/request@^2.88.10":
version "2.88.11"
resolved "https://registry.npmjs.org/@cypress/request/-/request-2.88.11.tgz"
integrity sha512-M83/wfQ1EkspjkE2lNWNV5ui2Cv7UCv1swW1DqljahbzLVWltcsexQh8jYtuS/vzFXP+HySntGM83ZXA9fn17w==
"@cypress/request@2.88.12":
version "2.88.12"
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.12.tgz#ba4911431738494a85e93fb04498cb38bc55d590"
integrity sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
@@ -1571,7 +1571,7 @@
performance-now "^2.1.0"
qs "~6.10.3"
safe-buffer "^5.1.2"
tough-cookie "~2.5.0"
tough-cookie "^4.1.3"
tunnel-agent "^0.6.0"
uuid "^8.3.2"
@@ -2457,11 +2457,16 @@
resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz"
integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==
"@types/node@*", "@types/node@>=10.0.0", "@types/node@^14.14.31", "@types/node@^14.14.9":
"@types/node@*", "@types/node@>=10.0.0", "@types/node@^14.14.9":
version "14.18.42"
resolved "https://registry.npmjs.org/@types/node/-/node-14.18.42.tgz"
integrity sha512-xefu+RBie4xWlK8hwAzGh3npDz/4VhF6icY/shU+zv/1fNn+ZVG7T7CRwe9LId9sAYRPxI+59QBPuKL3WpyGRg==
"@types/node@^16.18.39":
version "16.18.46"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.46.tgz#9f2102d0ba74a318fcbe170cbff5463f119eab59"
integrity sha512-Mnq3O9Xz52exs3mlxMcQuA7/9VFe/dXcrgAyfjLkABIqxXKOgBRjyazTxUbjsxDa4BP7hhPliyjVTP9RDP14xg==
"@types/parse-json@^4.0.0":
version "4.0.0"
resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz"
@@ -3358,10 +3363,10 @@ aws4@^1.8.0:
resolved "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz"
integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==
axe-core@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==
axe-core@^4.7.2:
version "4.7.2"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0"
integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==
axios@0.21.4:
version "0.21.4"
@@ -4437,14 +4442,14 @@ cypress-axe@^1.4.0:
resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-1.4.0.tgz#e67482bfe9e740796bf77c7823f19781a8a2faff"
integrity sha512-Ut7NKfzjyKm0BEbt2WxuKtLkIXmx6FD2j0RwdvO/Ykl7GmB/qRQkwbKLk3VP35+83hiIr8GKD04PDdrTK5BnyA==
cypress@12.10.0:
version "12.10.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.10.0.tgz#b6264f77c214d63530ebac2b33c4d099bd40b715"
integrity sha512-Y0wPc221xKKW1/4iAFCphkrG2jNR4MjOne3iGn4mcuCaE7Y5EtXL83N8BzRsAht7GYfWVjJ/UeTqEdDKHz39HQ==
cypress@12.17.4:
version "12.17.4"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.4.tgz#b4dadf41673058493fa0d2362faa3da1f6ae2e6c"
integrity sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ==
dependencies:
"@cypress/request" "^2.88.10"
"@cypress/request" "2.88.12"
"@cypress/xvfb" "^1.2.4"
"@types/node" "^14.14.31"
"@types/node" "^16.18.39"
"@types/sinonjs__fake-timers" "8.1.1"
"@types/sizzle" "^2.3.2"
arch "^2.2.0"
@@ -4477,9 +4482,10 @@ cypress@12.10.0:
minimist "^1.2.8"
ospath "^1.2.2"
pretty-bytes "^5.6.0"
process "^0.11.10"
proxy-from-env "1.0.0"
request-progress "^3.0.0"
semver "^7.3.2"
semver "^7.5.3"
supports-color "^8.1.1"
tmp "~0.2.1"
untildify "^4.0.0"
@@ -8191,10 +8197,12 @@ ng2-file-upload@1.4.0:
dependencies:
tslib "^1.9.0"
ng2-nouislider@^1.8.3:
version "1.8.3"
resolved "https://registry.npmjs.org/ng2-nouislider/-/ng2-nouislider-1.8.3.tgz"
integrity sha512-Vl8tHCcJ/ioJLAs2t6FBC35sZq1P/O5ZdqdFwYxOCOMVbILGWNg+2gWZIjFstvv9pqb/mVvVUYe6qGG/mA/RBQ==
ng2-nouislider@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ng2-nouislider/-/ng2-nouislider-2.0.0.tgz#a62fd6cf3f1561be19a2691c2f68d21a46dc6006"
integrity sha512-NGbF/0w0+bZqclpSPFOlWIeVJaVwRRYFJzD1x8PClbw9GIeo7fCHoBzZ81y7K7FTJg6to+cgjSTFETPZG/Dizg==
dependencies:
tslib "^2.3.0"
ngx-infinite-scroll@^15.0.0:
version "15.0.0"
@@ -8337,10 +8345,10 @@ normalize-url@^4.5.0:
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz"
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
nouislider@^14.6.3:
version "14.7.0"
resolved "https://registry.npmjs.org/nouislider/-/nouislider-14.7.0.tgz"
integrity sha512-4RtQ1+LHJKesDCNJrXkQcwXAWCrC2aggdLYMstS/G5fEWL+fXZbUA9pwVNHFghMGuFGRATlDLNInRaPeRKzpFQ==
nouislider@^15.7.1:
version "15.7.1"
resolved "https://registry.yarnpkg.com/nouislider/-/nouislider-15.7.1.tgz#77d55e47d9b4cd771728515713df43b489db9705"
integrity sha512-5N7C1ru/i8y3dg9+Z6ilj6+m1EfabvOoaRa7ztpxBSKKRZso4vA52DGSbBJjw5XLtFr/LZ9SgGAXqyVtlVHO5w==
npm-bundled@^3.0.0:
version "3.0.0"
@@ -9267,6 +9275,11 @@ process-nextick-args@~2.0.0:
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
process@^0.11.10:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz"
@@ -10131,7 +10144,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8:
semver@^7.0.0, semver@^7.1.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
@@ -10912,6 +10925,16 @@ tough-cookie@^4.0.0, tough-cookie@^4.1.2:
universalify "^0.2.0"
url-parse "^1.5.3"
tough-cookie@^4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf"
integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==
dependencies:
psl "^1.1.33"
punycode "^2.1.1"
universalify "^0.2.0"
url-parse "^1.5.3"
tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz"