Skip to content

Commit ade744e

Browse files
authored
Merge pull request opentripplanner#426 from opentripplanner/fix-a11y-issues
Fix Accessibility and Test for Accessibility Issues
2 parents 518def8 + 574da74 commit ade744e

File tree

19 files changed

+815
-409
lines changed

19 files changed

+815
-409
lines changed

.github/workflows/node-ci.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ on:
88

99
jobs:
1010
test-build-release:
11-
1211
runs-on: ubuntu-latest
1312

1413
steps:
@@ -29,6 +28,8 @@ jobs:
2928
run: yarn jest
3029
- name: Build example project
3130
run: yarn build
31+
- name: Run a11y tests
32+
run: yarn a11y-test
3233

3334
# at this point, the build is successful
3435
- name: Semantic Release

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ dist
1818
# secrets. Ignore any versions of config.yml, except for example.
1919
*config.yml
2020
!example-config.yml
21+
!test-config.yml

__tests__/components/viewers/__snapshots__/stop-viewer.js.snap

+40-60
Large diffs are not rendered by default.

a11y/a11y.test.js

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import fs from 'fs'
2+
import path from 'path'
3+
4+
import puppeteer from 'puppeteer'
5+
import execa from 'execa'
6+
7+
import { mockServer } from './mock-server'
8+
9+
const OTP_RR_CONFIG_FILE_PATH = './config.yml'
10+
const OTP_RR_CONFIG_BACKUP_PATH = './config.non-test.yml'
11+
const OTP_RR_TEST_CONFIG_PATH = './a11y/test-config.yml'
12+
13+
let server
14+
15+
beforeEach(() => {
16+
// backup current config file
17+
if (fs.existsSync(OTP_RR_CONFIG_FILE_PATH)) {
18+
fs.renameSync(
19+
OTP_RR_CONFIG_FILE_PATH,
20+
OTP_RR_CONFIG_BACKUP_PATH
21+
)
22+
console.log('Backed up current OTP-RR config file')
23+
}
24+
// copy over test config file
25+
fs.copyFileSync(
26+
OTP_RR_TEST_CONFIG_PATH,
27+
OTP_RR_CONFIG_FILE_PATH
28+
)
29+
console.log('Copied a11y test config file')
30+
31+
// Build OTP-RR main.js using new config file
32+
execa.sync('yarn', ['build'])
33+
console.log('Built OTP-RR')
34+
35+
// Launch mock OTP server
36+
const MOCK_SERVER_PORT = 9999
37+
server = mockServer.listen(MOCK_SERVER_PORT, () => {
38+
console.log(`Mock response server running on http://localhost:${MOCK_SERVER_PORT}`)
39+
})
40+
})
41+
42+
afterEach(async () => {
43+
fs.unlinkSync(OTP_RR_CONFIG_FILE_PATH)
44+
if (fs.existsSync(OTP_RR_CONFIG_BACKUP_PATH)) {
45+
fs.renameSync(
46+
path.resolve(OTP_RR_CONFIG_BACKUP_PATH),
47+
path.resolve(OTP_RR_CONFIG_FILE_PATH)
48+
)
49+
}
50+
console.log('Restored original OTP-RR config file')
51+
await server.close()
52+
console.log('Closed mock server')
53+
})
54+
55+
test('checks the test page with Axe', async () => {
56+
jest.setTimeout(600000)
57+
// Web security is disabled to allow requests to the mock OTP server
58+
const browser = await puppeteer.launch({args: ['--disable-web-security']})
59+
const page = await browser.newPage()
60+
await page.goto(`file://${path.resolve(__dirname, '../index-for-puppeteer.html')}#/?ui_activeSearch=0qoydlnut&ui_activeItinerary=0&fromPlace=1900%20Main%20Street%2C%20Houston%2C%20TX%2C%20USA%3A%3A29.750144%2C-95.370998&toPlace=800%20Congress%2C%20Houston%2C%20TX%2C%20USA%3A%3A29.76263%2C-95.362178&date=2021-08-04&time=08%3A14&arriveBy=false&mode=WALK%2CBUS%2CTRAM&showIntermediateStops=true&maxWalkDistance=1207&optimize=QUICK&walkSpeed=1.34&ignoreRealtimeUpdates=true&numItineraries=3&otherThanPreferredRoutesPenalty=900`)
61+
62+
// These rules aren't relevant to this project
63+
await expect(page).toPassAxeTests({
64+
disabledRules: [
65+
'region', // Leaflet does not comply
66+
'meta-viewport', // Leaflet does not comply
67+
'page-has-heading-one' // Heading is provided by logo
68+
]
69+
})
70+
await browser.close()
71+
})

a11y/mock-server.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const express = require('express')
2+
3+
const PLAN_REALTIME = require('./mocks/plan.json')
4+
const STOPS_FIRST = require('./mocks/stops.json')
5+
const PARK_AND_RIDE = require('./mocks/pr.json')
6+
7+
const app = express()
8+
// Mock exactly the requests the test link will create requests to
9+
app.get('/otp/routers/default/plan', (req, res) => {
10+
res.send(PLAN_REALTIME)
11+
})
12+
app.get('/otp/routers/default/index/stops', (req, res) => {
13+
res.send(STOPS_FIRST)
14+
})
15+
app.get('/otp/routers/default/park_and_ride', (req, res) => {
16+
res.send(PARK_AND_RIDE)
17+
})
18+
module.exports.mockServer = app

a11y/mocks/plan.json

+1
Large diffs are not rendered by default.

a11y/mocks/pr.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"name":"P+R Eastwood Park & Ride","x":-95.33571105000001,"y":29.72878155},{"name":"P+R Magnolia Park & Ride","x":-95.30348190000001,"y":29.73431275},{"name":"P+R Eastex Park & Ride","x":-95.29903390000001,"y":29.927564550000003},{"name":"P+R Fifth Ward Park & Ride","x":-95.31554165,"y":29.776087200000003},{"name":"P+R Maxey Road Park & Ride","x":-95.2173539,"y":29.784856000000005},{"name":"P+R Kashmere Park & Ride","x":-95.32818135000001,"y":29.813871050000003},{"name":"P+R Cypress Park & Ride","x":-95.68538430000001,"y":29.964691300000002},{"name":"P+R Kuykendahl Park & Ride","x":-95.4254829,"y":29.973071000000004},{"name":"P+R Fuqua Park & Ride","x":-95.2169867,"y":29.60929745},{"name":"P+R Hilcroft Park and Ride","x":-95.4953988,"y":29.722077400000003},{"name":"P+R Kingsland Park & Ride","x":-95.74403235,"y":29.773826550000003},{"name":"P+R Bay Area Park and Ride","x":-95.1242302,"y":29.553318850000004},{"name":"P+R Spring Park and Ride","x":-95.41705290000002,"y":30.0219978},{"name":"P+R Grand Parkway Park & Ride","x":-95.775868,"y":29.787324400000003},{"name":"P+R Missouri City Park and Ride","x":-95.50684269999999,"y":29.62220885},{"name":"P+R Addicks Park and Ride","x":-95.6378214,"y":29.7871035},{"name":"P+R South Point Park & Ride","x":-95.2090594,"y":29.613153700000005},{"name":"P+R West Loop Park & Ride","x":-95.4590115,"y":29.67969695},{"name":"P+R West Loop Park & Ride","x":-95.4575754,"y":29.678453400000002},{"name":"P+R West Loop Park & RIde","x":-95.45893530000001,"y":29.680618600000003},{"name":"P+R West Loop Park & Ride","x":-95.45812955,"y":29.67963985},{"name":"P+R METRO West Bellfort Park & Ride","x":-95.56057320000001,"y":29.6551495},{"name":"P+R Townsen Park and Ride","x":-95.2639671,"y":30.019417250000004},{"name":"P+R Townsen Park and Ride","x":-95.2640864,"y":30.018127850000003},{"name":"P+R","x":-95.5108057,"y":29.53132745},{"name":"P+R Baytown Park & Ride","x":-94.9865383,"y":29.79922535},{"name":"P+R Northline Transit Center Parking","x":-95.3763821,"y":29.8326085},{"name":"P+R Westwood Park and Ride","x":-95.54868185000001,"y":29.672184450000003},{"name":"P+R Northwest Transit Center Park & Ride","x":-95.4537731,"y":29.78344215},{"name":"P+R","x":-95.4542179,"y":29.7830552},{"name":"P+R Westwood Park and Ride","x":-95.54843235000001,"y":29.672169000000004},{"name":"P+R Monroe Park and Ride","x":-95.25483990000001,"y":29.664887150000002},{"name":"P+R Kingsland Park & Ride","x":-95.7442418,"y":29.77444045},{"name":"P+R Hiram Clarke Park & Ride","x":-95.43183185000001,"y":29.614350900000005},{"name":"P+R Northwest Station Park & Ride","x":-95.59425515000001,"y":29.901728900000002},{"name":"P+R Seton Lake Park & Ride","x":-95.49940015,"y":29.927510500000004},{"name":"P+R West Little York Park & Ride","x":-95.55293265,"y":29.86934865},{"name":"P+R Kingwood Park and Ride","x":-95.1830843,"y":30.054689000000003},{"name":"P+R El Dorado Park & Ride","x":-95.1573459,"y":29.54807475},{"name":"P+R Mission Bend Transit Center","x":-95.6294207,"y":29.710549450000002},{"name":"P+R Gessner Park and Ride","x":-95.53812250000001,"y":29.720586000000004},{"name":"P+R Westchase Park and Ride","x":-95.5627122,"y":29.716845600000003},{"name":"P+R Southeast Park & Ride","x":-95.35762960000001,"y":29.7019081},{"name":"P+R Tidwell Park & Ride","x":-95.33732285,"y":29.852356300000004},{"name":"P+R North Shepherd Park & Ride","x":-95.41379545000001,"y":29.876002500000002},{"name":"P+R North Shepherd Park & Ride","x":-95.41460395000001,"y":29.877670450000004},{"name":"P+R North Shepherd Park & Ride","x":-95.41447335000001,"y":29.87597495},{"name":"P+R Fannin South Park & Ride","x":-95.40149275000002,"y":29.67472505},{"name":"P+R Conroe Park & Ride","x":-95.469595,"y":30.309220600000003}]

a11y/mocks/stops.json

+1
Large diffs are not rendered by default.

a11y/test-config.yml

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
branding: a11y-test
2+
title: a11y test environment
3+
homeTimezone: America/Chicago
4+
5+
# Default OTP API
6+
api:
7+
host: http://localhost:9999
8+
# host: http://localhost:8001 # For testing against a local OTP instance
9+
path: /otp/routers/default
10+
11+
# Enabled multimodal routing types (e.g. interary, profile)
12+
routingTypes:
13+
- key: ITINERARY
14+
text: Exact Time
15+
16+
# Show/hide elevation profile chart for walk/bike legs
17+
# elevationProfile: true
18+
19+
# Map config
20+
map:
21+
# Enabled map views
22+
views:
23+
- type: DEFAULT
24+
text: Map View
25+
# Delete/uncomment the following block to disable the stylized map
26+
#- type: STYLIZED
27+
# text: Network View
28+
29+
# Default map center
30+
initLat: 29.7604
31+
initLon: -95.3698
32+
initZoom: 12
33+
34+
overlays:
35+
- type: bike-rental
36+
name: BCycle Locations
37+
modes:
38+
- BICYCLE_RENT
39+
companies:
40+
- BCYCLE
41+
mapSymbols:
42+
- maxZoom: 12
43+
minZoom: 0
44+
type: circle
45+
pixels: 3
46+
fillColor: "#FF2E28"
47+
dockStrokeColor: "#000000"
48+
- maxZoom: 15
49+
minZoom: 13
50+
type: circle
51+
pixels: 5
52+
fillColor: "#FF2E28"
53+
dockStrokeColor: "#000000"
54+
- maxZoom: 20
55+
minZoom: 16
56+
fillColor: "#FF2E28"
57+
dockStrokeColor: "#000000"
58+
type: hubAndFloatingBike
59+
60+
- type: park-and-ride
61+
name: Park & Ride Locations
62+
maxTransitDistance: 1000
63+
modes:
64+
- CAR_PARK
65+
66+
- type: stops
67+
name: Transit Stops
68+
visible: true
69+
70+
# A list of private transportation operators. These are either companies that
71+
# provide rental vehicles or transportation network companies. Companies that
72+
# have multiple modes of transport should have all modes listed as a string with
73+
# commas. For example: BICYCLE_RENT,MICROMOBILITY_RENT.
74+
companies:
75+
- id: BCYCLE
76+
label: BCycle
77+
modes: BICYCLE_RENT
78+
79+
# Mode selector configuration
80+
modes:
81+
transitModes:
82+
- mode: BUS
83+
label: Bus
84+
- mode: TRAM
85+
label: METRORail
86+
87+
# access to transit modes. These options are all combined with the above
88+
# transit modes in the request to OTP
89+
accessModes:
90+
- mode: BICYCLE
91+
label: Transit + Personal bike
92+
- mode: BICYCLE_RENT
93+
label: Transit + BCycle
94+
company: BCYCLE
95+
- mode: CAR_PARK
96+
label: Park & Ride
97+
# which exclusive modes to show. This involves using a single mode and no
98+
# transit for the duration of the trip. Further configurations of
99+
# `bicycleModes`, `micromobilityModes` and/or `carModes` are needed as
100+
# desired, but no extra configuration is needed for a WALK exclusive mode.
101+
exclusiveModes:
102+
- WALK
103+
- BICYCLE
104+
105+
bicycleModes:
106+
- mode: BICYCLE
107+
label: Own Bike
108+
iconWidth: 18
109+
- mode: BICYCLE_RENT
110+
label: BCycle
111+
iconWidth: 36
112+
113+
dateTime:
114+
timeFormat: h:mm a
115+
dateFormat: MM/DD/YYYY
116+
longDateFormat: MMMM D, YYYY

index-for-puppeteer.html

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
6+
<link rel="stylesheet" href="dist/index.css">
7+
<link rel="stylesheet" href="example.css">
8+
<title>OTP R.R.</title>
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
13+
<script src="dist/index.js"></script>
14+
</body>
15+
</html>

index.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<!DOCTYPE html>
2-
<html>
2+
<!-- FIXME: insert correct i18n code here -->
3+
<!-- Required to meet a11y requirements -->
4+
<html lang="en">
35
<head>
46
<meta charset="utf-8">
57
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">

lib/components/admin/styled.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export const Text = styled.span`
145145

146146
export const Val = styled.span`
147147
:empty:before {
148-
color: grey;
148+
color: #685C5C;
149149
content: 'N/A';
150150
}
151151
`

lib/components/app/app-frame.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import NotFound from './not-found'
99
* content and an optional sub-navigation component can be inserted.
1010
*/
1111
const AppFrame = ({ children, SubNav }) => (
12-
<div className='otp'>
12+
<div className='otp' id='otp' role='main'>
1313
{/* TODO: Do mobile view. */}
1414
<DesktopNav />
1515
{SubNav && <SubNav />}

lib/components/app/app-menu.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class AppMenu extends Component {
5353
} = this.props
5454

5555
return (
56-
<div className='app-menu'>
56+
<div aria-label='app-menu' className='app-menu' role='navigation'>
5757
<DropdownButton
5858
aria-label='Application Menu'
5959
className='app-menu-button'

lib/components/narrative/narrative.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@
195195

196196
.otp .tabbed-itineraries .tab-button .details {
197197
font-size: 12px;
198-
color: gray;
198+
color: #685c5c;
199199
}
200200

201201
.otp .tabbed-itineraries .tab-button:hover .title {

lib/components/viewers/realtime-status-label.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ const STATUS = {
3434
label: 'early'
3535
},
3636
LATE: {
37-
color: '#d9534f',
37+
color: '#D92923',
3838
label: 'late'
3939
},
4040
ON_TIME: {
41-
color: '#5cb85c',
41+
color: '#028602',
4242
label: 'on time'
4343
},
4444
SCHEDULED: {

lib/components/viewers/viewers.css

+4-4
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138
.otp .stop-viewer .trip-table .header {
139139
display: table-row;
140140
font-size: 11px;
141-
color: gray;
141+
color: #685c5c;
142142
text-align: center;
143143
}
144144

@@ -195,14 +195,14 @@
195195

196196
.otp .trip-viewer .strip-map-highlight {
197197
position: absolute;
198-
background-color: cyan;
198+
background-color: #13c1c1;
199199
width: 20px;
200200
height: 30px;
201201
}
202202

203203
.otp .trip-viewer .strip-map-highlight-first {
204204
position: absolute;
205-
background-color: cyan;
205+
background-color: #13c1c1;
206206
top: 2px;
207207
width: 20px;
208208
height: 28px;
@@ -212,7 +212,7 @@
212212

213213
.otp .trip-viewer .strip-map-highlight-last {
214214
position: absolute;
215-
background-color: cyan;
215+
background-color: #13c1c1;
216216
top: 0px;
217217
width: 20px;
218218
height: 28px;

0 commit comments

Comments
 (0)