From 32cc86ec99775bd034676b53cd02bc4e79ca5be9 Mon Sep 17 00:00:00 2001 From: shall0pass <20625555+shall0pass@users.noreply.github.com> Date: Fri, 18 Aug 2023 09:25:29 -0500 Subject: [PATCH 01/15] Mobile: Don't show hidden categories (#1539) * don't show hidden categories on mobile * release note --- .../src/components/budget/MobileBudgetTable.js | 2 +- upcoming-release-notes/1539.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 upcoming-release-notes/1539.md diff --git a/packages/desktop-client/src/components/budget/MobileBudgetTable.js b/packages/desktop-client/src/components/budget/MobileBudgetTable.js index cdab6e96f63..c74999e3262 100644 --- a/packages/desktop-client/src/components/budget/MobileBudgetTable.js +++ b/packages/desktop-client/src/components/budget/MobileBudgetTable.js @@ -273,7 +273,7 @@ class BudgetCategory extends PureComponent { let budgeted = rolloverBudget.catBudgeted(category.id); let balance = rolloverBudget.catBalance(category.id); - let content = ( + let content = !category.hidden && ( (this.container = el)} style={[ diff --git a/upcoming-release-notes/1539.md b/upcoming-release-notes/1539.md new file mode 100644 index 00000000000..c8d91b557a7 --- /dev/null +++ b/upcoming-release-notes/1539.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [shall0pass] +--- + +Mobile: Don't show hidden categories From ac055dc2e07b02ce2652a2df9c7ae943b5e36212 Mon Sep 17 00:00:00 2001 From: shall0pass <20625555+shall0pass@users.noreply.github.com> Date: Fri, 18 Aug 2023 15:39:22 -0500 Subject: [PATCH 02/15] Mobile: To Budget inconsistency (#1540) * rearrangement * release note --- .../src/components/modals/BudgetSummary.js | 42 +++++++++++-------- upcoming-release-notes/1540.md | 6 +++ 2 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 upcoming-release-notes/1540.md diff --git a/packages/desktop-client/src/components/modals/BudgetSummary.js b/packages/desktop-client/src/components/modals/BudgetSummary.js index 4e78e8881d4..80d7c4fb459 100644 --- a/packages/desktop-client/src/components/modals/BudgetSummary.js +++ b/packages/desktop-client/src/components/modals/BudgetSummary.js @@ -13,9 +13,31 @@ import format from '../spreadsheet/format'; import NamespaceContext from '../spreadsheet/NamespaceContext'; import useSheetValue from '../spreadsheet/useSheetValue'; +function ToBudget({ toBudget }) { + let budgetAmount = useSheetValue(toBudget); + return ( + + + {budgetAmount < 0 ? 'Overbudget:' : 'To budget:'} + + + {format(budgetAmount, 'financial')} + + + ); +} + function BudgetSummary({ month, modalProps }) { const prevMonthName = monthUtils.format(monthUtils.prevMonth(month), 'MMM'); - const budgetAmount = useSheetValue(rolloverBudget.toBudget); return ( @@ -74,23 +96,7 @@ function BudgetSummary({ month, modalProps }) { - - - {budgetAmount < 0 ? 'Overbudget:' : 'To budget:'} - - - {format(budgetAmount, 'financial')} - - + Date: Sat, 19 Aug 2023 19:42:04 +0100 Subject: [PATCH 03/15] :white_check_mark: (e2e) adding mobile e2e tests (#1521) --- packages/desktop-client/e2e/mobile.test.js | 111 ++++++++++++++++++ .../e2e/page-models/mobile-account-page.js | 39 ++++++ .../e2e/page-models/mobile-accounts-page.js | 33 ++++++ .../e2e/page-models/mobile-budget-page.js | 9 ++ .../e2e/page-models/mobile-navigation.js | 30 +++++ .../mobile-transaction-entry-page.js | 15 +++ .../accounts/MobileAccountDetails.js | 27 +++-- .../src/components/accounts/MobileAccounts.js | 3 + .../components/budget/MobileBudgetTable.js | 4 +- .../src/components/common/Button.tsx | 2 +- .../src/components/common/ButtonLink.tsx | 1 + .../src/components/mobile/MobileForms.js | 2 + .../src/components/spreadsheet/CellValue.tsx | 4 +- .../transactions/MobileTransaction.js | 3 + upcoming-release-notes/1521.md | 6 + 15 files changed, 273 insertions(+), 16 deletions(-) create mode 100644 packages/desktop-client/e2e/mobile.test.js create mode 100644 packages/desktop-client/e2e/page-models/mobile-account-page.js create mode 100644 packages/desktop-client/e2e/page-models/mobile-accounts-page.js create mode 100644 packages/desktop-client/e2e/page-models/mobile-budget-page.js create mode 100644 packages/desktop-client/e2e/page-models/mobile-navigation.js create mode 100644 packages/desktop-client/e2e/page-models/mobile-transaction-entry-page.js create mode 100644 upcoming-release-notes/1521.md diff --git a/packages/desktop-client/e2e/mobile.test.js b/packages/desktop-client/e2e/mobile.test.js new file mode 100644 index 00000000000..d5f65b38ce5 --- /dev/null +++ b/packages/desktop-client/e2e/mobile.test.js @@ -0,0 +1,111 @@ +import { test, expect } from '@playwright/test'; + +import { ConfigurationPage } from './page-models/configuration-page'; +import { MobileNavigation } from './page-models/mobile-navigation'; + +test.describe('Mobile', () => { + let page; + let navigation; + let configurationPage; + + 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(); + }); + + 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', + ]); + }); + + test('opens the accounts page and asserts on balances', async () => { + const accountsPage = await navigation.goToAccountsPage(); + + const account = await accountsPage.getNthAccount(0); + + expect(account.name).toEqual('Ally Savings'); + expect(account.balance).toBeGreaterThan(0); + }); + + test('opens individual account page and checks that filtering is working', async () => { + const accountsPage = await navigation.goToAccountsPage(); + const accountPage = await accountsPage.openNthAccount(1); + + await expect(accountPage.heading).toHaveText('Bank of America'); + expect(await accountPage.getBalance()).toBeGreaterThan(0); + + await expect(accountPage.noTransactionsFoundError).not.toBeVisible(); + + await accountPage.searchByText('nothing should be found'); + await expect(accountPage.noTransactionsFoundError).toBeVisible(); + await expect(accountPage.transactions).toHaveCount(0); + + await accountPage.searchByText('Kroger'); + await expect(accountPage.transactions).not.toHaveCount(0); + }); + + test('creates a transaction', async () => { + const accountsPage = await navigation.goToAccountsPage(); + const accountPage = await accountsPage.openNthAccount(1); + const transactionEntryPage = await accountPage.clickCreateTransaction(); + + await expect(transactionEntryPage.header).toHaveText('New Transaction'); + + await transactionEntryPage.amountField.fill('12.34'); + await transactionEntryPage.fillField( + page.getByTestId('payee-field'), + 'Kroger', + ); + await transactionEntryPage.fillField( + page.getByTestId('category-field'), + 'Clothing', + ); + + await transactionEntryPage.add.click(); + + await expect(accountPage.transactions.nth(0)).toHaveText( + 'KrogerClothing-12.34', + ); + }); + + test('checks that settings page can be opened', async () => { + const settingsPage = await navigation.goToSettingsPage(); + + const downloadPromise = page.waitForEvent('download'); + + await settingsPage.exportData(); + + const download = await downloadPromise; + + expect(await download.suggestedFilename()).toMatch( + /^\d{4}-\d{2}-\d{2}-.*.zip$/, + ); + }); +}); diff --git a/packages/desktop-client/e2e/page-models/mobile-account-page.js b/packages/desktop-client/e2e/page-models/mobile-account-page.js new file mode 100644 index 00000000000..9e58489fb74 --- /dev/null +++ b/packages/desktop-client/e2e/page-models/mobile-account-page.js @@ -0,0 +1,39 @@ +import { MobileTransactionEntryPage } from './mobile-transaction-entry-page'; + +export class MobileAccountPage { + constructor(page) { + this.page = page; + + this.heading = page.getByRole('heading'); + this.balance = page.getByTestId('account-balance'); + this.noTransactionsFoundError = page.getByText('No transactions'); + this.searchBox = page.getByPlaceholder(/^Search/); + this.transactionList = page.getByLabel('transaction list'); + this.transactions = this.transactionList.getByRole('button'); + this.createTransactionButton = page.getByRole('button', { + name: 'Add Transaction', + }); + } + + /** + * Retrieve the balance of the account as a number + */ + async getBalance() { + return parseInt(await this.balance.textContent(), 10); + } + + /** + * Search by the given term + */ + async searchByText(term) { + await this.searchBox.fill(term); + } + + /** + * Go to transaction creation page + */ + async clickCreateTransaction() { + this.createTransactionButton.click(); + return new MobileTransactionEntryPage(this.page); + } +} diff --git a/packages/desktop-client/e2e/page-models/mobile-accounts-page.js b/packages/desktop-client/e2e/page-models/mobile-accounts-page.js new file mode 100644 index 00000000000..5c0b8c601c6 --- /dev/null +++ b/packages/desktop-client/e2e/page-models/mobile-accounts-page.js @@ -0,0 +1,33 @@ +import { MobileAccountPage } from './mobile-account-page'; + +export class MobileAccountsPage { + constructor(page) { + this.page = page; + + this.accounts = this.page.getByTestId('account'); + } + + /** + * Get the name and balance of the nth account + */ + async getNthAccount(idx) { + const accountRow = this.accounts.nth(idx); + + return { + name: await accountRow.getByTestId('account-name').textContent(), + balance: parseInt( + await accountRow.getByTestId('account-balance').textContent(), + 10, + ), + }; + } + + /** + * Click on the n-th account to open it up + */ + async openNthAccount(idx) { + await this.accounts.nth(idx).getByRole('button').click(); + + return new MobileAccountPage(this.page); + } +} diff --git a/packages/desktop-client/e2e/page-models/mobile-budget-page.js b/packages/desktop-client/e2e/page-models/mobile-budget-page.js new file mode 100644 index 00000000000..c5d843fd220 --- /dev/null +++ b/packages/desktop-client/e2e/page-models/mobile-budget-page.js @@ -0,0 +1,9 @@ +export class MobileBudgetPage { + constructor(page) { + this.page = page; + + this.categoryNames = page + .getByTestId('budget-groups') + .getByTestId('category-name'); + } +} diff --git a/packages/desktop-client/e2e/page-models/mobile-navigation.js b/packages/desktop-client/e2e/page-models/mobile-navigation.js new file mode 100644 index 00000000000..16d667af25d --- /dev/null +++ b/packages/desktop-client/e2e/page-models/mobile-navigation.js @@ -0,0 +1,30 @@ +import { MobileAccountsPage } from './mobile-accounts-page'; +import { MobileBudgetPage } from './mobile-budget-page'; +import { SettingsPage } from './settings-page'; + +export class MobileNavigation { + constructor(page) { + this.page = page; + } + + async goToBudgetPage() { + const link = this.page.getByRole('link', { name: 'Budget' }); + await link.click(); + + return new MobileBudgetPage(this.page); + } + + async goToAccountsPage() { + const link = this.page.getByRole('link', { name: 'Accounts' }); + await link.click(); + + return new MobileAccountsPage(this.page); + } + + async goToSettingsPage() { + const link = this.page.getByRole('link', { name: 'Settings' }); + await link.click(); + + return new SettingsPage(this.page); + } +} diff --git a/packages/desktop-client/e2e/page-models/mobile-transaction-entry-page.js b/packages/desktop-client/e2e/page-models/mobile-transaction-entry-page.js new file mode 100644 index 00000000000..3b5115f72fb --- /dev/null +++ b/packages/desktop-client/e2e/page-models/mobile-transaction-entry-page.js @@ -0,0 +1,15 @@ +export class MobileTransactionEntryPage { + constructor(page) { + this.page = page; + + this.header = page.getByRole('heading'); + this.amountField = page.getByTestId('amount-input'); + this.add = page.getByRole('button', { name: 'Add transaction' }); + } + + async fillField(fieldLocator, content) { + await fieldLocator.click(); + await this.page.locator('css=[role=combobox] input').fill(content); + await this.page.keyboard.press('Enter'); + } +} diff --git a/packages/desktop-client/src/components/accounts/MobileAccountDetails.js b/packages/desktop-client/src/components/accounts/MobileAccountDetails.js index 6def3b6bad7..1d3ac56fd77 100644 --- a/packages/desktop-client/src/components/accounts/MobileAccountDetails.js +++ b/packages/desktop-client/src/components/accounts/MobileAccountDetails.js @@ -5,7 +5,7 @@ import Add from '../../icons/v1/Add'; import CheveronLeft from '../../icons/v1/CheveronLeft'; import SearchAlternate from '../../icons/v2/SearchAlternate'; import { theme, styles } from '../../style'; -import Button from '../common/Button'; +import ButtonLink from '../common/ButtonLink'; import InputWithContent from '../common/InputWithContent'; import Label from '../common/Label'; import Text from '../common/Text'; @@ -126,21 +126,21 @@ export default function AccountDetails({ fontSize: 16, fontWeight: 500, }} + role="heading" > {account.name} - {/* - TODO: connect to an add transaction modal - Only left here but hidden for flex centering of the account name. - */} - - - + + + +