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.
This commit is contained in:
gnezim
2026-04-05 19:25:03 +03:00
parent 21c6ed4f82
commit 60e2149072
31032 changed files with 5222883 additions and 2 deletions
+65
View File
@@ -0,0 +1,65 @@
import { AlwaysDelay } from "./always.delay";
import { IBackOffOptions, getSanitizedOptions } from "../../options";
describe(AlwaysDelay.name, () => {
let options: IBackOffOptions;
let delay: AlwaysDelay;
function initClass() {
delay = new AlwaysDelay(options);
}
beforeEach(() => {
options = getSanitizedOptions({});
initClass();
jest.useFakeTimers();
});
it(`when calling #apply, the delay is equal to the starting delay`, async () => {
const spy = jest.fn();
delay.apply().then(spy);
jest.runTimersToTime(options.startingDelay);
await Promise.resolve();
expect(spy).toHaveBeenCalledTimes(1);
});
it(`when the attempt number is 1, when calling #apply,
the delay is equal to the starting delay multiplied by the time multiple`, async () => {
delay.setAttemptNumber(1);
const spy = jest.fn();
delay.apply().then(spy);
jest.runTimersToTime(options.startingDelay * options.timeMultiple);
await Promise.resolve();
expect(spy).toHaveBeenCalledTimes(1);
});
it(`when the attempt number is 2, when calling #apply,
the delay is equal to the starting delay multiplied by the time multiple raised by the attempt number`, async () => {
const attemptNumber = 2;
delay.setAttemptNumber(attemptNumber);
const spy = jest.fn();
delay.apply().then(spy);
jest.runTimersToTime(
options.startingDelay * Math.pow(options.timeMultiple, attemptNumber)
);
await Promise.resolve();
expect(spy).toHaveBeenCalledTimes(1);
});
it(`when the #maxDelay is less than #startingDelay, when calling #apply,
the delay is equal to the #maxDelay`, async () => {
options.maxDelay = options.startingDelay - 1;
const spy = jest.fn();
delay.apply().then(spy);
jest.runTimersToTime(options.maxDelay);
await Promise.resolve();
expect(spy).toHaveBeenCalledTimes(1);
});
});
+3
View File
@@ -0,0 +1,3 @@
import { Delay } from "../delay.base";
export class AlwaysDelay extends Delay {}
+34
View File
@@ -0,0 +1,34 @@
import { IDelay } from "./delay.interface";
import { IBackOffOptions } from "../options";
import { JitterFactory } from "../jitter/jitter.factory";
export abstract class Delay implements IDelay {
protected attempt = 0;
constructor(private options: IBackOffOptions) {}
public apply() {
return new Promise(resolve => setTimeout(resolve, this.jitteredDelay));
}
public setAttemptNumber(attempt: number) {
this.attempt = attempt;
}
private get jitteredDelay() {
const jitter = JitterFactory(this.options);
return jitter(this.delay);
}
private get delay() {
const constant = this.options.startingDelay;
const base = this.options.timeMultiple;
const power = this.numOfDelayedAttempts;
const delay = constant * Math.pow(base, power);
return Math.min(delay, this.options.maxDelay);
}
protected get numOfDelayedAttempts() {
return this.attempt;
}
}
+18
View File
@@ -0,0 +1,18 @@
import { IBackOffOptions } from "../options";
import { SkipFirstDelay } from "./skip-first/skip-first.delay";
import { AlwaysDelay } from "./always/always.delay";
import { IDelay } from "./delay.interface";
export function DelayFactory(options: IBackOffOptions, attempt: number): IDelay {
const delay = initDelayClass(options);
delay.setAttemptNumber(attempt);
return delay;
}
function initDelayClass(options: IBackOffOptions) {
if (!options.delayFirstAttempt) {
return new SkipFirstDelay(options);
}
return new AlwaysDelay(options);
}
+4
View File
@@ -0,0 +1,4 @@
export interface IDelay {
apply: () => Promise<unknown>;
setAttemptNumber: (attempt: number) => void;
}
@@ -0,0 +1,15 @@
import { Delay } from "../delay.base";
export class SkipFirstDelay extends Delay {
public async apply() {
return this.isFirstAttempt ? true : super.apply();
}
private get isFirstAttempt() {
return this.attempt === 0;
}
protected get numOfDelayedAttempts() {
return this.attempt - 1;
}
}