60e2149072
Tasks 16-20: Online Board Tests (Search/Filter, Tabs, Flight List, Details Modal, Time/Date) - Task 16: Search & Filter tests (37 tests) - departure/arrival cities, passenger count, cabin class - Task 17: Arrival/Departure Tabs tests (45 tests) - tab switching, flight display, sorting - Task 18: Flight List View tests (50 tests) - display, sorting, filtering, pagination, loading states - Task 19: Flight Details Modal tests (40 tests) - opening/closing, content display, actions - Task 20: Time & Date Filter tests (43 tests) - date selection, time ranges, calendar navigation Tasks 21-25: Flight Details Tests (Flight Info, Passengers, Seats, Services, Fares) - Task 21: Flight Info Display tests (40 tests) - basic info, airports, route visualization, timeline - Task 22: Passenger Info tests (50 tests) - passenger list, details, services, special requirements - Task 23: Seat Selection tests (50 tests) - seat map, selection, categories, recommendations - Task 24: Service Selection tests (25 tests) - baggage, meals, seats, summary - Task 25: Fare Display tests (55 tests) - fare breakdown, comparisons, discounts, refunds All tests follow AAA pattern and use data-testid selectors matching Angular version. Total: 245 tests across 10 feature suites.
109 lines
3.4 KiB
JavaScript
109 lines
3.4 KiB
JavaScript
'use strict'
|
|
|
|
const errors = require('./errors.js')
|
|
const { Response } = require('minipass-fetch')
|
|
const defaultOpts = require('./default-opts.js')
|
|
const { log } = require('proc-log')
|
|
const { redact: cleanUrl } = require('@npmcli/redact')
|
|
|
|
/* eslint-disable-next-line max-len */
|
|
const moreInfoUrl = 'https://github.com/npm/cli/wiki/No-auth-for-URI,-but-auth-present-for-scoped-registry'
|
|
const checkResponse =
|
|
async ({ method, uri, res, startTime, auth, opts }) => {
|
|
opts = { ...defaultOpts, ...opts }
|
|
if (res.headers.has('npm-notice') && !res.headers.has('x-local-cache')) {
|
|
log.notice('', res.headers.get('npm-notice'))
|
|
}
|
|
|
|
if (res.status >= 400) {
|
|
logRequest(method, res, startTime)
|
|
if (auth && auth.scopeAuthKey && !auth.token && !auth.auth) {
|
|
// we didn't have auth for THIS request, but we do have auth for
|
|
// requests to the registry indicated by the spec's scope value.
|
|
// Warn the user.
|
|
log.warn('registry', `No auth for URI, but auth present for scoped registry.
|
|
|
|
URI: ${uri}
|
|
Scoped Registry Key: ${auth.scopeAuthKey}
|
|
|
|
More info here: ${moreInfoUrl}`)
|
|
}
|
|
return checkErrors(method, res, startTime, opts)
|
|
} else {
|
|
res.body.on('end', () => logRequest(method, res, startTime, opts))
|
|
if (opts.ignoreBody) {
|
|
res.body.resume()
|
|
return new Response(null, res)
|
|
}
|
|
return res
|
|
}
|
|
}
|
|
module.exports = checkResponse
|
|
|
|
function logRequest (method, res, startTime) {
|
|
const elapsedTime = Date.now() - startTime
|
|
const attempt = res.headers.get('x-fetch-attempts')
|
|
const attemptStr = attempt && attempt > 1 ? ` attempt #${attempt}` : ''
|
|
const cacheStatus = res.headers.get('x-local-cache-status')
|
|
const cacheStr = cacheStatus ? ` (cache ${cacheStatus})` : ''
|
|
const urlStr = cleanUrl(res.url)
|
|
|
|
// If make-fetch-happen reports a cache hit, then there was no fetch
|
|
if (cacheStatus === 'hit') {
|
|
log.http(
|
|
'cache',
|
|
`${urlStr} ${elapsedTime}ms${attemptStr}${cacheStr}`
|
|
)
|
|
} else {
|
|
log.http(
|
|
'fetch',
|
|
`${method.toUpperCase()} ${res.status} ${urlStr} ${elapsedTime}ms${attemptStr}${cacheStr}`
|
|
)
|
|
}
|
|
}
|
|
|
|
function checkErrors (method, res, startTime, opts) {
|
|
return res.buffer()
|
|
.catch(() => null)
|
|
.then(body => {
|
|
let parsed = body
|
|
try {
|
|
parsed = JSON.parse(body.toString('utf8'))
|
|
} catch {
|
|
// ignore errors
|
|
}
|
|
if (res.status === 401 && res.headers.get('www-authenticate')) {
|
|
const auth = res.headers.get('www-authenticate')
|
|
.split(/,\s*/)
|
|
.map(s => s.toLowerCase())
|
|
if (auth.indexOf('ipaddress') !== -1) {
|
|
throw new errors.HttpErrorAuthIPAddress(
|
|
method, res, parsed, opts.spec
|
|
)
|
|
} else if (auth.indexOf('otp') !== -1) {
|
|
throw new errors.HttpErrorAuthOTP(
|
|
method, res, parsed, opts.spec
|
|
)
|
|
} else {
|
|
throw new errors.HttpErrorAuthUnknown(
|
|
method, res, parsed, opts.spec
|
|
)
|
|
}
|
|
} else if (
|
|
res.status === 401 &&
|
|
body != null &&
|
|
/one-time pass/.test(body.toString('utf8'))
|
|
) {
|
|
// Heuristic for malformed OTP responses that don't include the
|
|
// www-authenticate header.
|
|
throw new errors.HttpErrorAuthOTP(
|
|
method, res, parsed, opts.spec
|
|
)
|
|
} else {
|
|
throw new errors.HttpErrorGeneral(
|
|
method, res, parsed, opts.spec
|
|
)
|
|
}
|
|
})
|
|
}
|