Pomoc z rysunkami ekranow, kafelki na cala szerokosc, kalkulator na danych GUS i Eurostatu
Deploy Polska Lokalnie / deploy (push) Successful in 22s
Deploy Polska Lokalnie / deploy (push) Successful in 22s
Pomoc i kontakt: - kanaly kontaktu wypelniaja szerokosc panelu (auto-fit zamiast czterech kolumn) - FAQ podzielone na cztery moduly, 21 pytan, ze schematycznymi rysunkami ekranow (inline SVG w helpIllustrations.tsx - nie zrzuty, wiec nie dezaktualizuja sie) - menu konta nie przykleja sie juz nad trescia na telefonie i tablecie Kafelki wyboru: - kolumna nie schodzi ponizej czytelnej szerokosci, podpisy lamia sie miedzy slowami zamiast po literach (bylo widoczne na monitorach ok. 1440 px) Kalkulator zdolnosci kredytowej: - rodzaj oprocentowania decyduje o buforze: 2,5 p.p. przy stalym, 5 p.p. przy zmiennym (wczesniej zawsze 2,5 p.p., co zawyzalo zdolnosc przy zmiennej stopie o ok. 100 tys. zl) - nowy modul market: przecietne wynagrodzenie z GUS BDL i stopa rynku pienieznego 3M z Eurostatu, odswiezane co tydzien, publiczny GET /api/market-indicators - prog wyzszego DStI i podpowiadane oprocentowanie licza sie z tych wskaznikow, z podpisem zrodla i okresu; brak danych = wartosci zapasowe Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -853,3 +853,163 @@ test.describe('Ustawienia powiadomien', () => {
|
||||
await page.waitForTimeout(700);
|
||||
});
|
||||
});
|
||||
|
||||
// Pomoc i kontakt: kanaly kontaktu wypelniaja szerokosc panelu, a FAQ jest podzielone
|
||||
// na moduly serwisu i pokazuje schematyczne rysunki ekranow.
|
||||
test.describe('Pomoc i kontakt', () => {
|
||||
test('kanaly kontaktu zajmuja cala szerokosc panelu', async ({ page }) => {
|
||||
await login(page);
|
||||
await goto(page, ROUTES.accountHelpContact);
|
||||
|
||||
const grid = await page.locator('.help-contact-channel-grid').boundingBox();
|
||||
const cards = await page.locator('.help-contact-channel-card').all();
|
||||
expect(cards.length).toBeGreaterThan(0);
|
||||
const last = await cards[cards.length - 1].boundingBox();
|
||||
// Ostatnia karta konczy sie na prawej krawedzi siatki - zadnej pustej kolumny.
|
||||
expect(grid!.x + grid!.width - (last!.x + last!.width)).toBeLessThanOrEqual(2);
|
||||
});
|
||||
|
||||
test('FAQ ma grupy modulow i rysunek w odpowiedzi', async ({ page }) => {
|
||||
await login(page);
|
||||
await goto(page, ROUTES.accountHelpContact);
|
||||
|
||||
const groups = page.locator('.help-faq-group');
|
||||
await expect(groups).toHaveCount(4);
|
||||
await expect(page.locator('.help-contact-faq-list details').first()).toBeVisible();
|
||||
|
||||
const item = page.locator('.help-contact-faq-list details').filter({ hasText: 'Jak dodać ogłoszenie?' }).first();
|
||||
await expect(item.locator('.help-figure')).toBeHidden();
|
||||
await item.locator('summary').click();
|
||||
await expect(item.locator('.help-figure svg')).toBeVisible();
|
||||
await expect(item.locator('figcaption')).not.toBeEmpty();
|
||||
});
|
||||
});
|
||||
|
||||
// Kafelki wyboru nie moga sciskac sie do kilkudziesieciu pikseli - wtedy podpisy lamaly sie
|
||||
// po literach ("Og rze wa nie po dlo go we"). Kolumna ma minimalna szerokosc, reszta idzie nizej.
|
||||
test.describe('Kafelki wyboru w kreatorze wyceny', () => {
|
||||
for (const width of [1920, 1600, 1440, 1366, 1280, 1100]) {
|
||||
test(`podpisy udogodnien mieszcza sie przy szerokosci ${width}`, async ({ page }) => {
|
||||
await page.setViewportSize({ width, height: 1000 });
|
||||
await goto(page, ROUTES.valuationCondition);
|
||||
|
||||
const labels = page.locator('.valuation-amenity-grid button span');
|
||||
await expect(labels.first()).toBeVisible();
|
||||
const measured = await labels.evaluateAll((nodes) => nodes.map((node) => {
|
||||
const rect = node.getBoundingClientRect();
|
||||
return { text: node.textContent ?? '', width: rect.width, height: rect.height };
|
||||
}));
|
||||
|
||||
for (const item of measured) {
|
||||
// Ponizej ~90 px nawet krotkie slowo zaczyna sie lamac w pionie.
|
||||
expect(item.width, `podpis "${item.text}"`).toBeGreaterThan(90);
|
||||
// Najdluzsza nazwa to dwa slowa - wiecej niz dwa wiersze znacza lamanie po literach.
|
||||
expect(item.height, `podpis "${item.text}"`).toBeLessThanOrEqual(34);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Ten sam problem co w kreatorze wyceny dotyczyl kafelkow mediow i udogodnien w formularzu
|
||||
// ogloszenia. Test przechodzi kreator do kroku "Dodatkowe informacje" i mierzy podpisy.
|
||||
test.describe('Kafelki wyboru w formularzu ogloszenia', () => {
|
||||
test('podpisy mediow i udogodnien lamia sie miedzy slowami', async ({ page }) => {
|
||||
// Formularz edycji wchodzi z kompletem danych, wiec kroki przepuszczaja walidacje.
|
||||
// Konto z gotowymi ogloszeniami: to samo, ktorego uzywaja testy ulubionych i wyszukiwan.
|
||||
await goto(page, ROUTES.login);
|
||||
await page.getByPlaceholder('Wpisz swój adres e-mail').fill('anna.kowalska@example.com');
|
||||
await page.getByPlaceholder('Wpisz swoje hasło').fill(ADMIN.password);
|
||||
await page.getByRole('button', { name: 'Zaloguj się', exact: true }).click();
|
||||
await page.waitForURL(/\/konto$/);
|
||||
|
||||
const token = await page.evaluate(() => window.localStorage.getItem('polskalokalnie-auth-token'));
|
||||
const own = await page.request.get('/api/listings/mine', { headers: { Authorization: `Bearer ${token}` } })
|
||||
.then((response) => response.json());
|
||||
expect(own.length, 'konto testowe musi mieć własne ogłoszenie').toBeGreaterThan(0);
|
||||
|
||||
await page.setViewportSize({ width: 1440, height: 1200 });
|
||||
await goto(page, `/edytuj-ogloszenie/${own[0].id}`);
|
||||
|
||||
// Baner zgody stoi nad paskiem akcji na dole kroku - bez zamkniecia przejmuje klikniecie.
|
||||
const consent = page.getByRole('button', { name: 'Tylko niezbędne' });
|
||||
if (await consent.count() > 0) {
|
||||
await consent.click();
|
||||
}
|
||||
// Formularz edycji dociaga dane ogloszenia - dopoki pola sa puste, walidacja nie puszcza dalej.
|
||||
await expect(page.locator('.add-steps-item.active strong')).toContainText('Szczegóły oferty');
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
// Trzy kroki dalej: Szczegoly -> Lokalizacja -> Zdjecia -> Dodatkowe informacje.
|
||||
for (const next of ['Lokalizacja', 'Zdjęcia', 'Dodatkowe informacje']) {
|
||||
await page.getByRole('button', { name: new RegExp('^Dalej') }).first().click();
|
||||
await expect(page.locator('.add-steps-item.active strong')).toContainText(next);
|
||||
}
|
||||
await expect(page.locator('.add-steps-item.active strong')).toContainText('Dodatkowe informacje');
|
||||
|
||||
const labels = page.locator('.extra-option-grid label span');
|
||||
await expect(labels.first()).toBeVisible();
|
||||
const measured = await labels.evaluateAll((nodes) => nodes.map((node) => ({
|
||||
text: node.textContent ?? '',
|
||||
height: node.getBoundingClientRect().height,
|
||||
})));
|
||||
expect(measured.length).toBeGreaterThan(5);
|
||||
for (const item of measured) {
|
||||
// Najdluzsze podpisy to trzy slowa - powyzej dwoch wierszy znaczy lamanie po literach.
|
||||
expect(item.height, `podpis "${item.text}"`).toBeLessThanOrEqual(34);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Kalkulator zdolnosci: bufor zalezy od rodzaju oprocentowania (Rekomendacja S), a prog wyzszego
|
||||
// DStI i podpowiadane oprocentowanie pochodza ze wskaznikow pobieranych z GUS i Eurostatu.
|
||||
test.describe('Kalkulator zdolnosci kredytowej', () => {
|
||||
test('wskazniki rynkowe sa dostepne publicznie', async ({ page }) => {
|
||||
const indicators = await page.request.get('/api/market-indicators').then((response) => response.json());
|
||||
const keys = indicators.map((item: { key: string }) => item.key);
|
||||
expect(keys).toContain('average-wage-gross');
|
||||
expect(keys).toContain('money-market-3m');
|
||||
for (const item of indicators) {
|
||||
expect(item.value, `wskaznik ${item.key}`).toBeGreaterThan(0);
|
||||
expect(item.asOf, `wskaznik ${item.key}`).toBeTruthy();
|
||||
expect(item.source, `wskaznik ${item.key}`).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
test('zmienne oprocentowanie daje nizsza zdolnosc niz stale', async ({ page }) => {
|
||||
await goto(page, ROUTES.creditCalculator);
|
||||
await page.locator('.credit-field').filter({ hasText: 'Twój dochód' }).locator('input').fill('9000');
|
||||
await page.waitForTimeout(400);
|
||||
|
||||
const maxLoan = async () => {
|
||||
const row = page.locator('.credit-details article, .credit-detail-row, .credit-detail')
|
||||
.filter({ hasText: 'Maksymalna kwota kredytu' }).first();
|
||||
const text = await row.innerText();
|
||||
return Number(text.replace(/[^\d]/g, ''));
|
||||
};
|
||||
|
||||
await page.getByRole('button', { name: 'Stałe', exact: true }).click();
|
||||
await page.waitForTimeout(400);
|
||||
const withFixed = await maxLoan();
|
||||
|
||||
await page.getByRole('button', { name: 'Zmienne', exact: true }).click();
|
||||
await page.waitForTimeout(400);
|
||||
const withVariable = await maxLoan();
|
||||
|
||||
expect(withFixed).toBeGreaterThan(0);
|
||||
// Wyzszy bufor przy zmiennej stopie musi obnizyc maksymalna kwote kredytu.
|
||||
expect(withVariable).toBeLessThan(withFixed);
|
||||
});
|
||||
|
||||
test('podpowiadane oprocentowanie i prog DStI podaja zrodlo', async ({ page }) => {
|
||||
await goto(page, ROUTES.creditCalculator);
|
||||
await page.waitForTimeout(800);
|
||||
|
||||
const rate = await page.locator('.credit-field').filter({ hasText: 'Oprocentowanie' }).locator('input').inputValue();
|
||||
expect(Number(rate.replace(',', '.'))).toBeGreaterThan(0);
|
||||
|
||||
const notes = (await page.locator('.credit-method-note').allInnerTexts()).join(' ');
|
||||
expect(notes).toContain('GUS');
|
||||
expect(notes).toContain('Eurostat');
|
||||
await expect(page.locator('.credit-details, .credit-detail-row, .credit-detail').first()).toContainText('próg');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user