Compare commits
21 Commits
scrollToLo
...
dnd-kit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1877286702 | ||
|
|
ab2857521f | ||
|
|
ae43ed86ea | ||
|
|
2aa94b5d89 | ||
|
|
140f564e2e | ||
|
|
59168a284e | ||
|
|
61a65895cb | ||
|
|
779f2a5c13 | ||
|
|
def0aed7c6 | ||
|
|
8d8cd631b5 | ||
|
|
ed53972817 | ||
|
|
04e761e08a | ||
|
|
811f9e4300 | ||
|
|
fae68c19f5 | ||
|
|
96a9966d6b | ||
|
|
ca88608218 | ||
|
|
30a7701bdb | ||
|
|
c438a60fd7 | ||
|
|
72d491963f | ||
|
|
f46d87fd2d | ||
|
|
675918edea |
79
.eslintrc.js
@@ -38,7 +38,6 @@ module.exports = {
|
||||
extends: [
|
||||
'react-app',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:import/typescript',
|
||||
@@ -58,7 +57,7 @@ module.exports = {
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
varsIgnorePattern: '^(_|React)',
|
||||
varsIgnorePattern: '^_',
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
@@ -91,7 +90,15 @@ module.exports = {
|
||||
'react/prop-types': 'off',
|
||||
|
||||
// TODO: re-enable these rules
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
'react/display-name': 'off',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
// 'react-hooks/exhaustive-deps': [
|
||||
// 'warn',
|
||||
// {
|
||||
// additionalHooks: 'useLiveQuery',
|
||||
// },
|
||||
// ],
|
||||
|
||||
'no-var': 'warn',
|
||||
'react/jsx-curly-brace-presence': 'warn',
|
||||
@@ -146,9 +153,10 @@ module.exports = {
|
||||
'Using default React import is discouraged, please use named exports directly instead.',
|
||||
},
|
||||
{
|
||||
// forbid <a> in favor of <Link>
|
||||
// forbid <a> in favor of <LinkButton> or <ExternalLink>
|
||||
selector: 'JSXOpeningElement[name.name="a"]',
|
||||
message: 'Using <a> is discouraged, please use <Link> instead.',
|
||||
message:
|
||||
'Using <a> is discouraged, please use <LinkButton> or <ExternalLink> instead.',
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': [
|
||||
@@ -269,69 +277,6 @@ module.exports = {
|
||||
'import/no-default-export': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
// TODO: fix the issues in these files
|
||||
files: [
|
||||
'./packages/desktop-client/src/components/accounts/Account.jsx',
|
||||
'./packages/desktop-client/src/components/accounts/MobileAccount.jsx',
|
||||
'./packages/desktop-client/src/components/accounts/MobileAccounts.jsx',
|
||||
'./packages/desktop-client/src/components/App.tsx',
|
||||
'./packages/desktop-client/src/components/budget/BudgetCategories.jsx',
|
||||
'./packages/desktop-client/src/components/budget/BudgetSummaries.tsx',
|
||||
'./packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx',
|
||||
'./packages/desktop-client/src/components/budget/index.tsx',
|
||||
'./packages/desktop-client/src/components/budget/MobileBudget.tsx',
|
||||
'./packages/desktop-client/src/components/budget/rollover/HoldMenu.tsx',
|
||||
'./packages/desktop-client/src/components/budget/rollover/TransferMenu.tsx',
|
||||
'./packages/desktop-client/src/components/common/Menu.tsx',
|
||||
'./packages/desktop-client/src/components/FinancesApp.tsx',
|
||||
'./packages/desktop-client/src/components/GlobalKeys.ts',
|
||||
'./packages/desktop-client/src/components/LoggedInUser.tsx',
|
||||
'./packages/desktop-client/src/components/manager/ManagementApp.jsx',
|
||||
'./packages/desktop-client/src/components/manager/subscribe/common.tsx',
|
||||
'./packages/desktop-client/src/components/ManageRules.tsx',
|
||||
'./packages/desktop-client/src/components/mobile/MobileAmountInput.jsx',
|
||||
'./packages/desktop-client/src/components/mobile/MobileNavTabs.tsx',
|
||||
'./packages/desktop-client/src/components/Modals.tsx',
|
||||
'./packages/desktop-client/src/components/modals/EditRule.jsx',
|
||||
'./packages/desktop-client/src/components/modals/ImportTransactions.jsx',
|
||||
'./packages/desktop-client/src/components/modals/MergeUnusedPayees.jsx',
|
||||
'./packages/desktop-client/src/components/Notifications.tsx',
|
||||
'./packages/desktop-client/src/components/payees/ManagePayees.jsx',
|
||||
'./packages/desktop-client/src/components/payees/ManagePayeesWithData.jsx',
|
||||
'./packages/desktop-client/src/components/payees/PayeeTable.tsx',
|
||||
'./packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx',
|
||||
'./packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx',
|
||||
'./packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx',
|
||||
'./packages/desktop-client/src/components/reports/reports/CustomReport.jsx',
|
||||
'./packages/desktop-client/src/components/reports/reports/NetWorthCard.jsx',
|
||||
'./packages/desktop-client/src/components/reports/SaveReportName.tsx',
|
||||
'./packages/desktop-client/src/components/reports/useReport.ts',
|
||||
'./packages/desktop-client/src/components/schedules/ScheduleDetails.jsx',
|
||||
'./packages/desktop-client/src/components/schedules/SchedulesTable.tsx',
|
||||
'./packages/desktop-client/src/components/select/DateSelect.tsx',
|
||||
'./packages/desktop-client/src/components/sidebar/Tools.tsx',
|
||||
'./packages/desktop-client/src/components/sort.tsx',
|
||||
'./packages/desktop-client/src/components/spreadsheet/useSheetValue.ts',
|
||||
'./packages/desktop-client/src/components/table.tsx',
|
||||
'./packages/desktop-client/src/components/Titlebar.tsx',
|
||||
'./packages/desktop-client/src/components/transactions/MobileTransaction.jsx',
|
||||
'./packages/desktop-client/src/components/transactions/SelectedTransactions.jsx',
|
||||
'./packages/desktop-client/src/components/transactions/SimpleTransactionsTable.jsx',
|
||||
'./packages/desktop-client/src/components/transactions/TransactionList.jsx',
|
||||
'./packages/desktop-client/src/components/transactions/TransactionsTable.jsx',
|
||||
'./packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx',
|
||||
'./packages/desktop-client/src/hooks/useAccounts.ts',
|
||||
'./packages/desktop-client/src/hooks/useCategories.ts',
|
||||
'./packages/desktop-client/src/hooks/usePayees.ts',
|
||||
'./packages/desktop-client/src/hooks/useProperFocus.tsx',
|
||||
'./packages/desktop-client/src/hooks/useSelected.tsx',
|
||||
'./packages/loot-core/src/client/query-hooks.tsx',
|
||||
],
|
||||
rules: {
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
|
||||
29
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -8,13 +8,6 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please ensure you provide as much information as possible to better assist in confirming and identifying a fix for the bug.
|
||||
- type: markdown
|
||||
id: intro-md
|
||||
attributes:
|
||||
value: |
|
||||
**IMPORTANT:** we use Github Issues only for BUG REPORTS and FEATURE REQUESTS. If you are looking for help/support - please reach out to the [community on Discord](https://discord.gg/pRYNYr4W5A). All non-bug and non-feature-request issues will be closed.
|
||||
|
||||
**Bank-sync problems (SimpleFin / GoCardless)?** Reach out via the [community Discord](https://discord.gg/pRYNYr4W5A) first and open an issue only if the community deems the issue to be a legitimate bug in Actual.
|
||||
- type: checkboxes
|
||||
id: existing-issue
|
||||
attributes:
|
||||
@@ -23,10 +16,20 @@ body:
|
||||
options:
|
||||
- label: 'I have searched and found no existing issue'
|
||||
required: true
|
||||
- label: 'I will be providing steps how to reproduce the bug (in most cases this will also mean uploading a demo budget file)'
|
||||
required: true
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: bank-sync-issue
|
||||
attributes:
|
||||
label: 'Is this related to GoCardless, Simplefin or another bank-sync provider?'
|
||||
description: 'Most issues with bank-sync providers are due to a lack of a custom bank-mapper (i.e. payee or other fields not coming through). In such cases you can create a custom bank mapper in [actual-server](https://github.com/actualbudget/actual-server/blob/master/src/app-gocardless/README.md) repository. Other likely issue is misconfigured server - in which case please reach out via the [community Discord](https://discord.gg/pRYNYr4W5A) to get support.'
|
||||
options:
|
||||
- label: 'I have checked my server logs and could not see any errors there'
|
||||
- label: 'I will be attaching my server logs to this issue'
|
||||
- label: 'I will be attaching my client-side (browser) logs to this issue'
|
||||
- label: 'I understand that this issue will be automatically closed if insufficient information is provided'
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
@@ -36,6 +39,13 @@ body:
|
||||
value: 'A bug happened!'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: errors-received
|
||||
attributes:
|
||||
label: 'What error did you receive?'
|
||||
description: 'If you received an error or a message on the screen, please provide that here.'
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
id: env-info
|
||||
attributes:
|
||||
@@ -49,7 +59,6 @@ body:
|
||||
- Locally via Yarn
|
||||
- Docker
|
||||
- Fly.io
|
||||
- Pikapods
|
||||
- NAS
|
||||
- Desktop App (Electron)
|
||||
- Other
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Bank-sync issues
|
||||
url: https://discord.gg/pRYNYr4W5A
|
||||
about: Is bank-sync not working? Returning too much or too few information? Reach out to the community on Discord.
|
||||
- name: Support
|
||||
url: https://discord.gg/pRYNYr4W5A
|
||||
about: Need help with something? Having troubles setting up? Or perhaps issues using the API? Reach out to the community on Discord.
|
||||
about: Need help with something? Perhaps having issues setting up bank-sync with GoCardless or SimpleFin? Reach out to the community on Discord.
|
||||
|
||||
4
.github/actions/setup/action.yml
vendored
@@ -4,11 +4,11 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.16.0
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
id: cache
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
|
||||
14
.github/workflows/build.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
api:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Build API
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
- name: Create package tgz
|
||||
run: cd packages/api && yarn pack && mv package.tgz actual-api.tgz
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: actual-api
|
||||
path: packages/api/actual-api.tgz
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
crdt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Build CRDT
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- name: Create package tgz
|
||||
run: cd packages/crdt && yarn pack && mv package.tgz actual-crdt.tgz
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: actual-crdt
|
||||
path: packages/crdt/actual-crdt.tgz
|
||||
@@ -53,18 +53,18 @@ jobs:
|
||||
web:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Build Web
|
||||
run: ./bin/package-browser
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: actual-web
|
||||
path: packages/desktop-client/build
|
||||
- name: Upload Build Stats
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: build-stats
|
||||
path: packages/desktop-client/build-stats
|
||||
|
||||
10
.github/workflows/check.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Lint
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Typecheck
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Test
|
||||
@@ -40,8 +40,8 @@ jobs:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '19'
|
||||
- name: Check migrations
|
||||
|
||||
6
.github/workflows/codeql.yml
vendored
@@ -22,14 +22,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: '/language:javascript'
|
||||
|
||||
12
.github/workflows/e2e-test.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
outputs:
|
||||
netlify_url: ${{ steps.netlify.outputs.url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Wait for Netlify build to finish
|
||||
@@ -33,20 +33,19 @@ jobs:
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.41.1-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Run E2E Tests on Netlify URL
|
||||
run: yarn e2e
|
||||
env:
|
||||
E2E_START_URL: ${{ needs.netlify.outputs.netlify_url }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: desktop-client-test-results
|
||||
path: packages/desktop-client/test-results/
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
vrt:
|
||||
name: Visual regression
|
||||
needs: netlify
|
||||
@@ -54,17 +53,16 @@ jobs:
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.41.1-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Run VRT Tests on Netlify URL
|
||||
run: yarn vrt
|
||||
env:
|
||||
E2E_START_URL: ${{ needs.netlify.outputs.netlify_url }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: desktop-client-test-results
|
||||
path: packages/desktop-client/test-results/
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
||||
40
.github/workflows/electron-master.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Electron Master
|
||||
name: Electron
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -9,8 +9,8 @@ env:
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v**
|
||||
branches:
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -18,9 +18,6 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# this is so the assets can be added to the release
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
@@ -29,46 +26,25 @@ jobs:
|
||||
- macos-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- if: ${{ startsWith(matrix.os, 'windows') }}
|
||||
run: pip.exe install setuptools
|
||||
- if: ${{ ! startsWith(matrix.os, 'windows') }}
|
||||
run: |
|
||||
mkdir .venv
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
python3 -m pip install setuptools
|
||||
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||
run: |
|
||||
sudo apt-get install flatpak -y
|
||||
sudo apt-get install flatpak-builder -y
|
||||
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
sudo flatpak install org.freedesktop.Sdk/x86_64/23.08 -y
|
||||
sudo flatpak install org.freedesktop.Platform/x86_64/23.08 -y
|
||||
sudo flatpak install org.electronjs.Electron2.BaseApp/x86_64/23.08 -y
|
||||
run: python3 -m pip install setuptools
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Build Electron
|
||||
run: ./bin/package-electron
|
||||
env:
|
||||
# CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
# CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: actual-electron-${{ matrix.os }}
|
||||
path: |
|
||||
packages/desktop-electron/dist/*.dmg
|
||||
packages/desktop-electron/dist/*.exe
|
||||
packages/desktop-electron/dist/*.AppImage
|
||||
packages/desktop-electron/dist/*.flatpak
|
||||
- name: Add to Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
packages/desktop-electron/dist/*.dmg
|
||||
packages/desktop-electron/dist/*.exe
|
||||
packages/desktop-electron/dist/*.AppImage
|
||||
packages/desktop-electron/dist/*.flatpak
|
||||
|
||||
19
.github/workflows/electron-pr.yml
vendored
@@ -24,33 +24,20 @@ jobs:
|
||||
- macos-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- if: ${{ startsWith(matrix.os, 'windows') }}
|
||||
run: pip.exe install setuptools
|
||||
- if: ${{ ! startsWith(matrix.os, 'windows') }}
|
||||
run: |
|
||||
mkdir .venv
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
python3 -m pip install setuptools
|
||||
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||
run: |
|
||||
sudo apt-get install flatpak -y
|
||||
sudo apt-get install flatpak-builder -y
|
||||
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
sudo flatpak install org.freedesktop.Sdk/x86_64/23.08 -y
|
||||
sudo flatpak install org.freedesktop.Platform/x86_64/23.08 -y
|
||||
sudo flatpak install org.electronjs.Electron2.BaseApp/x86_64/23.08 -y
|
||||
run: python3 -m pip install setuptools
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Build Electron
|
||||
run: ./bin/package-electron
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: actual-electron-${{ matrix.os }}
|
||||
path: |
|
||||
packages/desktop-electron/dist/*.dmg
|
||||
packages/desktop-electron/dist/*.exe
|
||||
packages/desktop-electron/dist/*.AppImage
|
||||
packages/desktop-electron/dist/*.flatpak
|
||||
|
||||
@@ -24,8 +24,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# This is not a security concern because we have approved & merged the PR
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '19'
|
||||
- name: Handle feature requests
|
||||
|
||||
43
.github/workflows/netlify-release.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Deploy Netlify Release
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
CI: true
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repository Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Install Netlify
|
||||
run: npm install netlify-cli@17.10.1 -g
|
||||
|
||||
- name: Build Actual
|
||||
run: ./bin/package-browser
|
||||
|
||||
- name: Deploy to Netlify
|
||||
id: netlify_deploy
|
||||
run: |
|
||||
netlify deploy \
|
||||
--dir packages/desktop-client/build \
|
||||
--site ${{ secrets.NETLIFY_SITE_ID }} \
|
||||
--auth ${{ secrets.NETLIFY_API_TOKEN }} \
|
||||
--filter @actual-app/web \
|
||||
--prod
|
||||
2
.github/workflows/release-notes.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
- name: Check release notes
|
||||
if: startsWith(github.head_ref, 'release/') == false
|
||||
uses: actualbudget/actions/release-notes/check@main
|
||||
|
||||
4
.github/workflows/size-compare.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
echo "Build failed on PR branch or ${{github.base_ref}}"
|
||||
exit 1
|
||||
- name: Download build artifact from ${{github.base_ref}}
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
id: pr-build
|
||||
with:
|
||||
branch: ${{github.base_ref}}
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
path: base
|
||||
|
||||
- name: Download build artifact from PR
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
pr: ${{github.event.pull_request.number}}
|
||||
workflow: build.yml
|
||||
|
||||
39
.github/workflows/trafico.yml
vendored
@@ -1,39 +0,0 @@
|
||||
##########################################################################################
|
||||
# WARNING! This workflow uses the 'pull_request_target' event. That mans that it will #
|
||||
# always run in the context of the main actualbudget/actual repo, even if the PR is from #
|
||||
# a fork. This is necessary to get access to a GitHub token that can modify the PR. #
|
||||
# Be VERY CAREFUL about adding things to this workflow, since forks can inject #
|
||||
# arbitrary code into their branch, and can pollute the artifacts we download. Arbitrary #
|
||||
# code execution in this workflow could lead to a compromise of the main repo. #
|
||||
##########################################################################################
|
||||
# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests #
|
||||
##########################################################################################
|
||||
|
||||
name: Trafico Reviews
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- closed
|
||||
- reopened
|
||||
- synchronize
|
||||
- edited
|
||||
- review_requested
|
||||
- review_request_removed
|
||||
pull_request_review:
|
||||
types: [submitted, edited, dismissed]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
manage-review:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actualbudget/trafico@main
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
27
.github/workflows/wip.yml
vendored
@@ -1,27 +0,0 @@
|
||||
name: Add WIP
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add_wip_prefix:
|
||||
if: |
|
||||
join(github.event.pull_request.requested_reviewers) == ''
|
||||
&& !contains(github.event.pull_request.title, 'WIP')
|
||||
&& !contains(github.event.pull_request.labels.*.name, 'WIP')
|
||||
&& github.event.pull_request.draft != true
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Add WIP
|
||||
env:
|
||||
TITLE: ${{ github.event.pull_request.title }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo ${{ secrets.GITHUB_TOKEN }} | gh auth login --with-token
|
||||
gh pr edit ${{ github.event.pull_request.number }} -t "[WIP] ${TITLE}"
|
||||
37
.gitignore
vendored
@@ -1,22 +1,6 @@
|
||||
# Sample Data
|
||||
/data/*
|
||||
!data/.gitkeep
|
||||
/data2
|
||||
Actual-*
|
||||
**/xcuserdata/*
|
||||
export-2020-01-10.csv
|
||||
|
||||
# Secrets
|
||||
.secret-tokens
|
||||
|
||||
# MacOS
|
||||
.DS_Store
|
||||
|
||||
# Logs
|
||||
**/*.log
|
||||
|
||||
# JavaScript
|
||||
node_modules
|
||||
packages/api/dist
|
||||
packages/api/@types
|
||||
packages/crdt/dist
|
||||
@@ -24,10 +8,22 @@ packages/desktop-electron/client-build
|
||||
packages/desktop-electron/.electron-symbols
|
||||
packages/desktop-electron/dist
|
||||
packages/desktop-electron/loot-core
|
||||
node_modules
|
||||
.DS_Store
|
||||
lerna-debug.log
|
||||
Actual-*
|
||||
.#*
|
||||
**/xcuserdata/*
|
||||
.secret-tokens
|
||||
bundle.desktop.js
|
||||
bundle.desktop.js.map
|
||||
bundle.mobile.js
|
||||
bundle.mobile.js.map
|
||||
export-2020-01-10.csv
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
**/*.log
|
||||
|
||||
# Yarn
|
||||
.pnp.*
|
||||
@@ -40,12 +36,3 @@ bundle.mobile.js.map
|
||||
|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
# IntelliJ IDEA
|
||||
.idea
|
||||
|
||||
# Misc
|
||||
.#*
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
|
||||
@@ -4,4 +4,4 @@ enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.0.2.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.0.1.cjs
|
||||
|
||||
@@ -10,4 +10,4 @@ if [ ! -d "node_modules" ] || [ "$(ls -A node_modules)" = "" ]; then
|
||||
yarn
|
||||
fi
|
||||
|
||||
BROWSER=0 yarn start:browser
|
||||
yarn start:browser
|
||||
|
||||
@@ -38,7 +38,7 @@ yarn rebuild-electron
|
||||
|
||||
yarn workspace loot-core build:node
|
||||
|
||||
yarn workspace @actual-app/web build --mode=desktop
|
||||
yarn workspace @actual-app/web build
|
||||
|
||||
yarn workspace desktop-electron update-client
|
||||
|
||||
|
||||
32
bin/run-vrt
@@ -1,32 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# See here for more information: https://github.com/actualbudget/actual/tree/master/packages/desktop-client#visual-regression
|
||||
|
||||
if [ ! -d "node_modules" ] || [ "$(ls -A node_modules)" = "" ]; then
|
||||
yarn
|
||||
fi
|
||||
|
||||
E2E_START_URL="${E2E_START_URL:-https://localhost:3001}"
|
||||
VRT_ARGS=""
|
||||
|
||||
# Loop through all arguments
|
||||
while [ $# -gt 0 ]; do
|
||||
key="$1"
|
||||
case $key in
|
||||
--e2e-start-url)
|
||||
E2E_START_URL="$2"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
VRT_ARGS="$VRT_ARGS $1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
echo "Running VRT tests with the following parameters:"
|
||||
echo "E2E_START_URL: $E2E_START_URL"
|
||||
echo "VRT_ARGS: $VRT_ARGS"
|
||||
|
||||
docker run --rm --network host -v "$(pwd)":/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.41.1-jammy /bin/bash \
|
||||
-c "E2E_START_URL=$E2E_START_URL yarn vrt $VRT_ARGS"
|
||||
@@ -8,8 +8,6 @@ services:
|
||||
actual-development:
|
||||
build: .
|
||||
image: actual-development
|
||||
environment:
|
||||
- HTTPS
|
||||
ports:
|
||||
- '3001:3001'
|
||||
volumes:
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
"test:debug": "yarn workspaces foreach --all --verbose run test",
|
||||
"e2e": "yarn workspaces foreach --all --parallel --verbose run e2e",
|
||||
"vrt": "yarn workspaces foreach --all --parallel --verbose run vrt",
|
||||
"vrt:docker": "./bin/run-vrt",
|
||||
"rebuild-electron": "./node_modules/.bin/electron-rebuild -f -m ./packages/loot-core",
|
||||
"rebuild-node": "yarn workspace loot-core rebuild",
|
||||
"lint": "eslint . --max-warnings 0 --ext .js,.jsx,.ts,.tsx",
|
||||
@@ -53,19 +52,17 @@
|
||||
"eslint-plugin-react": "7.32.2",
|
||||
"eslint-plugin-rulesdir": "^0.2.2",
|
||||
"node-jq": "^4.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"prettier": "3.2.4",
|
||||
"react-refresh": "^0.14.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"typescript": "^5.0.2",
|
||||
"typescript-strict-plugin": "^2.2.2-beta.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"rollup": "4.9.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"packageManager": "yarn@4.0.2",
|
||||
"packageManager": "yarn@4.0.1",
|
||||
"browserslist": [
|
||||
"electron 24.0",
|
||||
"defaults"
|
||||
|
||||
@@ -58,42 +58,11 @@ describe('API CRUD operations', () => {
|
||||
await api.loadBudget(budgetName);
|
||||
});
|
||||
|
||||
// apis: getCategoryGroups, createCategoryGroup, updateCategoryGroup, deleteCategoryGroup
|
||||
// apis: createCategoryGroup, updateCategoryGroup, deleteCategoryGroup
|
||||
test('CategoryGroups: successfully update category groups', async () => {
|
||||
const month = '2023-10';
|
||||
global.currentMonth = month;
|
||||
|
||||
// get existing category groups
|
||||
const groups = await api.getCategoryGroups();
|
||||
expect(groups).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
hidden: 0,
|
||||
id: 'fc3825fd-b982-4b72-b768-5b30844cf832',
|
||||
is_income: 0,
|
||||
name: 'Usual Expenses',
|
||||
sort_order: 16384,
|
||||
tombstone: 0,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
hidden: 0,
|
||||
id: 'a137772f-cf2f-4089-9432-822d2ddc1466',
|
||||
is_income: 0,
|
||||
name: 'Investments and Savings',
|
||||
sort_order: 32768,
|
||||
tombstone: 0,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
hidden: 0,
|
||||
id: '2E1F5BDB-209B-43F9-AF2C-3CE28E380C00',
|
||||
is_income: 1,
|
||||
name: 'Income',
|
||||
sort_order: 32768,
|
||||
tombstone: 0,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// create our test category group
|
||||
const mainGroupId = await api.createCategoryGroup({
|
||||
name: 'test-group',
|
||||
@@ -346,221 +315,13 @@ describe('API CRUD operations', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// apis: getRules, getPayeeRules, createRule, updateRule, deleteRule
|
||||
test('Rules: successfully update rules', async () => {
|
||||
await api.createPayee({ name: 'test-payee' });
|
||||
await api.createPayee({ name: 'test-payee2' });
|
||||
|
||||
// create our test rules
|
||||
const rule = await api.createRule({
|
||||
stage: 'pre',
|
||||
conditionsOp: 'and',
|
||||
conditions: [
|
||||
{
|
||||
field: 'payee',
|
||||
op: 'is',
|
||||
value: 'test-payee',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
op: 'set',
|
||||
field: 'category',
|
||||
value: 'fc3825fd-b982-4b72-b768-5b30844cf832',
|
||||
},
|
||||
],
|
||||
});
|
||||
const rule2 = await api.createRule({
|
||||
stage: 'pre',
|
||||
conditionsOp: 'and',
|
||||
conditions: [
|
||||
{
|
||||
field: 'payee',
|
||||
op: 'is',
|
||||
value: 'test-payee2',
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
op: 'set',
|
||||
field: 'category',
|
||||
value: 'fc3825fd-b982-4b72-b768-5b30844cf832',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// get existing rules
|
||||
const rules = await api.getRules();
|
||||
expect(rules).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
actions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'category',
|
||||
op: 'set',
|
||||
type: 'id',
|
||||
value: 'fc3825fd-b982-4b72-b768-5b30844cf832',
|
||||
}),
|
||||
]),
|
||||
conditions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'payee',
|
||||
op: 'is',
|
||||
type: 'id',
|
||||
value: 'test-payee2',
|
||||
}),
|
||||
]),
|
||||
conditionsOp: 'and',
|
||||
id: rule2.id,
|
||||
stage: 'pre',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
actions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'category',
|
||||
op: 'set',
|
||||
type: 'id',
|
||||
value: 'fc3825fd-b982-4b72-b768-5b30844cf832',
|
||||
}),
|
||||
]),
|
||||
conditions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'payee',
|
||||
op: 'is',
|
||||
type: 'id',
|
||||
value: 'test-payee',
|
||||
}),
|
||||
]),
|
||||
conditionsOp: 'and',
|
||||
id: rule.id,
|
||||
stage: 'pre',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// get by payee
|
||||
expect(await api.getPayeeRules('test-payee')).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
actions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'category',
|
||||
op: 'set',
|
||||
type: 'id',
|
||||
value: 'fc3825fd-b982-4b72-b768-5b30844cf832',
|
||||
}),
|
||||
]),
|
||||
conditions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'payee',
|
||||
op: 'is',
|
||||
type: 'id',
|
||||
value: 'test-payee',
|
||||
}),
|
||||
]),
|
||||
conditionsOp: 'and',
|
||||
id: rule.id,
|
||||
stage: 'pre',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(await api.getPayeeRules('test-payee2')).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
actions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'category',
|
||||
op: 'set',
|
||||
type: 'id',
|
||||
value: 'fc3825fd-b982-4b72-b768-5b30844cf832',
|
||||
}),
|
||||
]),
|
||||
conditions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'payee',
|
||||
op: 'is',
|
||||
type: 'id',
|
||||
value: 'test-payee2',
|
||||
}),
|
||||
]),
|
||||
conditionsOp: 'and',
|
||||
id: rule2.id,
|
||||
stage: 'pre',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// update one rule
|
||||
const updatedRule = {
|
||||
...rule,
|
||||
stage: 'post',
|
||||
conditionsOp: 'or',
|
||||
};
|
||||
expect(await api.updateRule(updatedRule)).toEqual(updatedRule);
|
||||
|
||||
expect(await api.getRules()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
actions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'category',
|
||||
op: 'set',
|
||||
type: 'id',
|
||||
value: 'fc3825fd-b982-4b72-b768-5b30844cf832',
|
||||
}),
|
||||
]),
|
||||
conditions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'payee',
|
||||
op: 'is',
|
||||
type: 'id',
|
||||
value: 'test-payee',
|
||||
}),
|
||||
]),
|
||||
conditionsOp: 'or',
|
||||
id: rule.id,
|
||||
stage: 'post',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
actions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'category',
|
||||
op: 'set',
|
||||
type: 'id',
|
||||
value: 'fc3825fd-b982-4b72-b768-5b30844cf832',
|
||||
}),
|
||||
]),
|
||||
conditions: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: 'payee',
|
||||
op: 'is',
|
||||
type: 'id',
|
||||
value: 'test-payee2',
|
||||
}),
|
||||
]),
|
||||
conditionsOp: 'and',
|
||||
id: rule2.id,
|
||||
stage: 'pre',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// delete rules
|
||||
await api.deleteRule(rules[1]);
|
||||
expect(await api.getRules()).toHaveLength(1);
|
||||
|
||||
await api.deleteRule(rules[0]);
|
||||
expect(await api.getRules()).toHaveLength(0);
|
||||
});
|
||||
|
||||
// apis: addTransactions, getTransactions, importTransactions, updateTransaction, deleteTransaction
|
||||
test('Transactions: successfully update transactions', async () => {
|
||||
const accountId = await api.createAccount({ name: 'test-account' }, 0);
|
||||
|
||||
let newTransaction = [
|
||||
{ date: '2023-11-03', imported_id: '11', amount: 100, notes: 'notes' },
|
||||
{ date: '2023-11-03', imported_id: '12', amount: 100, notes: '' },
|
||||
{ date: '2023-11-03', imported_id: '11', amount: 100 },
|
||||
{ date: '2023-11-03', imported_id: '11', amount: 100 },
|
||||
];
|
||||
|
||||
const addResult = await api.addTransactions(accountId, newTransaction, {
|
||||
@@ -583,9 +344,8 @@ describe('API CRUD operations', () => {
|
||||
expect(transactions).toHaveLength(2);
|
||||
|
||||
newTransaction = [
|
||||
{ date: '2023-12-03', imported_id: '11', amount: 100, notes: 'notes' },
|
||||
{ date: '2023-12-03', imported_id: '12', amount: 100, notes: 'notes' },
|
||||
{ date: '2023-12-03', imported_id: '22', amount: 200, notes: '' },
|
||||
{ date: '2023-12-03', imported_id: '11', amount: 100 },
|
||||
{ date: '2023-12-03', imported_id: '22', amount: 200 },
|
||||
];
|
||||
|
||||
const reconciled = await api.importTransactions(accountId, newTransaction);
|
||||
@@ -601,22 +361,9 @@ describe('API CRUD operations', () => {
|
||||
'2023-12-31',
|
||||
);
|
||||
expect(transactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ imported_id: '22', amount: 200 }),
|
||||
]),
|
||||
);
|
||||
expect(transactions).toHaveLength(1);
|
||||
|
||||
// confirm imported transactions update perfomed
|
||||
transactions = await api.getTransactions(
|
||||
accountId,
|
||||
'2023-11-01',
|
||||
'2023-11-30',
|
||||
);
|
||||
expect(transactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ notes: 'notes', amount: 100 }),
|
||||
]),
|
||||
expect.arrayContaining(
|
||||
newTransaction.map(trans => expect.objectContaining(trans)),
|
||||
),
|
||||
);
|
||||
expect(transactions).toHaveLength(2);
|
||||
|
||||
|
||||
@@ -35,10 +35,6 @@ export async function sync() {
|
||||
return send('api/sync');
|
||||
}
|
||||
|
||||
export async function runBankSync(args?: { accountId: string }) {
|
||||
return send('api/bank-sync', args);
|
||||
}
|
||||
|
||||
export async function batchBudgetUpdates(func) {
|
||||
await send('api/batch-budget-start');
|
||||
try {
|
||||
@@ -125,10 +121,6 @@ export function deleteAccount(id) {
|
||||
return send('api/account-delete', { id });
|
||||
}
|
||||
|
||||
export function getCategoryGroups() {
|
||||
return send('api/category-groups-get');
|
||||
}
|
||||
|
||||
export function createCategoryGroup(group) {
|
||||
return send('api/category-group-create', { group });
|
||||
}
|
||||
@@ -172,23 +164,3 @@ export function updatePayee(id, fields) {
|
||||
export function deletePayee(id) {
|
||||
return send('api/payee-delete', { id });
|
||||
}
|
||||
|
||||
export function getRules() {
|
||||
return send('api/rules-get');
|
||||
}
|
||||
|
||||
export function getPayeeRules(id) {
|
||||
return send('api/payee-rules-get', { id });
|
||||
}
|
||||
|
||||
export function createRule(rule) {
|
||||
return send('api/rule-create', { rule });
|
||||
}
|
||||
|
||||
export function updateRule(rule) {
|
||||
return send('api/rule-update', { rule });
|
||||
}
|
||||
|
||||
export function deleteRule(id) {
|
||||
return send('api/rule-delete', { id });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actual-app/api",
|
||||
"version": "6.8.1",
|
||||
"version": "6.4.0",
|
||||
"license": "MIT",
|
||||
"description": "An API for Actual",
|
||||
"engines": {
|
||||
@@ -9,8 +9,7 @@
|
||||
"main": "dist/index.js",
|
||||
"types": "@types/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"@types"
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build:app": "yarn workspace loot-core build:api",
|
||||
@@ -22,18 +21,17 @@
|
||||
"clean": "rm -rf dist @types"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actual-app/crdt": "workspace:^",
|
||||
"better-sqlite3": "^9.6.0",
|
||||
"better-sqlite3": "^9.2.2",
|
||||
"compare-versions": "^6.1.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"uuid": "^9.0.1"
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@swc/core": "^1.5.3",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@swc/core": "^1.3.105",
|
||||
"@swc/jest": "^0.2.31",
|
||||
"@types/jest": "^27.5.0",
|
||||
"@types/uuid": "^9.0.2",
|
||||
"jest": "^27.5.1",
|
||||
"jest": "^27.0.0",
|
||||
"tsc-alias": "^1.8.8",
|
||||
"typescript": "^5.0.2"
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"outDir": "dist",
|
||||
"declarationDir": "@types",
|
||||
"paths": {
|
||||
"loot-core/src/*": ["./loot-core/*"],
|
||||
"loot-core/*": ["./@types/loot-core/*"],
|
||||
}
|
||||
},
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
"dependencies": {
|
||||
"google-protobuf": "^3.12.0-rc.1",
|
||||
"murmurhash": "^2.0.1",
|
||||
"uuid": "^9.0.1"
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@swc/core": "^1.5.3",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@swc/core": "^1.3.105",
|
||||
"@swc/jest": "^0.2.31",
|
||||
"@types/jest": "^27.5.0",
|
||||
"@types/uuid": "^9.0.2",
|
||||
"jest": "^27.5.1",
|
||||
"jest": "^27.0.0",
|
||||
"ts-protoc-gen": "^0.15.0",
|
||||
"typescript": "^5.0.2"
|
||||
}
|
||||
|
||||
@@ -32,35 +32,13 @@ Prerequisites:
|
||||
|
||||
#### Running against the local server
|
||||
|
||||
First start a dev instance:
|
||||
First start the dev server:
|
||||
|
||||
```sh
|
||||
HTTPS=true yarn start
|
||||
```
|
||||
|
||||
or using the dev container:
|
||||
```
|
||||
HTTPS=true docker compose up --build
|
||||
```
|
||||
|
||||
Note the network IP address and port the dev instance is listening on.
|
||||
|
||||
Next, navigate to the root of your project folder, run the standardized docker container, and launch the visual regression tests from within it.
|
||||
|
||||
Run via yarn:
|
||||
|
||||
```sh
|
||||
# By default, this connects to https://localhost:3001
|
||||
yarn vrt:docker
|
||||
|
||||
# To use a different ip and port:
|
||||
yarn vrt:docker --e2e-start-url https://ip:port
|
||||
|
||||
# To update snapshots, use the following command:
|
||||
yarn vrt:docker --e2e-start-url https://ip:port --update-snapshots
|
||||
```
|
||||
|
||||
Run manually:
|
||||
Next, navigate to the root of your project folder, run the standartised docker container, and launch the visual regression tests from within it.
|
||||
|
||||
```sh
|
||||
# Run docker container
|
||||
@@ -69,27 +47,17 @@ docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/
|
||||
# If you receive an error such as "docker: invalid reference format", please instead use the following command:
|
||||
docker run --rm --network host -v ${pwd}:/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.41.1-jammy /bin/bash
|
||||
|
||||
# Once inside the docker container, run the VRT tests: important - they MUST be ran against a HTTPS server.
|
||||
# Use the ip and port noted earlier
|
||||
E2E_START_URL=https://ip:port yarn vrt
|
||||
# Run the VRT tests: important - they MUST be ran against a HTTPS server
|
||||
E2E_START_URL=https://192.168.0.178:3001 yarn vrt
|
||||
|
||||
# To update snapshots, use the following command:
|
||||
E2E_START_URL=https://ip:port yarn vrt --update-snapshots
|
||||
E2E_START_URL=https://192.168.0.178:3001 yarn vrt --update-snapshots
|
||||
```
|
||||
|
||||
#### Running against a remote server
|
||||
|
||||
You can also run the tests against a remote server by passing the URL:
|
||||
|
||||
Run in standardized docker container:
|
||||
```sh
|
||||
E2E_START_URL=https://my-remote-server.com yarn vrt:docker
|
||||
|
||||
# Or pass in server URL as argument
|
||||
yarn vrt:docker --e2e-start-url https://my-remote-server.com
|
||||
```
|
||||
|
||||
Run locally:
|
||||
```sh
|
||||
E2E_START_URL=https://my-remote-server.com yarn vrt
|
||||
```
|
||||
|
||||
@@ -7,7 +7,6 @@ test.describe('Accounts', () => {
|
||||
let page;
|
||||
let navigation;
|
||||
let configurationPage;
|
||||
let accountPage;
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
@@ -23,7 +22,7 @@ test.describe('Accounts', () => {
|
||||
});
|
||||
|
||||
test('creates a new account and views the initial balance transaction', async () => {
|
||||
accountPage = await navigation.createAccount({
|
||||
const accountPage = await navigation.createAccount({
|
||||
name: 'New Account',
|
||||
offBudget: false,
|
||||
balance: 100,
|
||||
@@ -39,7 +38,7 @@ test.describe('Accounts', () => {
|
||||
});
|
||||
|
||||
test('closes an account', async () => {
|
||||
accountPage = await navigation.goToAccountPage('Roth IRA');
|
||||
const accountPage = await navigation.goToAccountPage('Roth IRA');
|
||||
|
||||
await expect(accountPage.accountName).toHaveText('Roth IRA');
|
||||
|
||||
@@ -51,52 +50,4 @@ test.describe('Accounts', () => {
|
||||
await expect(accountPage.accountName).toHaveText('Closed: Roth IRA');
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test.describe('Budgeted Accounts', () => {
|
||||
// Reset filters
|
||||
test.afterEach(async () => {
|
||||
await accountPage.removeFilter(0);
|
||||
});
|
||||
|
||||
test('creates a transfer from two existing transactions', async () => {
|
||||
accountPage = await navigation.goToAccountPage('For budget');
|
||||
await expect(accountPage.accountName).toHaveText('Budgeted Accounts');
|
||||
|
||||
await accountPage.filterByNote('Test Acc Transfer');
|
||||
|
||||
await accountPage.createSingleTransaction({
|
||||
account: 'Ally Savings',
|
||||
payee: '',
|
||||
notes: 'Test Acc Transfer',
|
||||
category: 'Food',
|
||||
debit: '34.56',
|
||||
});
|
||||
|
||||
await accountPage.createSingleTransaction({
|
||||
account: 'HSBC',
|
||||
payee: '',
|
||||
notes: 'Test Acc Transfer',
|
||||
category: 'Food',
|
||||
credit: '34.56',
|
||||
});
|
||||
|
||||
await page.waitForTimeout(100); // Give time for the previous transaction to be rendered
|
||||
|
||||
await accountPage.selectNthTransaction(0);
|
||||
await accountPage.selectNthTransaction(1);
|
||||
await accountPage.clickSelectAction('Make transfer');
|
||||
|
||||
let transaction = accountPage.getNthTransaction(0);
|
||||
await expect(transaction.payee).toHaveText('Ally Savings');
|
||||
await expect(transaction.category).toHaveText('Transfer');
|
||||
await expect(transaction.credit).toHaveText('34.56');
|
||||
await expect(transaction.account).toHaveText('HSBC');
|
||||
|
||||
transaction = accountPage.getNthTransaction(1);
|
||||
await expect(transaction.payee).toHaveText('HSBC');
|
||||
await expect(transaction.category).toHaveText('Transfer');
|
||||
await expect(transaction.debit).toHaveText('34.56');
|
||||
await expect(transaction.account).toHaveText('Ally Savings');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 54 KiB |
@@ -59,9 +59,12 @@ test.describe('Budget', () => {
|
||||
});
|
||||
|
||||
test('clicking on spent amounts opens a transaction page', async () => {
|
||||
const categoryName = await budgetPage.getCategoryNameForRow(1);
|
||||
const accountPage = await budgetPage.clickOnSpentAmountForRow(1);
|
||||
expect(page.url()).toContain('/accounts');
|
||||
expect(await accountPage.accountName.textContent()).toMatch('All Accounts');
|
||||
expect(await accountPage.accountName.textContent()).toMatch(
|
||||
new RegExp(String.raw`${categoryName} \(\w+ \d+\)`),
|
||||
);
|
||||
await page.getByRole('button', { name: 'Back' }).click();
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 83 KiB |
@@ -102,22 +102,10 @@
|
||||
"name": "Store",
|
||||
"transfer_account_id": null,
|
||||
"deleted": false
|
||||
},
|
||||
{
|
||||
"id": "620e85b1-2ae7-45b1-bb3e-b875ea5c553a",
|
||||
"name": "Work",
|
||||
"transfer_account_id": null,
|
||||
"deleted": false
|
||||
}
|
||||
],
|
||||
"payee_locations": [],
|
||||
"category_groups": [
|
||||
{
|
||||
"id": "a5c355c2-3b77-4a7f-b8b3-c832b10cfec8",
|
||||
"name": "Income",
|
||||
"hidden": false,
|
||||
"deleted": false
|
||||
},
|
||||
{
|
||||
"id": "d5c355c2-3b77-4a7f-b8b3-c832b10cfec9",
|
||||
"name": "Internal Master Category",
|
||||
@@ -623,30 +611,6 @@
|
||||
"goal_overall_funded": null,
|
||||
"goal_overall_left": null,
|
||||
"deleted": false
|
||||
},
|
||||
{
|
||||
"id": "1429f287-50aa-49d8-a89c-752cbd167d6c",
|
||||
"category_group_id": "a5c355c2-3b77-4a7f-b8b3-c832b10cfec8",
|
||||
"name": "Income",
|
||||
"hidden": false,
|
||||
"original_category_group_id": null,
|
||||
"note": null,
|
||||
"budgeted": 0,
|
||||
"activity": 0,
|
||||
"balance": 0,
|
||||
"goal_type": "NEED",
|
||||
"goal_day": null,
|
||||
"goal_cadence": 1,
|
||||
"goal_cadence_frequency": 1,
|
||||
"goal_creation_month": null,
|
||||
"goal_target": 0,
|
||||
"goal_target_month": null,
|
||||
"goal_percentage_complete": null,
|
||||
"goal_months_to_budget": null,
|
||||
"goal_under_funded": null,
|
||||
"goal_overall_funded": null,
|
||||
"goal_overall_left": null,
|
||||
"deleted": false
|
||||
}
|
||||
],
|
||||
"months": [
|
||||
@@ -1633,7 +1597,7 @@
|
||||
"date": "2023-08-04",
|
||||
"amount": 0,
|
||||
"memo": "getting paid",
|
||||
"cleared": "reconciled",
|
||||
"cleared": "cleared",
|
||||
"approved": true,
|
||||
"flag_color": null,
|
||||
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
|
||||
@@ -1693,7 +1657,7 @@
|
||||
"date": "2023-08-04",
|
||||
"amount": 1000000,
|
||||
"memo": "",
|
||||
"cleared": "reconciled",
|
||||
"cleared": "cleared",
|
||||
"approved": true,
|
||||
"flag_color": null,
|
||||
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
|
||||
@@ -1740,28 +1704,8 @@
|
||||
"payee_id": "",
|
||||
"category_id": null,
|
||||
"transfer_account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
|
||||
"transfer_transaction_id": null,
|
||||
"matched_transaction_id": null,
|
||||
"import_id": null,
|
||||
"import_payee_name": null,
|
||||
"import_payee_name_original": null,
|
||||
"debt_transaction_type": null,
|
||||
"deleted": false
|
||||
},
|
||||
{
|
||||
"id": "9a22f287-f1e0-4667-9fc0-91e4a4262193",
|
||||
"date": "2024-02-02",
|
||||
"amount": 2000000,
|
||||
"memo": "Paycheck",
|
||||
"cleared": "cleared",
|
||||
"approved": true,
|
||||
"flag_color": null,
|
||||
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
|
||||
"payee_id": "620e85b1-2ae7-45b1-bb3e-b875ea5c553a",
|
||||
"category_id": "1429f287-50aa-49d8-a89c-752cbd167d6c",
|
||||
"transfer_account_id": null,
|
||||
"transfer_transaction_id": null,
|
||||
"matched_transaction_id": null,
|
||||
"transfer_transaction_id": "213526fc-ba49-4790-8a96-cc2a50182728",
|
||||
"matched_transaction_id": "",
|
||||
"import_id": null,
|
||||
"import_payee_name": null,
|
||||
"import_payee_name_original": null,
|
||||
@@ -1785,7 +1729,7 @@
|
||||
"transaction_id": "213526fc-ba49-4790-8a96-cc2a50182728",
|
||||
"amount": -50000,
|
||||
"memo": "split part b",
|
||||
"payee_id": "8d3017e0-2aa6-4fe2-b011-c53c9f147eb6",
|
||||
"payee_id": "2a20470a-634f-4efa-a7f6-f1c0b0bdda41",
|
||||
"category_id": null,
|
||||
"transfer_account_id": "125f339b-2a63-481e-84c0-f04d898905d2",
|
||||
"deleted": false
|
||||
|
||||
@@ -42,9 +42,6 @@ test.describe('Mobile', () => {
|
||||
'Mortgage',
|
||||
'Water',
|
||||
'Power',
|
||||
'Starting Balances',
|
||||
'Misc',
|
||||
'Income',
|
||||
]);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
@@ -52,7 +49,7 @@ test.describe('Mobile', () => {
|
||||
test('opens the accounts page and asserts on balances', async () => {
|
||||
const accountsPage = await navigation.goToAccountsPage();
|
||||
|
||||
const account = await accountsPage.getNthAccount(1);
|
||||
const account = await accountsPage.getNthAccount(0);
|
||||
|
||||
await expect(account.name).toHaveText('Ally Savings');
|
||||
await expect(account.balance).toHaveText('7,653.00');
|
||||
@@ -61,7 +58,7 @@ test.describe('Mobile', () => {
|
||||
|
||||
test('opens individual account page and checks that filtering is working', async () => {
|
||||
const accountsPage = await navigation.goToAccountsPage();
|
||||
const accountPage = await accountsPage.openNthAccount(0);
|
||||
const accountPage = await accountsPage.openNthAccount(1);
|
||||
|
||||
await expect(accountPage.heading).toHaveText('Bank of America');
|
||||
expect(await accountPage.getBalance()).toBeGreaterThan(0);
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 35 KiB |
@@ -59,7 +59,7 @@ test.describe('Onboarding', () => {
|
||||
await expect(budgetPage.budgetTable).toBeVisible({ timeout: 30000 });
|
||||
|
||||
const accountPage = await navigation.goToAccountPage('Checking');
|
||||
await expect(accountPage.accountBalance).toHaveText('2,600.00');
|
||||
await expect(accountPage.accountBalance).toHaveText('600.00');
|
||||
|
||||
await navigation.goToAccountPage('Saving');
|
||||
await expect(accountPage.accountBalance).toHaveText('250.00');
|
||||
|
||||
|
Before Width: | Height: | Size: 560 KiB After Width: | Height: | Size: 574 KiB |
|
Before Width: | Height: | Size: 567 KiB After Width: | Height: | Size: 649 KiB |
|
Before Width: | Height: | Size: 639 KiB |
|
Before Width: | Height: | Size: 635 KiB |
@@ -25,9 +25,6 @@ export class AccountPage {
|
||||
|
||||
this.filterButton = this.page.getByRole('button', { name: 'Filter' });
|
||||
this.filterSelectTooltip = this.page.getByTestId('filters-select-tooltip');
|
||||
|
||||
this.selectButton = this.page.getByTestId('transactions-select-button');
|
||||
this.selectTooltip = this.page.getByTestId('transactions-select-tooltip');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,21 +68,14 @@ export class AccountPage {
|
||||
await this.cancelTransactionButton.click();
|
||||
}
|
||||
|
||||
async selectNthTransaction(index) {
|
||||
const row = this.transactionTableRow.nth(index);
|
||||
await row.getByTestId('select').click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the data for the nth-transaction.
|
||||
* 0-based index
|
||||
*/
|
||||
getNthTransaction(index) {
|
||||
const row = this.transactionTableRow.nth(index);
|
||||
const account = row.getByTestId('account');
|
||||
|
||||
return {
|
||||
...(account ? { account } : {}),
|
||||
payee: row.getByTestId('payee'),
|
||||
notes: row.getByTestId('notes'),
|
||||
category: row.getByTestId('category'),
|
||||
@@ -94,11 +84,6 @@ export class AccountPage {
|
||||
};
|
||||
}
|
||||
|
||||
async clickSelectAction(action) {
|
||||
await this.selectButton.click();
|
||||
await this.selectTooltip.getByRole('button', { name: action }).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the modal for closing the account.
|
||||
*/
|
||||
@@ -121,15 +106,6 @@ export class AccountPage {
|
||||
return new FilterTooltip(this.page.getByTestId('filters-menu-tooltip'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to a specific note
|
||||
*/
|
||||
async filterByNote(note) {
|
||||
const filterTooltip = await this.filterBy('Note');
|
||||
await this.page.keyboard.type(note);
|
||||
await filterTooltip.applyButton.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the nth filter
|
||||
*/
|
||||
@@ -141,24 +117,6 @@ export class AccountPage {
|
||||
}
|
||||
|
||||
async _fillTransactionFields(transactionRow, transaction) {
|
||||
if (transaction.debit) {
|
||||
await transactionRow.getByTestId('debit').click();
|
||||
await this.page.keyboard.type(transaction.debit);
|
||||
await this.page.keyboard.press('Tab');
|
||||
}
|
||||
|
||||
if (transaction.credit) {
|
||||
await transactionRow.getByTestId('credit').click();
|
||||
await this.page.keyboard.type(transaction.credit);
|
||||
await this.page.keyboard.press('Tab');
|
||||
}
|
||||
|
||||
if (transaction.account) {
|
||||
await transactionRow.getByTestId('account').click();
|
||||
await this.page.keyboard.type(transaction.account);
|
||||
await this.page.keyboard.press('Tab');
|
||||
}
|
||||
|
||||
if (transaction.payee) {
|
||||
await transactionRow.getByTestId('payee').click();
|
||||
await this.page.keyboard.type(transaction.payee);
|
||||
@@ -181,6 +139,18 @@ export class AccountPage {
|
||||
await this.page.keyboard.press('Tab');
|
||||
}
|
||||
}
|
||||
|
||||
if (transaction.debit) {
|
||||
await transactionRow.getByTestId('debit').click();
|
||||
await this.page.keyboard.type(transaction.debit);
|
||||
await this.page.keyboard.press('Tab');
|
||||
}
|
||||
|
||||
if (transaction.credit) {
|
||||
await transactionRow.getByTestId('credit').click();
|
||||
await this.page.keyboard.type(transaction.credit);
|
||||
await this.page.keyboard.press('Tab');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ export class MobileAccountPage {
|
||||
this.page = page;
|
||||
|
||||
this.heading = page.getByRole('heading');
|
||||
this.balance = page.getByTestId('transactions-balance');
|
||||
this.balance = page.getByTestId('account-balance');
|
||||
this.noTransactionsFoundError = page.getByText('No transactions');
|
||||
this.searchBox = page.getByPlaceholder(/^Search/);
|
||||
this.transactionList = page.getByLabel('transaction list');
|
||||
|
||||
@@ -61,27 +61,6 @@ export class RulesPage {
|
||||
this.page.getByTestId('action-list'),
|
||||
);
|
||||
}
|
||||
|
||||
if (data.splits) {
|
||||
if (data.splits.beforeSplitActions) {
|
||||
await this._fillEditorFields(
|
||||
data.splits.beforeSplitActions,
|
||||
this.page.getByTestId('action-list'),
|
||||
);
|
||||
}
|
||||
|
||||
if (data.splits.splitActions) {
|
||||
let idx = data.splits?.beforeSplitActions.length ?? 0;
|
||||
for (const splitActions of data.splits.splitActions) {
|
||||
await this.page.getByTestId('add-split-transactions').click();
|
||||
await this._fillEditorFields(
|
||||
splitActions,
|
||||
this.page.getByTestId('action-list').nth(idx),
|
||||
);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _fillEditorFields(data, rootElement) {
|
||||
|
||||
@@ -6,10 +6,4 @@ export class SettingsPage {
|
||||
async exportData() {
|
||||
await this.page.getByRole('button', { name: 'Export data' }).click();
|
||||
}
|
||||
|
||||
async enableExperimentalFeature(featureName) {
|
||||
await this.page.getByTestId('advanced-settings').click();
|
||||
await this.page.getByTestId('experimental-settings').click();
|
||||
await this.page.getByLabel(featureName).check();
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 77 KiB |