diff --git a/packages/desktop-client/e2e/budget.test.js b/packages/desktop-client/e2e/budget.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..95da06367e976013157c40d731d43d2d5a72a859
--- /dev/null
+++ b/packages/desktop-client/e2e/budget.test.js
@@ -0,0 +1,54 @@
+import { test, expect } from '@playwright/test';
+
+import { ConfigurationPage } from './page-models/configuration-page';
+import { Navigation } from './page-models/navigation';
+
+test.describe('Budget', () => {
+  let page;
+  let navigation;
+  let configurationPage;
+  let budgetPage;
+
+  test.beforeAll(async ({ browser }) => {
+    page = await browser.newPage();
+    navigation = new Navigation(page);
+    configurationPage = new ConfigurationPage(page);
+
+    await page.goto('/');
+    budgetPage = await configurationPage.createTestFile();
+  });
+
+  test.afterAll(async () => {
+    await page.close();
+  });
+
+  test('renders the summary information: available funds, overspent, budgeted and for next month', async () => {
+    const summary = budgetPage.budgetSummary.first();
+
+    await expect(summary.getByText('Available Funds')).toBeVisible();
+    await expect(summary.getByText(/^Overspent in /)).toBeVisible();
+    await expect(summary.getByText('Budgeted')).toBeVisible();
+    await expect(summary.getByText('For Next Month')).toBeVisible();
+  });
+
+  test('transfer funds to another category', async () => {
+    const currentFundsA = await budgetPage.getBalanceForRow(1);
+    const currentFundsB = await budgetPage.getBalanceForRow(2);
+
+    await budgetPage.transferAllBalance(1, 2);
+    await page.waitForTimeout(1000);
+
+    expect(await budgetPage.getBalanceForRow(2)).toEqual(
+      currentFundsA + currentFundsB,
+    );
+  });
+
+  test('budget table is rendered', async () => {
+    await expect(budgetPage.budgetTable).toBeVisible();
+    expect(await budgetPage.getTableTotals()).toEqual({
+      budgeted: expect.any(Number),
+      spent: expect.any(Number),
+      balance: expect.any(Number),
+    });
+  });
+});
diff --git a/packages/desktop-client/e2e/data/actual-demo-budget.zip b/packages/desktop-client/e2e/data/actual-demo-budget.zip
new file mode 100644
index 0000000000000000000000000000000000000000..9a35f3f0ae90d9f4f8538e80279ed4d84e0c9955
Binary files /dev/null and b/packages/desktop-client/e2e/data/actual-demo-budget.zip differ
diff --git a/packages/desktop-client/e2e/data/ynab4-demo-budget.zip b/packages/desktop-client/e2e/data/ynab4-demo-budget.zip
new file mode 100644
index 0000000000000000000000000000000000000000..13db0b90e0bfaf2a4da82a72962dc5651c3da842
Binary files /dev/null and b/packages/desktop-client/e2e/data/ynab4-demo-budget.zip differ
diff --git a/packages/desktop-client/e2e/onboarding.test.js b/packages/desktop-client/e2e/onboarding.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..3295fd81a6676e163354c8b8b248fc35c8dd3621
--- /dev/null
+++ b/packages/desktop-client/e2e/onboarding.test.js
@@ -0,0 +1,71 @@
+import path from 'path';
+import { test, expect } from '@playwright/test';
+
+import { AccountPage } from './page-models/account-page';
+import { ConfigurationPage } from './page-models/configuration-page';
+import { Navigation } from './page-models/navigation';
+
+test.describe('Onboarding', () => {
+  let page;
+  let navigation;
+  let configurationPage;
+
+  test.beforeEach(async ({ browser }) => {
+    page = await browser.newPage();
+    navigation = new Navigation(page);
+    configurationPage = new ConfigurationPage(page);
+
+    await page.goto('/');
+  });
+
+  test.afterEach(async () => {
+    await page.close();
+  });
+
+  test('creates a new budget file by importing YNAB4 budget', async () => {
+    await configurationPage.clickOnNoServer();
+    const budgetPage = await configurationPage.importBudget(
+      'YNAB4',
+      path.resolve(__dirname, 'data/ynab4-demo-budget.zip'),
+    );
+
+    await expect(budgetPage.budgetTable).toBeVisible();
+
+    const accountPage = await navigation.goToAccountPage(
+      'Account1 with Starting Balance',
+    );
+    await expect(accountPage.accountBalance).toHaveText('-400.00');
+
+    await navigation.goToAccountPage('Account2 no Starting Balance');
+    await expect(accountPage.accountBalance).toHaveText('2,607.00');
+  });
+
+  // TODO: implement this test once we have an example nYNAB file
+  // test('creates a new budget file by importing nYNAB budget');
+
+  test('creates a new budget file by importing Actual budget', async () => {
+    await configurationPage.clickOnNoServer();
+    const budgetPage = await configurationPage.importBudget(
+      'Actual',
+      path.resolve(__dirname, 'data/actual-demo-budget.zip'),
+    );
+
+    await expect(budgetPage.budgetTable).toBeVisible();
+
+    const accountPage = await navigation.goToAccountPage('Ally Savings');
+    await expect(accountPage.accountBalance).toHaveText('1,772.80');
+
+    await navigation.goToAccountPage('Roth IRA');
+    await expect(accountPage.accountBalance).toHaveText('2,745.81');
+  });
+
+  test('creates a new empty budget file', async () => {
+    await configurationPage.clickOnNoServer();
+    await configurationPage.startFresh();
+
+    const accountPage = new AccountPage(page);
+    await expect(accountPage.accountName).toBeVisible();
+    await expect(accountPage.accountName).toHaveText('All Accounts');
+    await expect(accountPage.accountBalance).toHaveText('0.00');
+  });
+});
diff --git a/packages/desktop-client/e2e/page-models/account-page.js b/packages/desktop-client/e2e/page-models/account-page.js
index fac86780db30cf484a7ea4924073ae1f11da0dce..37398971a19c3858641a055c9c78753da9e3aec5 100644
--- a/packages/desktop-client/e2e/page-models/account-page.js
+++ b/packages/desktop-client/e2e/page-models/account-page.js
@@ -5,6 +5,7 @@ export class AccountPage {
     this.page = page;
 
     this.accountName = this.page.getByTestId('account-name');
+    this.accountBalance = this.page.getByTestId('account-balance');
     this.addNewTransactionButton = this.page.getByRole('button', {
       name: 'Add New',
     });
diff --git a/packages/desktop-client/e2e/page-models/budget-page.js b/packages/desktop-client/e2e/page-models/budget-page.js
new file mode 100644
index 0000000000000000000000000000000000000000..dfbe2bfb8225cce37b76b6cdf49e579f51bf4b9e
--- /dev/null
+++ b/packages/desktop-client/e2e/page-models/budget-page.js
@@ -0,0 +1,72 @@
+export class BudgetPage {
+  constructor(page) {
+    this.page = page;
+
+    this.budgetSummary = page.getByTestId('budget-summary');
+    this.budgetTable = page.getByTestId('budget-table');
+    this.budgetTableTotals = this.budgetTable.getByTestId('budget-totals');
+  }
+
+  async getTableTotals() {
+    return {
+      budgeted: parseInt(
+        await this.budgetTableTotals
+          .getByTestId(/total-budgeted$/)
+          .textContent(),
+        10,
+      ),
+      spent: parseInt(
+        await this.budgetTableTotals.getByTestId(/total-spent$/).textContent(),
+        10,
+      ),
+      balance: parseInt(
+        await this.budgetTableTotals
+          .getByTestId(/total-leftover$/)
+          .textContent(),
+        10,
+      ),
+    };
+  }
+
+  async showMoreMonths() {
+    await this.page.getByTestId('calendar-icon').first().click();
+  }
+
+  async getBalanceForRow(idx) {
+    return Math.round(
+      parseFloat(
+        await this.budgetTable
+          .getByTestId('row')
+          .nth(idx)
+          .getByTestId('balance')
+          .textContent(),
+      ) * 100,
+    );
+  }
+
+  async transferAllBalance(fromIdx, toIdx) {
+    const toName = await this.budgetTable
+      .getByTestId('row')
+      .nth(toIdx)
+      .getByTestId('category-name')
+      .textContent();
+
+    await this.budgetTable
+      .getByTestId('row')
+      .nth(fromIdx)
+      .getByTestId('balance')
+      .getByTestId(/^budget/)
+      .click();
+
+    await this.page
+      .getByRole('button', { name: 'Transfer to another category' })
+      .click();
+
+    await this.page.getByPlaceholder('(none)').click();
+
+    await this.page.keyboard.type(toName);
+    await this.page.keyboard.press('Enter');
+
+    await this.page.getByRole('button', { name: 'Transfer' }).click();
+  }
+}
diff --git a/packages/desktop-client/e2e/page-models/configuration-page.js b/packages/desktop-client/e2e/page-models/configuration-page.js
index 56a64bcaaa2dc2427d222406723fe825d43fdca6..ecaaa8a42eb3881bc1579b813c238d2514c1d326 100644
--- a/packages/desktop-client/e2e/page-models/configuration-page.js
+++ b/packages/desktop-client/e2e/page-models/configuration-page.js
@@ -1,3 +1,5 @@
+import { BudgetPage } from './budget-page';
+
 export class ConfigurationPage {
   constructor(page) {
     this.page = page;
@@ -5,5 +7,56 @@ export class ConfigurationPage {
 
   async createTestFile() {
     await this.page.getByRole('button', { name: 'Create test file' }).click();
+    return new BudgetPage(this.page);
+  }
+
+  async clickOnNoServer() {
+    await this.page.getByRole('button', { name: 'Don’t use a server' }).click();
+  }
+
+  async startFresh() {
+    await this.page.getByRole('button', { name: 'Start fresh' }).click();
+  }
+
+  async importBudget(type, file) {
+    const fileChooserPromise = this.page.waitForEvent('filechooser');
+    await this.page.getByRole('button', { name: 'Import my budget' }).click();
+
+    switch (type) {
+      case 'YNAB4':
+        await this.page
+          .getByRole('button', {
+            name: 'YNAB4 The old unsupported desktop app',
+          })
+          .click();
+        await this.page
+          .getByRole('button', { name: 'Select zip file...' })
+          .click();
+        break;
+
+      case 'nYNAB':
+        await this.page
+          .getByRole('button', { name: 'nYNAB The newer web app' })
+          .click();
+        await this.page.getByRole('button', { name: 'Select file...' }).click();
+        break;
+
+      case 'Actual':
+        await this.page
+          .getByRole('button', {
+            name: 'Actual Import a file exported from Actual',
+          })
+          .click();
+        await this.page.getByRole('button', { name: 'Select file...' }).click();
+        break;
+
+      default:
+        throw new Error(`Unrecognized import type: ${type}`);
+    }
+
+    const fileChooser = await fileChooserPromise;
+    await fileChooser.setFiles(file);
+
+    return new BudgetPage(this.page);
   }
 }
diff --git a/packages/desktop-client/src/components/accounts/Account.js b/packages/desktop-client/src/components/accounts/Account.js
index 28e364f9b5ab78b253a898a0973a5b2ceab63c89..ca69fe7b04ec6889dec55ecfdb916b8eea528e2d 100644
--- a/packages/desktop-client/src/components/accounts/Account.js
+++ b/packages/desktop-client/src/components/accounts/Account.js
@@ -401,6 +401,7 @@ function Balances({ balanceQuery, showExtraBalances, onToggleExtraBalances }) {
       }}
     >
       <Button
+        data-testid="account-balance"
         bare
         onClick={onToggleExtraBalances}
         style={{
@@ -767,6 +768,7 @@ const AccountHeader = React.memo(
               ) : (
                 <View
                   style={{ fontSize: 25, fontWeight: 500, marginBottom: 5 }}
+                  data-testid="account-name"
                 >
                   {account && account.closed
                     ? 'Closed: ' + accountName
diff --git a/packages/desktop-client/src/components/budget/misc.js b/packages/desktop-client/src/components/budget/misc.js
index 6e0f752597bcb77f618b8f56972c30b5b2b6f954..b5dfd75bdebc0c241983855d3467ec751cea0d2b 100644
--- a/packages/desktop-client/src/components/budget/misc.js
+++ b/packages/desktop-client/src/components/budget/misc.js
@@ -186,6 +186,7 @@ export class BudgetTable extends React.Component {
 
     return (
       <View
+        data-testid="budget-table"
         style={[
           { flex: 1 },
           styles.lightScrollbar && {
@@ -309,6 +310,7 @@ export function SidebarCategory({
       }}
     >
       <div
+        data-testid="category-name"
         style={{
           textOverflow: 'ellipsis',
           whiteSpace: 'nowrap',
@@ -607,6 +609,7 @@ function RenderMonths({ component: Component, editingIndex, args, style }) {
 const BudgetTotals = React.memo(function BudgetTotals({ MonthComponent }) {
   return (
     <View
+      data-testid="budget-totals"
       style={{
         backgroundColor: 'white',
         flexDirection: 'row',
diff --git a/packages/desktop-client/src/components/budget/rollover/BudgetSummary.js b/packages/desktop-client/src/components/budget/rollover/BudgetSummary.js
index 1dff564dc505f6a58973c06fa57a6672ba37cac3..69c0e194189420aeba19be2b741602475dc1ae05 100644
--- a/packages/desktop-client/src/components/budget/rollover/BudgetSummary.js
+++ b/packages/desktop-client/src/components/budget/rollover/BudgetSummary.js
@@ -277,6 +277,7 @@ export function BudgetSummary({
 
   return (
     <View
+      data-testid="budget-summary"
       style={{
         backgroundColor: 'white',
         boxShadow: MONTH_BOX_SHADOW,
diff --git a/packages/desktop-client/src/components/budget/rollover/TransferTooltip.js b/packages/desktop-client/src/components/budget/rollover/TransferTooltip.js
index afedb001a3adc5c44c9d7aa91756d0681a1c28ba..9f948f9612b1c9975c384d4edd3cf9e42beed4e5 100644
--- a/packages/desktop-client/src/components/budget/rollover/TransferTooltip.js
+++ b/packages/desktop-client/src/components/budget/rollover/TransferTooltip.js
@@ -88,7 +88,7 @@ export default function TransferTooltip({
         openOnFocus={true}
         onUpdate={id => {}}
         onSelect={id => setCategory(id)}
-        inputProps={{ onEnter: submit }}
+        inputProps={{ onEnter: submit, placeholder: '(none)' }}
       />
 
       <View
diff --git a/packages/loot-core/src/client/actions/budgets.js b/packages/loot-core/src/client/actions/budgets.js
index 351ff749b5fd8bd01b90a8d4893920b565a4ec33..24a7671d21788dab938edb94ef3fe6d48732d883 100644
--- a/packages/loot-core/src/client/actions/budgets.js
+++ b/packages/loot-core/src/client/actions/budgets.js
@@ -165,6 +165,7 @@ export function importBudget(filepath, type) {
     dispatch(closeModal());
 
     await dispatch(loadPrefs());
+    window.__history.push('/budget');
   };
 }
 
diff --git a/upcoming-release-notes/813.md b/upcoming-release-notes/813.md
new file mode 100644
index 0000000000000000000000000000000000000000..5a36b2ad38777120f4485218dbbb4934a8b84237
--- /dev/null
+++ b/upcoming-release-notes/813.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [MatissJanis]
+---
+
+Added onboarding and budget e2e tests