diff --git a/packages/desktop-client/e2e/mobile.test.js b/packages/desktop-client/e2e/mobile.test.js index 4f68619a60e..d7fdda7cc46 100644 --- a/packages/desktop-client/e2e/mobile.test.js +++ b/packages/desktop-client/e2e/mobile.test.js @@ -108,7 +108,7 @@ test.describe('Mobile', () => { test('creates a transaction from `/accounts/:id` page', async () => { const accountsPage = await navigation.goToAccountsPage(); - const accountPage = await accountsPage.openNthAccount(1); + const accountPage = await accountsPage.openNthAccount(2); const transactionEntryPage = await accountPage.clickCreateTransaction(); await expect(transactionEntryPage.header).toHaveText('New Transaction'); diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png index 8898769c0ba..8b6f637184d 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-1-chromium-linux.png index 7fcb3d43a44..6747d2985ad 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/page-models/rules-page.js b/packages/desktop-client/e2e/page-models/rules-page.js index 31926bcecbb..c1228bbd0e1 100644 --- a/packages/desktop-client/e2e/page-models/rules-page.js +++ b/packages/desktop-client/e2e/page-models/rules-page.js @@ -1,6 +1,7 @@ export class RulesPage { constructor(page) { this.page = page; + this.searchBox = page.getByPlaceholder('Filter rules...'); } /** @@ -22,19 +23,19 @@ export class RulesPage { * Retrieve the data for the nth-rule. * 0-based index */ - async getNthRule(index) { + getNthRule(index) { const row = this.page.getByTestId('table').getByTestId('row').nth(index); return { - conditions: await row - .getByTestId('conditions') - .evaluate(el => [...el.children].map(c => c.textContent)), - actions: await row - .getByTestId('actions') - .evaluate(el => [...el.children].map(c => c.textContent)), + conditions: row.getByTestId('conditions').locator(':scope > div'), + actions: row.getByTestId('actions').locator(':scope > div'), }; } + async searchFor(text) { + await this.searchBox.fill(text); + } + async _fillRuleFields(data) { if (data.conditionsOp) { await this.page diff --git a/packages/desktop-client/e2e/page-models/schedules-page.js b/packages/desktop-client/e2e/page-models/schedules-page.js index 052cd31cb6f..23b2fe26c1f 100644 --- a/packages/desktop-client/e2e/page-models/schedules-page.js +++ b/packages/desktop-client/e2e/page-models/schedules-page.js @@ -31,15 +31,15 @@ export class SchedulesPage { * Retrieve the data for the nth-schedule. * 0-based index */ - async getNthSchedule(index) { + getNthSchedule(index) { const row = this.getNthScheduleRow(index); return { - payee: await row.getByTestId('payee').textContent(), - account: await row.getByTestId('account').textContent(), - date: await row.getByTestId('date').textContent(), - status: await row.getByTestId('status').textContent(), - amount: await row.getByTestId('amount').textContent(), + payee: row.getByTestId('payee'), + account: row.getByTestId('account'), + date: row.getByTestId('date'), + status: row.getByTestId('status'), + amount: row.getByTestId('amount'), }; } diff --git a/packages/desktop-client/e2e/rules.test.js b/packages/desktop-client/e2e/rules.test.js index d379d6d3fef..bec8a475e8f 100644 --- a/packages/desktop-client/e2e/rules.test.js +++ b/packages/desktop-client/e2e/rules.test.js @@ -28,6 +28,7 @@ test.describe('Rules', () => { }); test('checks the page visuals', async () => { + await rulesPage.searchFor('Dominion'); await expect(page).toHaveScreenshot(screenshotConfig(page)); }); @@ -48,13 +49,13 @@ test.describe('Rules', () => { ], }); - expect(await rulesPage.getNthRule(0)).toMatchObject({ - conditions: ['payee is Fast Internet'], - actions: ['set category to General'], - }); + await rulesPage.searchFor('Fast Internet'); + const rule = rulesPage.getNthRule(0); + await expect(rule.conditions).toHaveText(['payee is Fast Internet']); + await expect(rule.actions).toHaveText(['set category to General']); await expect(page).toHaveScreenshot(screenshotConfig(page)); - const accountPage = await navigation.goToAccountPage('Bank of America'); + const accountPage = await navigation.goToAccountPage('HSBC'); await accountPage.createSingleTransaction({ payee: 'Fast Internet', diff --git a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png index b99cb9a5dbe..f64c072c972 100644 Binary files a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png index 5965ecaa59d..e74f3b7f1eb 100644 Binary files a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png and b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png index 0105e69eb51..6eef9213617 100644 Binary files a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png and b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/schedules.test.js b/packages/desktop-client/e2e/schedules.test.js index 683ddf9b2a7..16b0ca222b5 100644 --- a/packages/desktop-client/e2e/schedules.test.js +++ b/packages/desktop-client/e2e/schedules.test.js @@ -38,18 +38,15 @@ test.describe('Schedules', () => { amount: 25, }); - expect(await schedulesPage.getNthSchedule(0)).toMatchObject({ - payee: 'Home Depot', - account: 'HSBC', - amount: '~25.00', - status: 'Due', - }); + const schedule = schedulesPage.getNthSchedule(2); + await expect(schedule.payee).toHaveText('Home Depot'); + await expect(schedule.account).toHaveText('HSBC'); + await expect(schedule.amount).toHaveText('~25.00'); + await expect(schedule.status).toHaveText('Due'); await expect(page).toHaveScreenshot(screenshotConfig(page)); - await schedulesPage.postNthSchedule(0); - expect(await schedulesPage.getNthSchedule(0)).toMatchObject({ - status: 'Paid', - }); + await schedulesPage.postNthSchedule(2); + await expect(schedulesPage.getNthSchedule(2).status).toHaveText('Paid'); await expect(page).toHaveScreenshot(screenshotConfig(page)); // Go to transactions page @@ -62,26 +59,24 @@ test.describe('Schedules', () => { // go to rules page const rulesPage = await navigation.goToRulesPage(); - expect(await rulesPage.getNthRule(0)).toMatchObject({ - // actions: ['link schedule Home Depot (2023-02-28)'], - actions: [ - expect.stringMatching( - /^link schedule Home Depot \(\d{4}-\d{2}-\d{2}\)$/, - ), - ], - conditions: [ - 'payee is Home Depot', - 'and account is HSBC', - expect.stringMatching(/^and date is approx Every month on the/), - 'and amount is approx -25.00', - ], - }); + await rulesPage.searchFor('Home Depot'); + const rule = rulesPage.getNthRule(0); + await expect(rule.actions).toHaveText([ + 'link schedule Home Depot (2017-01-01)', + ]); + await expect(rule.conditions).toHaveText([ + 'payee is Home Depot', + 'and account is HSBC', + 'and date is approx Every month on the 1st', + 'and amount is approx -25.00', + ]); // Go back to schedules page await navigation.goToSchedulesPage(); - await schedulesPage.completeNthSchedule(0); - expect(await schedulesPage.getNthScheduleRow(0)).toHaveText( + await schedulesPage.completeNthSchedule(2); + await expect(schedulesPage.getNthScheduleRow(4)).toHaveText( 'Show completed schedules', ); + await expect(page).toHaveScreenshot(screenshotConfig(page)); }); }); diff --git a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-checks-the-page-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-checks-the-page-visuals-1-chromium-linux.png index 9397a30e057..39641565315 100644 Binary files a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-checks-the-page-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-checks-the-page-visuals-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-1-chromium-linux.png b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-1-chromium-linux.png index b1b23b44ac4..42ed28b1b78 100644 Binary files a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-1-chromium-linux.png and b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-2-chromium-linux.png b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-2-chromium-linux.png index bd6e38b4485..c061ae744bc 100644 Binary files a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-2-chromium-linux.png and b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-3-chromium-linux.png b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-3-chromium-linux.png new file mode 100644 index 00000000000..7aa16653376 Binary files /dev/null and b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-3-chromium-linux.png differ diff --git a/packages/desktop-client/src/components/ManageRules.js b/packages/desktop-client/src/components/ManageRules.js index 11c9e3737ad..ee5c4e9bd42 100644 --- a/packages/desktop-client/src/components/ManageRules.js +++ b/packages/desktop-client/src/components/ManageRules.js @@ -1,10 +1,4 @@ -import React, { - useState, - useEffect, - useRef, - useCallback, - useMemo, -} from 'react'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { pushModal } from 'loot-core/src/client/actions/modals'; @@ -116,7 +110,6 @@ function ManageRulesContent({ isModal, payeeId, setLoading }) { ); let selectedInst = useSelected('manage-rules', allRules, []); let [hoveredRule, setHoveredRule] = useState(null); - let tableRef = useRef(null); async function loadRules() { setLoading(true); @@ -283,7 +276,6 @@ function ManageRulesContent({ isModal, payeeId, setLoading }) { groupById(payees)); let getAccountsById = memoizeOne(accounts => groupById(accounts)); -function isPreviewId(id) { - return id.indexOf('preview/') !== -1; -} - function getDescriptionPretty(transaction, payee, transferAcct) { let { amount } = transaction; @@ -1043,7 +1040,9 @@ export class TransactionList extends Component { } sections.push({ - id: transaction.date, + id: `${isPreviewId(transaction.id) ? 'preview/' : ''}${ + transaction.date + }`, date: transaction.date, data: [], }); diff --git a/packages/desktop-client/src/util/versions.ts b/packages/desktop-client/src/util/versions.ts index 00dc03eab3d..d483aa1543b 100644 --- a/packages/desktop-client/src/util/versions.ts +++ b/packages/desktop-client/src/util/versions.ts @@ -1,3 +1,5 @@ +import * as Platform from 'loot-core/src/client/platform'; + function parseSemanticVersion(versionString): [number, number, number] { return versionString .replace('v', '') @@ -15,18 +17,32 @@ function cmpSemanticVersion( return x[0] - y[0] || x[1] - y[1] || x[2] - y[2]; } -export async function getLatestVersion(): Promise { - let response = await fetch( - 'https://api.github.com/repos/actualbudget/actual/tags', - ); - let json = await response.json(); - let tags = json.map(t => t.name).concat([`v${window.Actual.ACTUAL_VERSION}`]); - tags.sort(cmpSemanticVersion); +export async function getLatestVersion(): Promise { + if (Platform.isPlaywright) { + return Promise.resolve('v99.9.9'); + } + + try { + let response = await fetch( + 'https://api.github.com/repos/actualbudget/actual/tags', + ); + let json = await response.json(); + let tags = json + .map(t => t.name) + .concat([`v${window.Actual.ACTUAL_VERSION}`]); + tags.sort(cmpSemanticVersion); - return tags[tags.length - 1]; + return tags[tags.length - 1]; + } catch { + // Rate limit exceeded? Or perhaps Github is down? + return 'unknown'; + } } export async function getIsOutdated(latestVersion: string): Promise { let clientVersion = window.Actual.ACTUAL_VERSION; + if (latestVersion === 'unknown') { + return Promise.resolve(false); + } return cmpSemanticVersion(clientVersion, latestVersion) < 0; } diff --git a/packages/loot-core/src/mocks/budget.ts b/packages/loot-core/src/mocks/budget.ts index 0c9e241b997..7f53c8ac3f9 100644 --- a/packages/loot-core/src/mocks/budget.ts +++ b/packages/loot-core/src/mocks/budget.ts @@ -731,6 +731,116 @@ export async function createTestBudget(handlers) { await sheet.waitOnSpreadsheet(); + // Create some schedules + await runMutator(() => + batchMessages(async () => { + const account = accounts.find(acc => acc.name === 'Bank of America'); + + await runHandler(handlers['schedule/create'], { + schedule: { + name: 'Phone bills', + posts_transaction: false, + }, + conditions: [ + { + op: 'is', + field: 'payee', + value: payees.find(item => item.name === 'Dominion Power').id, + }, + { + op: 'is', + field: 'account', + value: account.id, + }, + { + op: 'is', + field: 'date', + value: { + start: monthUtils.currentDay(), + frequency: 'monthly', + patterns: [], + skipWeekend: false, + weekendSolveMode: 'after', + }, + }, + { op: 'isapprox', field: 'amount', value: -12000 }, + ], + }); + + await runHandler(handlers['schedule/create'], { + schedule: { + name: 'Internet bill', + posts_transaction: false, + }, + conditions: [ + { + op: 'is', + field: 'payee', + value: payees.find(item => item.name === 'Fast Internet').id, + }, + { + op: 'is', + field: 'account', + value: account.id, + }, + { + op: 'is', + field: 'date', + value: monthUtils.subDays(monthUtils.currentDay(), 1), + }, + { op: 'isapprox', field: 'amount', value: -14000 }, + ], + }); + + await runHandler(handlers['schedule/create'], { + schedule: { + name: 'Wedding', + posts_transaction: false, + }, + conditions: [ + { + op: 'is', + field: 'date', + value: { + start: monthUtils.subDays(monthUtils.currentDay(), 3), + frequency: 'monthly', + patterns: [], + skipWeekend: false, + weekendSolveMode: 'after', + }, + }, + { op: 'is', field: 'amount', value: -2700000 }, + ], + }); + + await runHandler(handlers['schedule/create'], { + schedule: { + name: 'Utilities', + posts_transaction: false, + }, + conditions: [ + { + op: 'is', + field: 'account', + value: account.id, + }, + { + op: 'is', + field: 'date', + value: { + start: monthUtils.addDays(monthUtils.currentDay(), 1), + frequency: 'monthly', + patterns: [], + skipWeekend: false, + weekendSolveMode: 'after', + }, + }, + { op: 'is', field: 'amount', value: -190000 }, + ], + }); + }), + ); + // Create a budget await createBudget(accounts, payees, allGroups); } diff --git a/packages/loot-core/src/server/schedules/app.ts b/packages/loot-core/src/server/schedules/app.ts index 633e9898eaa..a371d459837 100644 --- a/packages/loot-core/src/server/schedules/app.ts +++ b/packages/loot-core/src/server/schedules/app.ts @@ -212,7 +212,7 @@ export async function createSchedule({ schedule = null, conditions = [], } = {}) { - let scheduleId = (schedule && schedule.id) || uuidv4(); + let scheduleId = schedule?.id || uuidv4(); let { date: dateCond } = extractScheduleConds(conditions); if (dateCond == null) { @@ -235,8 +235,7 @@ export async function createSchedule({ } // Create the rule here based on the info - let ruleId; - ruleId = await insertRule({ + let ruleId = await insertRule({ stage: null, conditionsOp: 'and', conditions, diff --git a/packages/loot-core/src/server/schedules/types/handlers.ts b/packages/loot-core/src/server/schedules/types/handlers.ts index 0992b858aee..f2a00618bfd 100644 --- a/packages/loot-core/src/server/schedules/types/handlers.ts +++ b/packages/loot-core/src/server/schedules/types/handlers.ts @@ -1,6 +1,10 @@ export interface SchedulesHandlers { 'schedule/create': (arg: { - schedule: unknown; + schedule: { + id?: string; + name?: string; + post_transaction?: boolean; + }; conditions: unknown[]; }) => Promise; diff --git a/upcoming-release-notes/1672.md b/upcoming-release-notes/1672.md new file mode 100644 index 00000000000..c34fadd4fbf --- /dev/null +++ b/upcoming-release-notes/1672.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MatissJanis] +--- + +Added mock schedules to the test budget to improve reliability and testing experience