Files
actual/packages/desktop-client/e2e/budget.mobile.test.ts
Joel Jeremy Marquez 0504becaf5 [TypeScript] Convert test page models to TS (#4218)
* Dummy commit

* Delete js snapshots

* Move extended expect and test to fixtures

* Fix wrong commit

* Update VRT

* Dummy commit to run GH actions

* Convert test page models to TS

* Release notes

* Fix typecheck errors

* New page models to TS

* Fix typecheck error

* Fix page name

* Put awaits on getTableTotals

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-16 10:24:38 -08:00

412 lines
13 KiB
TypeScript

import { type Page } from '@playwright/test';
import * as monthUtils from 'loot-core/shared/months';
import { amountToCurrency, currencyToAmount } from 'loot-core/shared/util';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';
import { type MobileBudgetPage } from './page-models/mobile-budget-page';
import { MobileNavigation } from './page-models/mobile-navigation';
const copyLastMonthBudget = async (
budgetPage: MobileBudgetPage,
categoryName: string,
) => {
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
await budgetMenuModal.copyLastMonthBudget();
await budgetMenuModal.close();
};
const setTo3MonthAverage = async (
budgetPage: MobileBudgetPage,
categoryName: string,
) => {
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
await budgetMenuModal.setTo3MonthAverage();
await budgetMenuModal.close();
};
const setTo6MonthAverage = async (
budgetPage: MobileBudgetPage,
categoryName: string,
) => {
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
await budgetMenuModal.setTo6MonthAverage();
await budgetMenuModal.close();
};
const setToYearlyAverage = async (
budgetPage: MobileBudgetPage,
categoryName: string,
) => {
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
await budgetMenuModal.setToYearlyAverage();
await budgetMenuModal.close();
};
async function setBudgetAverage(
budgetPage: MobileBudgetPage,
categoryName: string,
numberOfMonths: number,
setBudgetAverageFn: (
budgetPage: MobileBudgetPage,
categoryName: string,
numberOfMonths: number,
) => Promise<void>,
) {
let totalSpent = 0;
for (let i = 0; i < numberOfMonths; i++) {
await budgetPage.goToPreviousMonth();
const spentButton = await budgetPage.getButtonForSpent(categoryName);
const spent = await spentButton.textContent();
if (!spent) {
throw new Error('Failed to get spent amount');
}
totalSpent += currencyToAmount(spent) ?? 0;
}
// Calculate average amount
const averageSpent = totalSpent / numberOfMonths;
// Go back to the current month
for (let i = 0; i < numberOfMonths; i++) {
await budgetPage.goToNextMonth();
}
await setBudgetAverageFn(budgetPage, categoryName, numberOfMonths);
return averageSpent;
}
const budgetTypes = ['Envelope', 'Tracking'] as const;
budgetTypes.forEach(budgetType => {
test.describe(`Mobile Budget [${budgetType}]`, () => {
let page: Page;
let navigation: MobileNavigation;
let configurationPage: ConfigurationPage;
let previousGlobalIsTesting: boolean;
test.beforeAll(() => {
// TODO: Hack, properly mock the currentMonth function
previousGlobalIsTesting = global.IS_TESTING;
global.IS_TESTING = true;
});
test.afterAll(() => {
// TODO: Hack, properly mock the currentMonth function
global.IS_TESTING = previousGlobalIsTesting;
});
test.beforeEach(async ({ browser }) => {
page = await browser.newPage();
navigation = new MobileNavigation(page);
configurationPage = new ConfigurationPage(page);
await page.setViewportSize({
width: 350,
height: 600,
});
await page.goto('/');
await configurationPage.createTestFile();
const settingsPage = await navigation.goToSettingsPage();
await settingsPage.useBudgetType(budgetType);
});
test.afterEach(async () => {
await page.close();
});
test('loads the budget page with budgeted amounts', async () => {
const budgetPage = await navigation.goToBudgetPage();
await expect(budgetPage.categoryNames).toHaveText([
'Food',
'Restaurants',
'Entertainment',
'Clothing',
'General',
'Gift',
'Medical',
'Savings',
'Cell',
'Internet',
'Mortgage',
'Water',
'Power',
'Starting Balances',
'Misc',
'Income',
]);
await expect(page).toMatchThemeScreenshots();
});
// Page Header Tests
test('checks that clicking the Actual logo in the page header opens the budget page menu', async () => {
const budgetPage = await navigation.goToBudgetPage();
await budgetPage.openBudgetPageMenu();
const budgetPageMenuModal = page.getByRole('dialog');
await expect(budgetPageMenuModal).toBeVisible();
await expect(page).toMatchThemeScreenshots();
});
test("checks that clicking the left arrow in the page header shows the previous month's budget", async () => {
const budgetPage = await navigation.goToBudgetPage();
const selectedMonth = await budgetPage.getSelectedMonth();
const displayMonth = monthUtils.format(
selectedMonth,
budgetPage.MONTH_HEADER_DATE_FORMAT,
);
await expect(budgetPage.heading).toHaveText(displayMonth);
const previousMonth = await budgetPage.goToPreviousMonth();
const previousDisplayMonth = monthUtils.format(
previousMonth,
budgetPage.MONTH_HEADER_DATE_FORMAT,
);
await expect(budgetPage.heading).toHaveText(previousDisplayMonth);
await expect(page).toMatchThemeScreenshots();
});
test('checks that clicking the month in the page header opens the month menu modal', async () => {
const budgetPage = await navigation.goToBudgetPage();
const selectedMonth = await budgetPage.getSelectedMonth();
await budgetPage.openMonthMenu();
const monthMenuModal = page.getByRole('dialog');
const monthMenuModalHeading = monthMenuModal.getByRole('heading');
const displayMonth = monthUtils.format(
selectedMonth,
budgetPage.MONTH_HEADER_DATE_FORMAT,
);
await expect(monthMenuModalHeading).toHaveText(displayMonth);
await expect(page).toMatchThemeScreenshots();
});
test("checks that clicking the right arrow in the page header shows the next month's budget", async () => {
const budgetPage = await navigation.goToBudgetPage();
const selectedMonth = await budgetPage.getSelectedMonth();
const displayMonth = monthUtils.format(
selectedMonth,
budgetPage.MONTH_HEADER_DATE_FORMAT,
);
await expect(budgetPage.heading).toHaveText(displayMonth);
const nextMonth = await budgetPage.goToNextMonth();
const nextDisplayMonth = monthUtils.format(
nextMonth,
budgetPage.MONTH_HEADER_DATE_FORMAT,
);
await expect(budgetPage.heading).toHaveText(nextDisplayMonth);
await expect(page).toMatchThemeScreenshots();
});
// Category / Category Group Menu Tests
test('checks that clicking the category group name opens the category group menu modal', async () => {
const budgetPage = await navigation.goToBudgetPage();
const categoryGroupName = await budgetPage.getCategoryGroupNameForRow(0);
await budgetPage.openCategoryGroupMenu(categoryGroupName);
const categoryMenuModalHeading = page
.getByRole('dialog')
.getByRole('heading');
await expect(categoryMenuModalHeading).toHaveText(categoryGroupName);
await expect(page).toMatchThemeScreenshots();
});
test('checks that clicking the category name opens the category menu modal', async () => {
const budgetPage = await navigation.goToBudgetPage();
const categoryName = await budgetPage.getCategoryNameForRow(0);
const categoryMenuModal = await budgetPage.openCategoryMenu(categoryName);
await expect(categoryMenuModal.heading).toHaveText(categoryName);
await expect(page).toMatchThemeScreenshots();
});
// Budgeted Cell Tests
test('checks that clicking the budgeted cell opens the budget menu modal', async () => {
const budgetPage = await navigation.goToBudgetPage();
const categoryName = await budgetPage.getCategoryNameForRow(0);
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
await expect(budgetMenuModal.heading).toHaveText(categoryName);
await expect(page).toMatchThemeScreenshots();
});
test('updates the budgeted amount', async () => {
const budgetPage = await navigation.goToBudgetPage();
const categoryName = await budgetPage.getCategoryNameForRow(0);
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
const budgetAmount = 123;
// Set to 123.00
await budgetMenuModal.setBudgetAmount(`${budgetAmount}00`);
const budgetedButton =
await budgetPage.getButtonForBudgeted(categoryName);
await expect(budgetedButton).toHaveText(amountToCurrency(budgetAmount));
await expect(page).toMatchThemeScreenshots();
});
test(`copies last month's budget`, async () => {
const budgetPage = await navigation.goToBudgetPage();
const categoryName = await budgetPage.getCategoryNameForRow(3);
const budgetedButton =
await budgetPage.getButtonForBudgeted(categoryName);
await budgetPage.goToPreviousMonth();
const lastMonthBudget = await budgetedButton.textContent();
if (!lastMonthBudget) {
throw new Error('Failed to get last month budget');
}
await budgetPage.goToNextMonth();
await copyLastMonthBudget(budgetPage, categoryName);
await expect(budgetedButton).toHaveText(lastMonthBudget);
await expect(page).toMatchThemeScreenshots();
});
(
[
[3, setTo3MonthAverage],
[6, setTo6MonthAverage],
[12, setToYearlyAverage],
] as const
).forEach(([numberOfMonths, setBudgetAverageFn]) => {
test(`set budget to ${numberOfMonths} month average`, async () => {
const budgetPage = await navigation.goToBudgetPage();
const categoryName = await budgetPage.getCategoryNameForRow(3);
const averageSpent = await setBudgetAverage(
budgetPage,
categoryName,
numberOfMonths,
setBudgetAverageFn,
);
const budgetedButton =
await budgetPage.getButtonForBudgeted(categoryName);
await expect(budgetedButton).toHaveText(
amountToCurrency(Math.abs(averageSpent)),
);
await expect(page).toMatchThemeScreenshots();
});
});
test(`applies budget template`, async () => {
const settingsPage = await navigation.goToSettingsPage();
await settingsPage.enableExperimentalFeature('Goal templates');
const budgetPage = await navigation.goToBudgetPage();
const categoryName = await budgetPage.getCategoryNameForRow(1);
const amountToTemplate = 123;
const categoryMenuModal = await budgetPage.openCategoryMenu(categoryName);
const editNotesModal = await categoryMenuModal.editNotes();
const templateNotes = `#template ${amountToTemplate}`;
await editNotesModal.updateNotes(templateNotes);
await editNotesModal.close();
const budgetedButton =
await budgetPage.getButtonForBudgeted(categoryName);
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
await budgetMenuModal.applyBudgetTemplate();
await budgetMenuModal.close();
await expect(budgetedButton).toHaveText(
amountToCurrency(amountToTemplate),
);
const notification = page.getByRole('alert').first();
await expect(notification).toContainText(templateNotes);
await expect(page).toMatchThemeScreenshots();
});
// Spent Cell Tests
test('checks that clicking spent cell redirects to the category transactions page', async () => {
const budgetPage = await navigation.goToBudgetPage();
const categoryName = await budgetPage.getCategoryNameForRow(0);
const accountPage = await budgetPage.openSpentPage(categoryName);
await expect(accountPage.heading).toContainText(categoryName);
await expect(accountPage.transactionList).toBeVisible();
await expect(page).toMatchThemeScreenshots();
});
// Balance Cell Tests
test('checks that clicking the balance cell opens the balance menu modal', async () => {
const budgetPage = await navigation.goToBudgetPage();
const categoryName = await budgetPage.getCategoryNameForRow(0);
const balanceMenuModal = await budgetPage.openBalanceMenu(categoryName);
await expect(balanceMenuModal.heading).toHaveText(categoryName);
await expect(page).toMatchThemeScreenshots();
});
if (budgetType === 'Envelope') {
test('checks that clicking the To Budget/Overbudgeted amount opens the budget summary menu modal', async () => {
const budgetPage = await navigation.goToBudgetPage();
const envelopeBudgetSummaryModal =
await budgetPage.openEnvelopeBudgetSummary();
await expect(envelopeBudgetSummaryModal.heading).toHaveText(
'Budget Summary',
);
await expect(page).toMatchThemeScreenshots();
});
}
if (budgetType === 'Tracking') {
test('checks that clicking the Saved/Projected Savings/Overspent amount opens the budget summary menu modal', async () => {
const budgetPage = await navigation.goToBudgetPage();
const trackingBudgetSummaryModal =
await budgetPage.openTrackingBudgetSummary();
await expect(trackingBudgetSummaryModal.heading).toHaveText(
'Budget Summary',
);
await expect(page).toMatchThemeScreenshots();
});
}
});
});