Files
flights_web_raw/node_modules/@npmcli/redact/lib/utils.js
T
gnezim 60e2149072 Add comprehensive e2e test suites for Tasks 16-25
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.
2026-04-05 19:25:03 +03:00

203 lines
5.6 KiB
JavaScript

const {
URL_MATCHER,
TYPE_URL,
TYPE_REGEX,
TYPE_PATH,
} = require('./matchers')
/**
* creates a string of asterisks,
* this forces a minimum asterisk for security purposes
*/
const asterisk = (length = 0) => {
length = typeof length === 'string' ? length.length : length
if (length < 8) {
return '*'.repeat(8)
}
return '*'.repeat(length)
}
/**
* escapes all special regex chars
* @see https://stackoverflow.com/a/9310752
* @see https://github.com/tc39/proposal-regex-escaping
*/
const escapeRegExp = (text) => {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, `\\$&`)
}
/**
* provieds a regex "or" of the url versions of a string
*/
const urlEncodeRegexGroup = (value) => {
const decoded = decodeURIComponent(value)
const encoded = encodeURIComponent(value)
const union = [...new Set([encoded, decoded, value])].map(escapeRegExp).join('|')
return union
}
/**
* a tagged template literal that returns a regex ensures all variables are excaped
*/
const urlEncodeRegexTag = (strings, ...values) => {
let pattern = ''
for (let i = 0; i < values.length; i++) {
pattern += strings[i] + `(${urlEncodeRegexGroup(values[i])})`
}
pattern += strings[strings.length - 1]
return new RegExp(pattern)
}
/**
* creates a matcher for redacting url hostname
*/
const redactUrlHostnameMatcher = ({ hostname, replacement } = {}) => ({
type: TYPE_URL,
predicate: ({ url }) => url.hostname === hostname,
pattern: ({ url }) => {
return urlEncodeRegexTag`(^${url.protocol}//${url.username}:.+@)?${url.hostname}`
},
replacement: `$1${replacement || asterisk()}`,
})
/**
* creates a matcher for redacting url search / query parameter values
*/
const redactUrlSearchParamsMatcher = ({ param, replacement } = {}) => ({
type: TYPE_URL,
predicate: ({ url }) => url.searchParams.has(param),
pattern: ({ url }) => urlEncodeRegexTag`(${param}=)${url.searchParams.get(param)}`,
replacement: `$1${replacement || asterisk()}`,
})
/** creates a matcher for redacting the url password */
const redactUrlPasswordMatcher = ({ replacement } = {}) => ({
type: TYPE_URL,
predicate: ({ url }) => url.password,
pattern: ({ url }) => urlEncodeRegexTag`(^${url.protocol}//${url.username}:)${url.password}`,
replacement: `$1${replacement || asterisk()}`,
})
const redactUrlReplacement = (...matchers) => (subValue) => {
try {
const url = new URL(subValue)
return redactMatchers(...matchers)(subValue, { url })
} catch (err) {
return subValue
}
}
/**
* creates a matcher / submatcher for urls, this function allows you to first
* collect all urls within a larger string and then pass those urls to a
* submatcher
*
* @example
* console.log("this will first match all urls, then pass those urls to the password patcher")
* redactMatchers(redactUrlMatcher(redactUrlPasswordMatcher()))
*
* @example
* console.log(
* "this will assume you are passing in a string that is a url, and will redact the password"
* )
* redactMatchers(redactUrlPasswordMatcher())
*
*/
const redactUrlMatcher = (...matchers) => {
return {
...URL_MATCHER,
replacement: redactUrlReplacement(...matchers),
}
}
const matcherFunctions = {
[TYPE_REGEX]: (matcher) => (value) => {
if (typeof value === 'string') {
value = value.replace(matcher.pattern, matcher.replacement)
}
return value
},
[TYPE_URL]: (matcher) => (value, ctx) => {
if (typeof value === 'string') {
try {
const url = ctx?.url || new URL(value)
const { predicate, pattern } = matcher
const predicateValue = predicate({ url })
if (predicateValue) {
value = value.replace(pattern({ url }), matcher.replacement)
}
} catch (_e) {
return value
}
}
return value
},
[TYPE_PATH]: (matcher) => (value, ctx) => {
const rawPath = ctx?.path
const path = rawPath.join('.').toLowerCase()
const { predicate, replacement } = matcher
const replace = typeof replacement === 'function' ? replacement : () => replacement
const shouldRun = predicate({ rawPath, path })
if (shouldRun) {
value = replace(value, { rawPath, path })
}
return value
},
}
/** converts a matcher to a function */
const redactMatcher = (matcher) => {
return matcherFunctions[matcher.type](matcher)
}
/** converts a series of matchers to a function */
const redactMatchers = (...matchers) => (value, ctx) => {
const flatMatchers = matchers.flat()
return flatMatchers.reduce((result, matcher) => {
const fn = (typeof matcher === 'function') ? matcher : redactMatcher(matcher)
return fn(result, ctx)
}, value)
}
/**
* replacement handler, keeping $1 (if it exists) and replacing the
* rest of the string with asterisks, maintaining string length
*/
const redactDynamicReplacement = () => (value, start) => {
if (typeof start === 'number') {
return asterisk(value)
}
return start + asterisk(value.substring(start.length).length)
}
/**
* replacement handler, keeping $1 (if it exists) and replacing the
* rest of the string with a fixed number of asterisks
*/
const redactFixedReplacement = (length) => (_value, start) => {
if (typeof start === 'number') {
return asterisk(length)
}
return start + asterisk(length)
}
const redactUrlPassword = (value, replacement) => {
return redactMatchers(redactUrlPasswordMatcher({ replacement }))(value)
}
module.exports = {
asterisk,
escapeRegExp,
urlEncodeRegexGroup,
urlEncodeRegexTag,
redactUrlHostnameMatcher,
redactUrlSearchParamsMatcher,
redactUrlPasswordMatcher,
redactUrlMatcher,
redactUrlReplacement,
redactDynamicReplacement,
redactFixedReplacement,
redactMatchers,
redactUrlPassword,
}