Compare commits
7 Commits
redux-tool
...
react-aria
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2bf2e9cc9 | ||
|
|
7e1cc49478 | ||
|
|
e6a49b1d99 | ||
|
|
db7d890e79 | ||
|
|
46977b59ca | ||
|
|
b3d0348493 | ||
|
|
b78f1fd575 |
28
.eslintignore
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
packages/api/app/bundle.api.js
|
||||||
|
packages/api/dist
|
||||||
|
packages/api/@types
|
||||||
|
packages/api/migrations
|
||||||
|
|
||||||
|
packages/crdt/dist
|
||||||
|
|
||||||
|
packages/desktop-client/bundle.browser.js
|
||||||
|
packages/desktop-client/build/
|
||||||
|
packages/desktop-client/build-stats/
|
||||||
|
packages/desktop-client/public/kcab/
|
||||||
|
packages/desktop-client/public/data/
|
||||||
|
packages/desktop-client/**/node_modules/*
|
||||||
|
packages/desktop-client/node_modules/
|
||||||
|
packages/desktop-client/src/icons/**/*
|
||||||
|
packages/desktop-client/test-results/
|
||||||
|
packages/desktop-client/playwright-report/
|
||||||
|
|
||||||
|
packages/desktop-electron/client-build/
|
||||||
|
packages/desktop-electron/dist/
|
||||||
|
|
||||||
|
packages/import-ynab4/**/node_modules/*
|
||||||
|
|
||||||
|
packages/import-ynab5/**/node_modules/*
|
||||||
|
|
||||||
|
packages/loot-core/**/node_modules/*
|
||||||
|
packages/loot-core/**/lib-dist/*
|
||||||
|
packages/loot-core/**/proto/*
|
||||||
603
.eslintrc.js
Normal file
@@ -0,0 +1,603 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const rulesDirPlugin = require('eslint-plugin-rulesdir');
|
||||||
|
rulesDirPlugin.RULES_DIR = path.join(
|
||||||
|
__dirname,
|
||||||
|
'packages',
|
||||||
|
'eslint-plugin-actual',
|
||||||
|
'lib',
|
||||||
|
'rules',
|
||||||
|
);
|
||||||
|
|
||||||
|
const ruleFCMsg =
|
||||||
|
'Type the props argument and let TS infer or use ComponentType for a component prop';
|
||||||
|
|
||||||
|
const restrictedImportPatterns = [
|
||||||
|
{
|
||||||
|
group: ['*.api', '*.web', '*.electron'],
|
||||||
|
message: 'Don’t directly reference imports from other platforms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: ['uuid'],
|
||||||
|
importNames: ['*'],
|
||||||
|
message: "Use `import { v4 as uuidv4 } from 'uuid'` instead",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const restrictedImportColors = [
|
||||||
|
{
|
||||||
|
group: ['**/style', '**/colors'],
|
||||||
|
importNames: ['colors'],
|
||||||
|
message: 'Please use themes instead of colors',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
commonjs: true,
|
||||||
|
es6: true,
|
||||||
|
jest: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'prettier',
|
||||||
|
'import',
|
||||||
|
'rulesdir',
|
||||||
|
'@typescript-eslint',
|
||||||
|
'jsx-a11y',
|
||||||
|
'react-hooks',
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
'plugin:react/recommended',
|
||||||
|
'plugin:react/jsx-runtime',
|
||||||
|
'plugin:prettier/recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:import/typescript',
|
||||||
|
],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: { project: [path.join(__dirname, './tsconfig.json')] },
|
||||||
|
reportUnusedDisableDirectives: true,
|
||||||
|
globals: {
|
||||||
|
globalThis: false,
|
||||||
|
vi: true,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// http://eslint.org/docs/rules/
|
||||||
|
'array-callback-return': 'warn',
|
||||||
|
'default-case': ['warn', { commentPattern: '^no default$' }],
|
||||||
|
'dot-location': ['warn', 'property'],
|
||||||
|
eqeqeq: ['warn', 'smart'],
|
||||||
|
'new-parens': 'warn',
|
||||||
|
'no-array-constructor': 'warn',
|
||||||
|
'no-caller': 'warn',
|
||||||
|
'no-cond-assign': ['warn', 'except-parens'],
|
||||||
|
'no-const-assign': 'warn',
|
||||||
|
'no-control-regex': 'warn',
|
||||||
|
'no-delete-var': 'warn',
|
||||||
|
'no-dupe-args': 'warn',
|
||||||
|
'no-dupe-class-members': 'warn',
|
||||||
|
'no-dupe-keys': 'warn',
|
||||||
|
'no-duplicate-case': 'warn',
|
||||||
|
'no-empty-character-class': 'warn',
|
||||||
|
'no-empty-pattern': 'warn',
|
||||||
|
'no-eval': 'warn',
|
||||||
|
'no-ex-assign': 'warn',
|
||||||
|
'no-extend-native': 'warn',
|
||||||
|
'no-extra-bind': 'warn',
|
||||||
|
'no-extra-label': 'warn',
|
||||||
|
'no-fallthrough': 'warn',
|
||||||
|
'no-func-assign': 'warn',
|
||||||
|
'no-implied-eval': 'warn',
|
||||||
|
'no-invalid-regexp': 'warn',
|
||||||
|
'no-iterator': 'warn',
|
||||||
|
'no-label-var': 'warn',
|
||||||
|
'no-labels': ['warn', { allowLoop: true, allowSwitch: false }],
|
||||||
|
'no-lone-blocks': 'warn',
|
||||||
|
'no-mixed-operators': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
groups: [
|
||||||
|
['&', '|', '^', '~', '<<', '>>', '>>>'],
|
||||||
|
['==', '!=', '===', '!==', '>', '>=', '<', '<='],
|
||||||
|
['&&', '||'],
|
||||||
|
['in', 'instanceof'],
|
||||||
|
],
|
||||||
|
allowSamePrecedence: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-multi-str': 'warn',
|
||||||
|
'no-global-assign': 'warn',
|
||||||
|
'no-unsafe-negation': 'warn',
|
||||||
|
'no-new-func': 'warn',
|
||||||
|
'no-new-object': 'warn',
|
||||||
|
'no-new-symbol': 'warn',
|
||||||
|
'no-new-wrappers': 'warn',
|
||||||
|
'no-obj-calls': 'warn',
|
||||||
|
'no-octal': 'warn',
|
||||||
|
'no-octal-escape': 'warn',
|
||||||
|
'no-redeclare': 'warn',
|
||||||
|
'no-regex-spaces': 'warn',
|
||||||
|
'no-script-url': 'warn',
|
||||||
|
'no-self-assign': 'warn',
|
||||||
|
'no-self-compare': 'warn',
|
||||||
|
'no-sequences': 'warn',
|
||||||
|
'no-shadow-restricted-names': 'warn',
|
||||||
|
'no-sparse-arrays': 'warn',
|
||||||
|
'no-template-curly-in-string': 'warn',
|
||||||
|
'no-this-before-super': 'warn',
|
||||||
|
'no-throw-literal': 'warn',
|
||||||
|
'no-undef': 'error',
|
||||||
|
'no-unreachable': 'warn',
|
||||||
|
'no-unused-expressions': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
allowShortCircuit: true,
|
||||||
|
allowTernary: true,
|
||||||
|
allowTaggedTemplates: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-unused-labels': 'warn',
|
||||||
|
'no-use-before-define': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
functions: false,
|
||||||
|
classes: false,
|
||||||
|
variables: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-useless-computed-key': 'warn',
|
||||||
|
'no-useless-concat': 'warn',
|
||||||
|
'no-useless-constructor': 'warn',
|
||||||
|
'no-useless-escape': 'warn',
|
||||||
|
'no-useless-rename': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
ignoreDestructuring: false,
|
||||||
|
ignoreImport: false,
|
||||||
|
ignoreExport: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-with': 'warn',
|
||||||
|
'no-whitespace-before-property': 'warn',
|
||||||
|
'react-hooks/exhaustive-deps': 'warn',
|
||||||
|
'require-yield': 'warn',
|
||||||
|
'rest-spread-spacing': ['warn', 'never'],
|
||||||
|
strict: ['warn', 'never'],
|
||||||
|
'unicode-bom': ['warn', 'never'],
|
||||||
|
'use-isnan': 'warn',
|
||||||
|
'valid-typeof': 'warn',
|
||||||
|
'no-restricted-properties': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
object: 'require',
|
||||||
|
property: 'ensure',
|
||||||
|
message:
|
||||||
|
'Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: 'System',
|
||||||
|
property: 'import',
|
||||||
|
message:
|
||||||
|
'Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'getter-return': 'warn',
|
||||||
|
|
||||||
|
// https://github.com/benmosher/eslint-plugin-import/tree/master/docs/rules
|
||||||
|
'import/first': 'error',
|
||||||
|
'import/no-amd': 'error',
|
||||||
|
'import/no-anonymous-default-export': 'warn',
|
||||||
|
'import/no-webpack-loader-syntax': 'error',
|
||||||
|
|
||||||
|
// https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules
|
||||||
|
'react/forbid-foreign-prop-types': ['warn', { allowInPropTypes: true }],
|
||||||
|
'react/jsx-no-comment-textnodes': 'warn',
|
||||||
|
'react/jsx-no-duplicate-props': 'warn',
|
||||||
|
'react/jsx-no-target-blank': 'warn',
|
||||||
|
'react/jsx-no-undef': 'error',
|
||||||
|
'react/jsx-pascal-case': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
allowAllCaps: true,
|
||||||
|
ignore: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'react/no-danger-with-children': 'warn',
|
||||||
|
// Disabled because of undesirable warnings
|
||||||
|
// See https://github.com/facebook/create-react-app/issues/5204 for
|
||||||
|
// blockers until its re-enabled
|
||||||
|
// 'react/no-deprecated': 'warn',
|
||||||
|
'react/no-direct-mutation-state': 'warn',
|
||||||
|
'react/no-is-mounted': 'warn',
|
||||||
|
'react/no-typos': 'error',
|
||||||
|
'react/require-render-return': 'error',
|
||||||
|
'react/style-prop-object': 'warn',
|
||||||
|
|
||||||
|
// https://github.com/evcohen/eslint-plugin-jsx-a11y/tree/master/docs/rules
|
||||||
|
'jsx-a11y/alt-text': 'warn',
|
||||||
|
'jsx-a11y/anchor-has-content': 'warn',
|
||||||
|
'jsx-a11y/anchor-is-valid': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
aspects: ['noHref', 'invalidHref'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'jsx-a11y/aria-activedescendant-has-tabindex': 'warn',
|
||||||
|
'jsx-a11y/aria-props': 'warn',
|
||||||
|
'jsx-a11y/aria-proptypes': 'warn',
|
||||||
|
'jsx-a11y/aria-role': ['warn', { ignoreNonDOM: true }],
|
||||||
|
'jsx-a11y/aria-unsupported-elements': 'warn',
|
||||||
|
'jsx-a11y/heading-has-content': 'warn',
|
||||||
|
'jsx-a11y/iframe-has-title': 'warn',
|
||||||
|
'jsx-a11y/img-redundant-alt': 'warn',
|
||||||
|
'jsx-a11y/no-access-key': 'warn',
|
||||||
|
'jsx-a11y/no-distracting-elements': 'warn',
|
||||||
|
'jsx-a11y/no-redundant-roles': 'warn',
|
||||||
|
'jsx-a11y/role-has-required-aria-props': 'warn',
|
||||||
|
'jsx-a11y/role-supports-aria-props': 'warn',
|
||||||
|
'jsx-a11y/scope': 'warn',
|
||||||
|
|
||||||
|
// https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
|
||||||
|
'prettier/prettier': 'warn',
|
||||||
|
|
||||||
|
// Note: base rule explicitly disabled in favor of the TS one
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
varsIgnorePattern: '^(_|React)',
|
||||||
|
ignoreRestSiblings: true,
|
||||||
|
caughtErrors: 'none',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
curly: ['warn', 'multi-line', 'consistent'],
|
||||||
|
|
||||||
|
'no-restricted-globals': ['warn'].concat(
|
||||||
|
require('confusing-browser-globals').filter(g => g !== 'self'),
|
||||||
|
),
|
||||||
|
|
||||||
|
'react/jsx-filename-extension': [
|
||||||
|
'warn',
|
||||||
|
{ extensions: ['.jsx', '.tsx'], allow: 'as-needed' },
|
||||||
|
],
|
||||||
|
'react/jsx-no-useless-fragment': 'warn',
|
||||||
|
'react/self-closing-comp': 'warn',
|
||||||
|
'react/no-unstable-nested-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowAsProps: true, customValidators: ['formatter'] },
|
||||||
|
],
|
||||||
|
|
||||||
|
'rulesdir/typography': 'warn',
|
||||||
|
'rulesdir/prefer-if-statement': 'warn',
|
||||||
|
|
||||||
|
// https://github.com/eslint/eslint/issues/16954
|
||||||
|
// https://github.com/eslint/eslint/issues/16953
|
||||||
|
'no-loop-func': 'off',
|
||||||
|
|
||||||
|
// Do don't need this as we're using TypeScript
|
||||||
|
'react/prop-types': 'off',
|
||||||
|
|
||||||
|
// TODO: re-enable these rules
|
||||||
|
'react/react-in-jsx-scope': 'off',
|
||||||
|
|
||||||
|
'no-var': 'warn',
|
||||||
|
'react/jsx-curly-brace-presence': 'warn',
|
||||||
|
'object-shorthand': ['warn', 'properties'],
|
||||||
|
|
||||||
|
'import/extensions': [
|
||||||
|
'warn',
|
||||||
|
'never',
|
||||||
|
{
|
||||||
|
json: 'always',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'import/no-useless-path-segments': 'warn',
|
||||||
|
'import/no-duplicates': ['warn', { 'prefer-inline': true }],
|
||||||
|
'import/no-unused-modules': ['warn', { unusedExports: true }],
|
||||||
|
'import/order': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
alphabetize: {
|
||||||
|
caseInsensitive: true,
|
||||||
|
order: 'asc',
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
'builtin', // Built-in types are first
|
||||||
|
'external',
|
||||||
|
'parent',
|
||||||
|
'sibling',
|
||||||
|
'index', // Then the index file
|
||||||
|
],
|
||||||
|
'newlines-between': 'always',
|
||||||
|
pathGroups: [
|
||||||
|
// Enforce that React (and react-related packages) is the first import
|
||||||
|
{ group: 'builtin', pattern: 'react?(-*)', position: 'before' },
|
||||||
|
// Separate imports from Actual from "real" external imports
|
||||||
|
{
|
||||||
|
group: 'external',
|
||||||
|
pattern: 'loot-{core,design}/**/*',
|
||||||
|
position: 'after',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pathGroupsExcludedImportTypes: ['react'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
'no-restricted-syntax': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
// forbid React.* as they are legacy https://twitter.com/dan_abramov/status/1308739731551858689
|
||||||
|
selector:
|
||||||
|
":matches(MemberExpression[object.name='React'], TSQualifiedName[left.name='React'])",
|
||||||
|
message:
|
||||||
|
'Using default React import is discouraged, please use named exports directly instead.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// forbid <a> in favor of <Link>
|
||||||
|
selector: 'JSXOpeningElement[name.name="a"]',
|
||||||
|
message: 'Using <a> is discouraged, please use <Link> instead.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-restricted-imports': [
|
||||||
|
'warn',
|
||||||
|
{ patterns: [...restrictedImportPatterns, ...restrictedImportColors] },
|
||||||
|
],
|
||||||
|
|
||||||
|
'@typescript-eslint/ban-ts-comment': [
|
||||||
|
'error',
|
||||||
|
{ 'ts-ignore': 'allow-with-description' },
|
||||||
|
],
|
||||||
|
|
||||||
|
// Rules disable during TS migration
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'prefer-const': 'warn',
|
||||||
|
'prefer-spread': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
|
'import/no-default-export': 'warn',
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['**/*.ts?(x)'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2018,
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// typescript-eslint specific options
|
||||||
|
warnOnUnsupportedTypeScriptVersion: true,
|
||||||
|
},
|
||||||
|
plugins: ['@typescript-eslint'],
|
||||||
|
// If adding a typescript-eslint version of an existing ESLint rule,
|
||||||
|
// make sure to disable the ESLint rule here.
|
||||||
|
rules: {
|
||||||
|
// TypeScript's `noFallthroughCasesInSwitch` option is more robust (#6906)
|
||||||
|
'default-case': 'off',
|
||||||
|
// 'tsc' already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/291)
|
||||||
|
'no-dupe-class-members': 'off',
|
||||||
|
// 'tsc' already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/477)
|
||||||
|
'no-undef': 'off',
|
||||||
|
|
||||||
|
// Add TypeScript specific rules (and turn off ESLint equivalents)
|
||||||
|
'@typescript-eslint/consistent-type-assertions': 'warn',
|
||||||
|
'no-array-constructor': 'off',
|
||||||
|
'@typescript-eslint/no-array-constructor': 'warn',
|
||||||
|
'no-redeclare': 'off',
|
||||||
|
'@typescript-eslint/no-redeclare': 'warn',
|
||||||
|
'no-use-before-define': 'off',
|
||||||
|
'@typescript-eslint/no-use-before-define': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
functions: false,
|
||||||
|
classes: false,
|
||||||
|
variables: false,
|
||||||
|
typedefs: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-unused-expressions': 'off',
|
||||||
|
'@typescript-eslint/no-unused-expressions': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
allowShortCircuit: true,
|
||||||
|
allowTernary: true,
|
||||||
|
allowTaggedTemplates: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-useless-constructor': 'off',
|
||||||
|
'@typescript-eslint/no-useless-constructor': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['.eslintrc.js', './**/.eslintrc.js'],
|
||||||
|
parserOptions: { project: null },
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/consistent-type-exports': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'./packages/desktop-client/**/*.{ts,tsx}',
|
||||||
|
'./packages/loot-core/src/client/**/*.{ts,tsx}',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
// enforce type over interface
|
||||||
|
'@typescript-eslint/consistent-type-definitions': ['warn', 'type'],
|
||||||
|
// enforce import type
|
||||||
|
'@typescript-eslint/consistent-type-imports': [
|
||||||
|
'warn',
|
||||||
|
{ prefer: 'type-imports', fixStyle: 'inline-type-imports' },
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-restricted-types': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
types: {
|
||||||
|
// forbid FC as superflous
|
||||||
|
FunctionComponent: { message: ruleFCMsg },
|
||||||
|
FC: { message: ruleFCMsg },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['./packages/desktop-client/**/*'],
|
||||||
|
excludedFiles: [
|
||||||
|
'./packages/desktop-client/src/hooks/useNavigate.{ts,tsx}',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'no-restricted-imports': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
group: ['react-router-dom'],
|
||||||
|
importNames: ['useNavigate'],
|
||||||
|
message: 'Please use Actual’s useNavigate() hook instead.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['./packages/loot-core/src/**/*'],
|
||||||
|
rules: {
|
||||||
|
'no-restricted-imports': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
patterns: [
|
||||||
|
...restrictedImportPatterns,
|
||||||
|
{
|
||||||
|
group: ['loot-core/**'],
|
||||||
|
message:
|
||||||
|
'Please use relative imports in loot-core instead of importing from `loot-core/*`',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'packages/loot-core/src/types/**/*',
|
||||||
|
'packages/loot-core/src/client/state-types/**/*',
|
||||||
|
'**/icons/**/*',
|
||||||
|
'**/{mocks,__mocks__}/**/*',
|
||||||
|
// can't correctly resolve usages
|
||||||
|
'**/*.{testing,electron,browser,web,api}.ts',
|
||||||
|
],
|
||||||
|
rules: { 'import/no-unused-modules': 'off' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'./packages/desktop-client/src/style/index.*',
|
||||||
|
'./packages/desktop-client/src/style/palette.*',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'no-restricted-imports': ['off', { patterns: restrictedImportColors }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'./packages/api/migrations/*',
|
||||||
|
'./packages/loot-core/migrations/*',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'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/envelope/HoldMenu.tsx',
|
||||||
|
'./packages/desktop-client/src/components/budget/envelope/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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'.eslintrc.js',
|
||||||
|
'*.test.js',
|
||||||
|
'*.test.ts',
|
||||||
|
'*.test.jsx',
|
||||||
|
'*.test.tsx',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'rulesdir/typography': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
'import/resolver': {
|
||||||
|
typescript: {
|
||||||
|
alwaysTryTypes: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
10
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -23,6 +23,8 @@ body:
|
|||||||
options:
|
options:
|
||||||
- label: 'I have searched and found no existing issue'
|
- label: 'I have searched and found no existing issue'
|
||||||
required: true
|
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:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@@ -34,14 +36,6 @@ body:
|
|||||||
value: 'A bug happened!'
|
value: 'A bug happened!'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
|
||||||
id: reproduction
|
|
||||||
attributes:
|
|
||||||
label: How can we reproduce the issue?
|
|
||||||
description: Please give step-by-step instructions on how to reproduce the issue. In most cases this might also require uploading a sample budget/import file.
|
|
||||||
value: 'How can we reproduce the issue?'
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: markdown
|
- type: markdown
|
||||||
id: env-info
|
id: env-info
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
4
.github/actions/setup/action.yml
vendored
@@ -7,10 +7,6 @@ runs:
|
|||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18.16.0
|
node-version: 18.16.0
|
||||||
- name: Install yarn
|
|
||||||
run: npm install -g yarn
|
|
||||||
shell: bash
|
|
||||||
if: ${{ env.ACT }}
|
|
||||||
- name: Cache
|
- name: Cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
id: cache
|
id: cache
|
||||||
|
|||||||
1
.github/workflows/electron-master.yml
vendored
@@ -80,7 +80,6 @@ jobs:
|
|||||||
- name: Add to Release
|
- name: Add to Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
draft: true
|
|
||||||
files: |
|
files: |
|
||||||
packages/desktop-electron/dist/*.dmg
|
packages/desktop-electron/dist/*.dmg
|
||||||
packages/desktop-electron/dist/*.exe
|
packages/desktop-electron/dist/*.exe
|
||||||
|
|||||||
36
.github/workflows/i18n-string-extract-master.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: Extract and upload i18n strings
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
extract-and-upload-i18n-strings:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up environment
|
|
||||||
uses: ./.github/actions/setup
|
|
||||||
- name: Configure i18n client
|
|
||||||
run: |
|
|
||||||
pip install wlc
|
|
||||||
- name: Generate i18n strings
|
|
||||||
run: yarn generate:i18n
|
|
||||||
- name: Upload i18n strings
|
|
||||||
run: |
|
|
||||||
if [[ ! -f packages/desktop-client/locale/en.json ]]; then
|
|
||||||
echo "File packages/desktop-client/locale/en.json not found. Ensure the file was generated correctly."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
wlc \
|
|
||||||
--url https://hosted.weblate.org/api/ \
|
|
||||||
--key "${{ secrets.WEBLATE_API_KEY_CI_STRINGS }}" \
|
|
||||||
upload \
|
|
||||||
--author-name "Actual Budget" \
|
|
||||||
--author-email "dev@actualbudget.org" \
|
|
||||||
--method add \
|
|
||||||
--input packages/desktop-client/locale/en.json \
|
|
||||||
actualbudget/actual/en
|
|
||||||
echo "Translations uploaded"
|
|
||||||
66
.github/workflows/update-vrt.yml
vendored
@@ -4,11 +4,11 @@ on:
|
|||||||
types: [ created ]
|
types: [ created ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: read
|
pull-requests: write
|
||||||
contents: read
|
contents: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.issue.number }}-${{ contains(github.event.comment.body, '/update-vrt') }}
|
group: ${{ github.workflow }}-${{ github.event.issue.number }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -41,48 +41,11 @@ jobs:
|
|||||||
run: yarn vrt --update-snapshots
|
run: yarn vrt --update-snapshots
|
||||||
env:
|
env:
|
||||||
E2E_START_URL: ${{ steps.netlify.outputs.url }}
|
E2E_START_URL: ${{ steps.netlify.outputs.url }}
|
||||||
- name: Create patch
|
- name: Commit and push changes
|
||||||
run: |
|
run: |
|
||||||
git config --system --add safe.directory "*"
|
git config --system --add safe.directory "*"
|
||||||
git config --global user.name "github-actions[bot]"
|
git config --global user.name "github-actions[bot]"
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
git reset
|
|
||||||
git add "**/*.png"
|
|
||||||
if git diff --staged --quiet; then
|
|
||||||
echo "No changes to commit"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
git commit -m "Update VRT"
|
|
||||||
git format-patch -1 HEAD --stdout > Update-VRT.patch
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: patch
|
|
||||||
path: Update-VRT.patch
|
|
||||||
|
|
||||||
push-patch:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: update-vrt
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- name: Get PR branch
|
|
||||||
# Until https://github.com/xt0rted/pull-request-comment-branch/issues/322 is resolved we use the forked version
|
|
||||||
uses: gotson/pull-request-comment-branch@head-repo-owner-dist
|
|
||||||
id: comment-branch
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: ${{ steps.comment-branch.outputs.head_owner }}/${{ steps.comment-branch.outputs.head_repo }}
|
|
||||||
ref: ${{ steps.comment-branch.outputs.head_ref }}
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
name: patch
|
|
||||||
- name: Apply patch and push
|
|
||||||
run: |
|
|
||||||
git config --global user.name "github-actions[bot]"
|
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git apply Update-VRT.patch
|
|
||||||
git add "**/*.png"
|
git add "**/*.png"
|
||||||
if git diff --staged --quiet; then
|
if git diff --staged --quiet; then
|
||||||
echo "No changes to commit"
|
echo "No changes to commit"
|
||||||
@@ -90,24 +53,3 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
git commit -m "Update VRT"
|
git commit -m "Update VRT"
|
||||||
git push origin HEAD:${{ steps.comment-branch.outputs.head_ref }}
|
git push origin HEAD:${{ steps.comment-branch.outputs.head_ref }}
|
||||||
- name: Add finished reaction
|
|
||||||
uses: dkershner6/reaction-action@v2
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
commentId: ${{ github.event.comment.id }}
|
|
||||||
reaction: "rocket"
|
|
||||||
|
|
||||||
add-starting-reaction:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: |
|
|
||||||
github.event.issue.pull_request &&
|
|
||||||
contains(github.event.comment.body, '/update-vrt')
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- name: React to comment
|
|
||||||
uses: dkershner6/reaction-action@v2
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
commentId: ${{ github.event.comment.id }}
|
|
||||||
reaction: "+1"
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ fi
|
|||||||
|
|
||||||
yarn workspace loot-core build:node
|
yarn workspace loot-core build:node
|
||||||
|
|
||||||
yarn workspace @actual-app/web build --mode=desktop # electron specific build
|
yarn workspace @actual-app/web build --mode=desktop
|
||||||
|
|
||||||
yarn workspace desktop-electron update-client
|
yarn workspace desktop-electron update-client
|
||||||
|
|
||||||
|
|||||||
@@ -1,789 +0,0 @@
|
|||||||
import path from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
|
|
||||||
import globals from 'globals';
|
|
||||||
|
|
||||||
import pluginImport from 'eslint-plugin-import';
|
|
||||||
import pluginJSXA11y from 'eslint-plugin-jsx-a11y';
|
|
||||||
import pluginPrettier from 'eslint-plugin-prettier/recommended';
|
|
||||||
import pluginReact from 'eslint-plugin-react';
|
|
||||||
import pluginReactHooks from 'eslint-plugin-react-hooks';
|
|
||||||
import pluginRulesDir from 'eslint-plugin-rulesdir';
|
|
||||||
import pluginTypescript from 'typescript-eslint';
|
|
||||||
|
|
||||||
import tsParser from '@typescript-eslint/parser';
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
|
|
||||||
pluginRulesDir.RULES_DIR = path.join(
|
|
||||||
__dirname,
|
|
||||||
'packages',
|
|
||||||
'eslint-plugin-actual',
|
|
||||||
'lib',
|
|
||||||
'rules',
|
|
||||||
);
|
|
||||||
|
|
||||||
const confusingBrowserGlobals = [
|
|
||||||
// https://github.com/facebook/create-react-app/tree/main/packages/confusing-browser-globals
|
|
||||||
'addEventListener',
|
|
||||||
'blur',
|
|
||||||
'close',
|
|
||||||
'closed',
|
|
||||||
'confirm',
|
|
||||||
'defaultStatus',
|
|
||||||
'defaultstatus',
|
|
||||||
'event',
|
|
||||||
'external',
|
|
||||||
'find',
|
|
||||||
'focus',
|
|
||||||
'frameElement',
|
|
||||||
'frames',
|
|
||||||
'history',
|
|
||||||
'innerHeight',
|
|
||||||
'innerWidth',
|
|
||||||
'length',
|
|
||||||
'location',
|
|
||||||
'locationbar',
|
|
||||||
'menubar',
|
|
||||||
'moveBy',
|
|
||||||
'moveTo',
|
|
||||||
'name',
|
|
||||||
'onblur',
|
|
||||||
'onerror',
|
|
||||||
'onfocus',
|
|
||||||
'onload',
|
|
||||||
'onresize',
|
|
||||||
'onunload',
|
|
||||||
'open',
|
|
||||||
'opener',
|
|
||||||
'opera',
|
|
||||||
'outerHeight',
|
|
||||||
'outerWidth',
|
|
||||||
'pageXOffset',
|
|
||||||
'pageYOffset',
|
|
||||||
'parent',
|
|
||||||
'print',
|
|
||||||
'removeEventListener',
|
|
||||||
'resizeBy',
|
|
||||||
'resizeTo',
|
|
||||||
'screen',
|
|
||||||
'screenLeft',
|
|
||||||
'screenTop',
|
|
||||||
'screenX',
|
|
||||||
'screenY',
|
|
||||||
'scroll',
|
|
||||||
'scrollbars',
|
|
||||||
'scrollBy',
|
|
||||||
'scrollTo',
|
|
||||||
'scrollX',
|
|
||||||
'scrollY',
|
|
||||||
'status',
|
|
||||||
'statusbar',
|
|
||||||
'stop',
|
|
||||||
'toolbar',
|
|
||||||
'top',
|
|
||||||
];
|
|
||||||
|
|
||||||
/** @type {import('eslint').Linter.Config[]} */
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
ignores: [
|
|
||||||
'packages/api/app/bundle.api.js',
|
|
||||||
'packages/api/dist',
|
|
||||||
'packages/api/@types',
|
|
||||||
'packages/api/migrations',
|
|
||||||
'packages/crdt/dist',
|
|
||||||
'packages/desktop-client/bundle.browser.js',
|
|
||||||
'packages/desktop-client/build/',
|
|
||||||
'packages/desktop-client/build-electron/',
|
|
||||||
'packages/desktop-client/build-stats/',
|
|
||||||
'packages/desktop-client/public/kcab/',
|
|
||||||
'packages/desktop-client/public/data/',
|
|
||||||
'packages/desktop-client/**/node_modules/*',
|
|
||||||
'packages/desktop-client/node_modules/',
|
|
||||||
'packages/desktop-client/src/icons/**/*',
|
|
||||||
'packages/desktop-client/test-results/',
|
|
||||||
'packages/desktop-client/playwright-report/',
|
|
||||||
'packages/desktop-electron/client-build/',
|
|
||||||
'packages/desktop-electron/build/',
|
|
||||||
'packages/desktop-electron/dist/',
|
|
||||||
'packages/import-ynab4/**/node_modules/*',
|
|
||||||
'packages/import-ynab5/**/node_modules/*',
|
|
||||||
'packages/loot-core/**/node_modules/*',
|
|
||||||
'packages/loot-core/**/lib-dist/*',
|
|
||||||
'packages/loot-core/**/proto/*',
|
|
||||||
'.yarn/*',
|
|
||||||
'.github/*',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
linterOptions: {
|
|
||||||
reportUnusedDisableDirectives: true,
|
|
||||||
},
|
|
||||||
languageOptions: {
|
|
||||||
globals: {
|
|
||||||
...globals.browser,
|
|
||||||
...globals.commonjs,
|
|
||||||
...globals.jest,
|
|
||||||
...globals.node,
|
|
||||||
globalThis: false,
|
|
||||||
vi: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
react: {
|
|
||||||
version: 'detect',
|
|
||||||
},
|
|
||||||
|
|
||||||
'import/resolver': {
|
|
||||||
typescript: {
|
|
||||||
alwaysTryTypes: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pluginReact.configs.flat.recommended,
|
|
||||||
pluginReact.configs.flat['jsx-runtime'],
|
|
||||||
pluginPrettier,
|
|
||||||
...pluginTypescript.configs.recommended,
|
|
||||||
pluginImport.flatConfigs.recommended,
|
|
||||||
{
|
|
||||||
plugins: {
|
|
||||||
'react-hooks': pluginReactHooks,
|
|
||||||
'jsx-a11y': pluginJSXA11y,
|
|
||||||
rulesdir: pluginRulesDir,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.{js,ts,jsx,tsx}'],
|
|
||||||
rules: {
|
|
||||||
// http://eslint.org/docs/rules/
|
|
||||||
'array-callback-return': 'warn',
|
|
||||||
|
|
||||||
'default-case': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
commentPattern: '^no default$',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
curly: ['warn', 'multi-line', 'consistent'],
|
|
||||||
'dot-location': ['warn', 'property'],
|
|
||||||
eqeqeq: ['warn', 'smart'],
|
|
||||||
'new-parens': 'warn',
|
|
||||||
'no-array-constructor': 'warn',
|
|
||||||
'no-caller': 'warn',
|
|
||||||
'no-cond-assign': ['warn', 'except-parens'],
|
|
||||||
'no-const-assign': 'warn',
|
|
||||||
'no-control-regex': 'warn',
|
|
||||||
'no-delete-var': 'warn',
|
|
||||||
'no-dupe-args': 'warn',
|
|
||||||
'no-dupe-class-members': 'warn',
|
|
||||||
'no-dupe-keys': 'warn',
|
|
||||||
'no-duplicate-case': 'warn',
|
|
||||||
'no-empty-character-class': 'warn',
|
|
||||||
'no-empty-pattern': 'warn',
|
|
||||||
'no-eval': 'warn',
|
|
||||||
'no-ex-assign': 'warn',
|
|
||||||
'no-extend-native': 'warn',
|
|
||||||
'no-extra-bind': 'warn',
|
|
||||||
'no-extra-label': 'warn',
|
|
||||||
'no-fallthrough': 'warn',
|
|
||||||
'no-func-assign': 'warn',
|
|
||||||
'no-implied-eval': 'warn',
|
|
||||||
'no-invalid-regexp': 'warn',
|
|
||||||
'no-iterator': 'warn',
|
|
||||||
'no-label-var': 'warn',
|
|
||||||
|
|
||||||
'no-labels': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
allowLoop: true,
|
|
||||||
allowSwitch: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'no-lone-blocks': 'warn',
|
|
||||||
|
|
||||||
'no-mixed-operators': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
groups: [
|
|
||||||
['&', '|', '^', '~', '<<', '>>', '>>>'],
|
|
||||||
['==', '!=', '===', '!==', '>', '>=', '<', '<='],
|
|
||||||
['&&', '||'],
|
|
||||||
['in', 'instanceof'],
|
|
||||||
],
|
|
||||||
|
|
||||||
allowSamePrecedence: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'no-multi-str': 'warn',
|
|
||||||
'no-global-assign': 'warn',
|
|
||||||
'no-unsafe-negation': 'warn',
|
|
||||||
'no-new-func': 'warn',
|
|
||||||
'no-new-object': 'warn',
|
|
||||||
'no-new-symbol': 'warn',
|
|
||||||
'no-new-wrappers': 'warn',
|
|
||||||
'no-obj-calls': 'warn',
|
|
||||||
'no-octal': 'warn',
|
|
||||||
'no-octal-escape': 'warn',
|
|
||||||
'no-redeclare': 'warn',
|
|
||||||
'no-regex-spaces': 'warn',
|
|
||||||
'no-script-url': 'warn',
|
|
||||||
'no-self-assign': 'warn',
|
|
||||||
'no-self-compare': 'warn',
|
|
||||||
'no-sequences': 'warn',
|
|
||||||
'no-shadow-restricted-names': 'warn',
|
|
||||||
'no-sparse-arrays': 'warn',
|
|
||||||
'no-template-curly-in-string': 'warn',
|
|
||||||
'no-this-before-super': 'warn',
|
|
||||||
'no-throw-literal': 'warn',
|
|
||||||
'no-undef': 'error',
|
|
||||||
'no-unreachable': 'warn',
|
|
||||||
|
|
||||||
'no-unused-expressions': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
allowShortCircuit: true,
|
|
||||||
allowTernary: true,
|
|
||||||
allowTaggedTemplates: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'no-unused-labels': 'warn',
|
|
||||||
|
|
||||||
'no-use-before-define': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
functions: false,
|
|
||||||
classes: false,
|
|
||||||
variables: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'no-useless-computed-key': 'warn',
|
|
||||||
'no-useless-concat': 'warn',
|
|
||||||
'no-useless-constructor': 'warn',
|
|
||||||
'no-useless-escape': 'warn',
|
|
||||||
|
|
||||||
'no-useless-rename': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
ignoreDestructuring: false,
|
|
||||||
ignoreImport: false,
|
|
||||||
ignoreExport: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'no-with': 'warn',
|
|
||||||
'no-whitespace-before-property': 'warn',
|
|
||||||
|
|
||||||
'require-yield': 'warn',
|
|
||||||
'rest-spread-spacing': ['warn', 'never'],
|
|
||||||
strict: ['warn', 'never'],
|
|
||||||
'unicode-bom': ['warn', 'never'],
|
|
||||||
'use-isnan': 'warn',
|
|
||||||
'valid-typeof': 'warn',
|
|
||||||
|
|
||||||
'no-restricted-properties': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
object: 'require',
|
|
||||||
property: 'ensure',
|
|
||||||
message:
|
|
||||||
'Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
object: 'System',
|
|
||||||
property: 'import',
|
|
||||||
message:
|
|
||||||
'Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'getter-return': 'warn',
|
|
||||||
|
|
||||||
// https://github.com/benmosher/eslint-plugin-import/tree/master/docs/rules
|
|
||||||
'import/first': 'error',
|
|
||||||
'import/no-amd': 'error',
|
|
||||||
'import/no-anonymous-default-export': 'warn',
|
|
||||||
'import/no-webpack-loader-syntax': 'error',
|
|
||||||
'import/extensions': [
|
|
||||||
'warn',
|
|
||||||
'never',
|
|
||||||
{
|
|
||||||
json: 'always',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/no-useless-path-segments': 'warn',
|
|
||||||
'import/no-duplicates': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
'prefer-inline': true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/order': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
alphabetize: {
|
|
||||||
caseInsensitive: true,
|
|
||||||
order: 'asc',
|
|
||||||
},
|
|
||||||
|
|
||||||
groups: ['builtin', 'external', 'parent', 'sibling', 'index'],
|
|
||||||
'newlines-between': 'always',
|
|
||||||
|
|
||||||
pathGroups: [
|
|
||||||
{
|
|
||||||
// Enforce that React (and react-related packages) is the first import
|
|
||||||
group: 'builtin',
|
|
||||||
pattern: 'react?(-*)',
|
|
||||||
position: 'before',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Separate imports from Actual from "real" external imports
|
|
||||||
group: 'external',
|
|
||||||
pattern: 'loot-{core,design}/**/*',
|
|
||||||
position: 'after',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
pathGroupsExcludedImportTypes: ['react'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
// https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules
|
|
||||||
'react/forbid-foreign-prop-types': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
allowInPropTypes: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'react/jsx-no-comment-textnodes': 'warn',
|
|
||||||
'react/jsx-no-duplicate-props': 'warn',
|
|
||||||
'react/jsx-no-target-blank': 'warn',
|
|
||||||
'react/jsx-no-undef': 'error',
|
|
||||||
'react/jsx-pascal-case': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
allowAllCaps: true,
|
|
||||||
ignore: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'react/no-danger-with-children': 'warn',
|
|
||||||
// Disabled because of undesirable warnings
|
|
||||||
// See https://github.com/facebook/create-react-app/issues/5204 for
|
|
||||||
// blockers until its re-enabled
|
|
||||||
// 'react/no-deprecated': 'warn',
|
|
||||||
'react/no-direct-mutation-state': 'warn',
|
|
||||||
'react/no-is-mounted': 'warn',
|
|
||||||
'react/no-typos': 'error',
|
|
||||||
'react/require-render-return': 'error',
|
|
||||||
'react/style-prop-object': 'warn',
|
|
||||||
'react/jsx-no-useless-fragment': 'warn',
|
|
||||||
'react/self-closing-comp': 'warn',
|
|
||||||
'react/jsx-filename-extension': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
extensions: ['.jsx', '.tsx'],
|
|
||||||
allow: 'as-needed',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'react/no-unstable-nested-components': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
allowAsProps: true,
|
|
||||||
customValidators: ['formatter'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
// Don't need this as we're using TypeScript
|
|
||||||
'react/prop-types': 'off',
|
|
||||||
|
|
||||||
// https://github.com/evcohen/eslint-plugin-jsx-a11y/tree/master/docs/rules
|
|
||||||
'jsx-a11y/alt-text': 'warn',
|
|
||||||
'jsx-a11y/anchor-has-content': 'warn',
|
|
||||||
'jsx-a11y/anchor-is-valid': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
aspects: ['noHref', 'invalidHref'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'jsx-a11y/aria-activedescendant-has-tabindex': 'warn',
|
|
||||||
'jsx-a11y/aria-props': 'warn',
|
|
||||||
'jsx-a11y/aria-proptypes': 'warn',
|
|
||||||
'jsx-a11y/aria-role': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
ignoreNonDOM: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'jsx-a11y/aria-unsupported-elements': 'warn',
|
|
||||||
'jsx-a11y/heading-has-content': 'warn',
|
|
||||||
'jsx-a11y/iframe-has-title': 'warn',
|
|
||||||
'jsx-a11y/img-redundant-alt': 'warn',
|
|
||||||
'jsx-a11y/no-access-key': 'warn',
|
|
||||||
'jsx-a11y/no-distracting-elements': 'warn',
|
|
||||||
'jsx-a11y/no-redundant-roles': 'warn',
|
|
||||||
'jsx-a11y/role-has-required-aria-props': 'warn',
|
|
||||||
'jsx-a11y/role-supports-aria-props': 'warn',
|
|
||||||
'jsx-a11y/scope': 'warn',
|
|
||||||
|
|
||||||
// https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks
|
|
||||||
'react-hooks/rules-of-hooks': 'error',
|
|
||||||
'react-hooks/exhaustive-deps': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
additionalHooks: '(useQuery)',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'rulesdir/typography': 'warn',
|
|
||||||
'rulesdir/prefer-if-statement': 'warn',
|
|
||||||
|
|
||||||
// Note: base rule explicitly disabled in favor of the TS one
|
|
||||||
'no-unused-vars': 'off',
|
|
||||||
'@typescript-eslint/no-unused-vars': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
varsIgnorePattern: '^(_|React)',
|
|
||||||
ignoreRestSiblings: true,
|
|
||||||
caughtErrors: 'none',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'no-restricted-globals': ['warn', ...confusingBrowserGlobals],
|
|
||||||
|
|
||||||
// https://github.com/eslint/eslint/issues/16954
|
|
||||||
// https://github.com/eslint/eslint/issues/16953
|
|
||||||
'no-loop-func': 'off',
|
|
||||||
|
|
||||||
// TODO: re-enable these rules
|
|
||||||
'react/react-in-jsx-scope': 'off',
|
|
||||||
'no-var': 'warn',
|
|
||||||
'react/jsx-curly-brace-presence': 'warn',
|
|
||||||
'object-shorthand': ['warn', 'properties'],
|
|
||||||
|
|
||||||
'no-restricted-syntax': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
// forbid React.* as they are legacy https://twitter.com/dan_abramov/status/1308739731551858689
|
|
||||||
selector:
|
|
||||||
":matches(MemberExpression[object.name='React'], TSQualifiedName[left.name='React'])",
|
|
||||||
message:
|
|
||||||
'Using default React import is discouraged, please use named exports directly instead.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// forbid <a> in favor of <Link>
|
|
||||||
selector: 'JSXOpeningElement[name.name="a"]',
|
|
||||||
message: 'Using <a> is discouraged, please use <Link> instead.',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'no-restricted-imports': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
group: ['*.api', '*.web', '*.electron'],
|
|
||||||
message: "Don't directly reference imports from other platforms",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: ['uuid'],
|
|
||||||
importNames: ['*'],
|
|
||||||
message: "Use `import { v4 as uuidv4 } from 'uuid'` instead",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: ['**/style', '**/colors'],
|
|
||||||
importNames: ['colors'],
|
|
||||||
message: 'Please use themes instead of colors',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'@typescript-eslint/ban-ts-comment': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
'ts-ignore': 'allow-with-description',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
// Rules disabled during TS migration
|
|
||||||
'@typescript-eslint/no-var-requires': 'off',
|
|
||||||
'prefer-const': 'warn',
|
|
||||||
'prefer-spread': 'off',
|
|
||||||
'@typescript-eslint/no-empty-function': 'off',
|
|
||||||
'@typescript-eslint/no-require-imports': 'off',
|
|
||||||
'import/no-default-export': 'warn',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.ts?(x)'],
|
|
||||||
|
|
||||||
languageOptions: {
|
|
||||||
parser: tsParser,
|
|
||||||
ecmaVersion: 2018,
|
|
||||||
sourceType: 'module',
|
|
||||||
|
|
||||||
parserOptions: {
|
|
||||||
project: [path.join(__dirname, './tsconfig.json')],
|
|
||||||
ecmaFeatures: {
|
|
||||||
jsx: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// typescript-eslint specific options
|
|
||||||
warnOnUnsupportedTypeScriptVersion: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// If adding a typescript-eslint version of an existing ESLint rule,
|
|
||||||
// make sure to disable the ESLint rule here.
|
|
||||||
rules: {
|
|
||||||
// TypeScript's `noFallthroughCasesInSwitch` option is more robust (#6906)
|
|
||||||
'default-case': 'off',
|
|
||||||
// 'tsc' already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/291)
|
|
||||||
'no-dupe-class-members': 'off',
|
|
||||||
// 'tsc' already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/477)
|
|
||||||
'no-undef': 'off',
|
|
||||||
|
|
||||||
// Add TypeScript specific rules (and turn off ESLint equivalents)
|
|
||||||
'@typescript-eslint/consistent-type-assertions': 'warn',
|
|
||||||
'no-array-constructor': 'off',
|
|
||||||
'@typescript-eslint/no-array-constructor': 'warn',
|
|
||||||
'no-redeclare': 'off',
|
|
||||||
'@typescript-eslint/no-redeclare': 'warn',
|
|
||||||
'no-use-before-define': 'off',
|
|
||||||
|
|
||||||
'@typescript-eslint/no-use-before-define': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
functions: false,
|
|
||||||
classes: false,
|
|
||||||
variables: false,
|
|
||||||
typedefs: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'no-unused-expressions': 'off',
|
|
||||||
|
|
||||||
'@typescript-eslint/no-unused-expressions': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
allowShortCircuit: true,
|
|
||||||
allowTernary: true,
|
|
||||||
allowTaggedTemplates: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'no-useless-constructor': 'off',
|
|
||||||
'@typescript-eslint/no-useless-constructor': 'warn',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'packages/desktop-client/**/*.{ts,tsx}',
|
|
||||||
'packages/loot-core/src/client/**/*.{ts,tsx}',
|
|
||||||
],
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
// enforce type over interface
|
|
||||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'type'],
|
|
||||||
|
|
||||||
// enforce import type
|
|
||||||
'@typescript-eslint/consistent-type-imports': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
prefer: 'type-imports',
|
|
||||||
fixStyle: 'inline-type-imports',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'@typescript-eslint/no-restricted-types': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
types: {
|
|
||||||
// forbid FC as superflous
|
|
||||||
FunctionComponent: {
|
|
||||||
message:
|
|
||||||
'Type the props argument and let TS infer or use ComponentType for a component prop',
|
|
||||||
},
|
|
||||||
|
|
||||||
FC: {
|
|
||||||
message:
|
|
||||||
'Type the props argument and let TS infer or use ComponentType for a component prop',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['packages/desktop-client/**/*'],
|
|
||||||
ignores: ['packages/desktop-client/src/hooks/useNavigate.{ts,tsx}'],
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'no-restricted-imports': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
group: ['react-router-dom'],
|
|
||||||
importNames: ['useNavigate'],
|
|
||||||
message: "Please use Actual's useNavigate() hook instead.",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['packages/loot-core/src/**/*'],
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'no-restricted-imports': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
group: ['*.api', '*.web', '*.electron'],
|
|
||||||
message: "Don't directly reference imports from other platforms",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: ['uuid'],
|
|
||||||
importNames: ['*'],
|
|
||||||
message: "Use `import { v4 as uuidv4 } from 'uuid'` instead",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: ['loot-core/**'],
|
|
||||||
message:
|
|
||||||
'Please use relative imports in loot-core instead of importing from `loot-core/*`',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'packages/loot-core/src/types/**/*',
|
|
||||||
'packages/loot-core/src/client/state-types/**/*',
|
|
||||||
'**/icons/**/*',
|
|
||||||
'**/{mocks,__mocks__}/**/*',
|
|
||||||
// can't correctly resolve usages
|
|
||||||
'**/*.{testing,electron,browser,web,api}.ts',
|
|
||||||
],
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'import/no-unused-modules': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'packages/desktop-client/src/style/index.*',
|
|
||||||
'packages/desktop-client/src/style/palette.*',
|
|
||||||
],
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'no-restricted-imports': [
|
|
||||||
'off',
|
|
||||||
{
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
group: ['**/style', '**/colors'],
|
|
||||||
importNames: ['colors'],
|
|
||||||
message: 'Please use themes instead of colors',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['packages/api/migrations/*', 'packages/loot-core/migrations/*'],
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'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/envelope/HoldMenu.tsx',
|
|
||||||
'packages/desktop-client/src/components/budget/envelope/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',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: [
|
|
||||||
'eslint.config.mjs',
|
|
||||||
'**/*.test.js',
|
|
||||||
'**/*.test.ts',
|
|
||||||
'**/*.test.jsx',
|
|
||||||
'**/*.test.tsx',
|
|
||||||
],
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'rulesdir/typography': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
22
package.json
@@ -38,33 +38,33 @@
|
|||||||
"vrt:docker": "./bin/run-vrt",
|
"vrt:docker": "./bin/run-vrt",
|
||||||
"rebuild-electron": "./node_modules/.bin/electron-rebuild -f -m ./packages/loot-core",
|
"rebuild-electron": "./node_modules/.bin/electron-rebuild -f -m ./packages/loot-core",
|
||||||
"rebuild-node": "yarn workspace loot-core rebuild",
|
"rebuild-node": "yarn workspace loot-core rebuild",
|
||||||
"lint": "eslint . --max-warnings 0",
|
"lint": "eslint . --max-warnings 0 --ext .js,.jsx,.ts,.tsx",
|
||||||
"lint:verbose": "DEBUG=eslint:cli-engine eslint . --max-warnings 0",
|
"lint:verbose": "DEBUG=eslint:cli-engine eslint . --max-warnings 0",
|
||||||
"typecheck": "yarn tsc && tsc-strict",
|
"typecheck": "yarn tsc && tsc-strict",
|
||||||
"jq": "./node_modules/node-jq/bin/jq",
|
"jq": "./node_modules/node-jq/bin/jq",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/parser": "^8.18.1",
|
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
||||||
|
"@typescript-eslint/parser": "^8.1.0",
|
||||||
|
"confusing-browser-globals": "^1.0.11",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^9.17.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-import-resolver-typescript": "^3.7.0",
|
"eslint-import-resolver-typescript": "3.6.1",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||||
"eslint-plugin-prettier": "5.2.1",
|
"eslint-plugin-prettier": "5.2.1",
|
||||||
"eslint-plugin-react": "^7.37.2",
|
"eslint-plugin-react": "7.35.0",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"eslint-plugin-rulesdir": "^0.2.2",
|
"eslint-plugin-rulesdir": "^0.2.2",
|
||||||
"globals": "^15.13.0",
|
|
||||||
"husky": "^9.0.11",
|
"husky": "^9.0.11",
|
||||||
"lint-staged": "^15.2.9",
|
"lint-staged": "^15.2.9",
|
||||||
"node-jq": "^4.0.1",
|
"node-jq": "^4.0.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.4.2",
|
"prettier": "3.3.3",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"typescript-eslint": "^8.18.1",
|
|
||||||
"typescript-strict-plugin": "^2.4.4"
|
"typescript-strict-plugin": "^2.4.4"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type {
|
|||||||
import type { InitConfig } from 'loot-core/server/main';
|
import type { InitConfig } from 'loot-core/server/main';
|
||||||
|
|
||||||
// @ts-ignore: bundle not available until we build it
|
// @ts-ignore: bundle not available until we build it
|
||||||
// eslint-disable-next-line import/extensions, import/no-unresolved
|
// eslint-disable-next-line import/extensions
|
||||||
import * as bundle from './app/bundle.api.js';
|
import * as bundle from './app/bundle.api.js';
|
||||||
import * as injected from './injected';
|
import * as injected from './injected';
|
||||||
import { validateNodeVersion } from './validateNodeVersion';
|
import { validateNodeVersion } from './validateNodeVersion';
|
||||||
|
|||||||
@@ -86,10 +86,7 @@ export function addTransactions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function importTransactions(accountId, transactions) {
|
export function importTransactions(accountId, transactions) {
|
||||||
return send('api/transactions-import', {
|
return send('api/transactions-import', { accountId, transactions });
|
||||||
accountId,
|
|
||||||
transactions,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTransactions(accountId, startDate, endDate) {
|
export function getTransactions(accountId, startDate, endDate) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@actual-app/api",
|
"name": "@actual-app/api",
|
||||||
"version": "25.1.0",
|
"version": "24.11.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "An API for Actual",
|
"description": "An API for Actual",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actual-app/crdt": "workspace:^",
|
"@actual-app/crdt": "workspace:^",
|
||||||
"better-sqlite3": "^11.7.0",
|
"better-sqlite3": "^9.6.0",
|
||||||
"compare-versions": "^6.1.0",
|
"compare-versions": "^6.1.0",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
|
|||||||
4
packages/desktop-client/.gitignore
vendored
@@ -10,7 +10,6 @@ playwright-report
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
build
|
build
|
||||||
build-electron
|
|
||||||
build-stats
|
build-stats
|
||||||
stats.json
|
stats.json
|
||||||
|
|
||||||
@@ -25,6 +24,3 @@ public/kcab
|
|||||||
public/data
|
public/data
|
||||||
public/data-file-index.txt
|
public/data-file-index.txt
|
||||||
public/*.wasm
|
public/*.wasm
|
||||||
|
|
||||||
# translations
|
|
||||||
locale/
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ test.describe('Mobile Accounts', () => {
|
|||||||
|
|
||||||
test('opens the accounts page and asserts on balances', async () => {
|
test('opens the accounts page and asserts on balances', async () => {
|
||||||
const accountsPage = await navigation.goToAccountsPage();
|
const accountsPage = await navigation.goToAccountsPage();
|
||||||
await accountsPage.waitFor();
|
|
||||||
|
|
||||||
const account = await accountsPage.getNthAccount(1);
|
const account = await accountsPage.getNthAccount(1);
|
||||||
|
|
||||||
@@ -38,10 +37,7 @@ test.describe('Mobile Accounts', () => {
|
|||||||
|
|
||||||
test('opens individual account page and checks that filtering is working', async () => {
|
test('opens individual account page and checks that filtering is working', async () => {
|
||||||
const accountsPage = await navigation.goToAccountsPage();
|
const accountsPage = await navigation.goToAccountsPage();
|
||||||
await accountsPage.waitFor();
|
|
||||||
|
|
||||||
const accountPage = await accountsPage.openNthAccount(0);
|
const accountPage = await accountsPage.openNthAccount(0);
|
||||||
await accountPage.waitFor();
|
|
||||||
|
|
||||||
await expect(accountPage.heading).toHaveText('Bank of America');
|
await expect(accountPage.heading).toHaveText('Bank of America');
|
||||||
await expect(accountPage.transactionList).toBeVisible();
|
await expect(accountPage.transactionList).toBeVisible();
|
||||||
@@ -54,9 +50,6 @@ test.describe('Mobile Accounts', () => {
|
|||||||
await expect(accountPage.transactions).toHaveCount(0);
|
await expect(accountPage.transactions).toHaveCount(0);
|
||||||
await expect(page).toMatchThemeScreenshots();
|
await expect(page).toMatchThemeScreenshots();
|
||||||
|
|
||||||
await accountPage.clearSearch();
|
|
||||||
await expect(accountPage.transactions).not.toHaveCount(0);
|
|
||||||
|
|
||||||
await accountPage.searchByText('Kroger');
|
await accountPage.searchByText('Kroger');
|
||||||
await expect(accountPage.transactions).not.toHaveCount(0);
|
await expect(accountPage.transactions).not.toHaveCount(0);
|
||||||
await expect(page).toMatchThemeScreenshots();
|
await expect(page).toMatchThemeScreenshots();
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 35 KiB |
@@ -54,17 +54,15 @@ test.describe('Accounts', () => {
|
|||||||
await expect(page).toMatchThemeScreenshots();
|
await expect(page).toMatchThemeScreenshots();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('On Budget Accounts', () => {
|
test.describe('Budgeted Accounts', () => {
|
||||||
// Reset filters
|
// Reset filters
|
||||||
test.afterEach(async () => {
|
test.afterEach(async () => {
|
||||||
await accountPage.removeFilter(0);
|
await accountPage.removeFilter(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('creates a transfer from two existing transactions', async () => {
|
test('creates a transfer from two existing transactions', async () => {
|
||||||
accountPage = await navigation.goToAccountPage('On budget');
|
accountPage = await navigation.goToAccountPage('For budget');
|
||||||
await accountPage.waitFor();
|
await expect(accountPage.accountName).toHaveText('Budgeted Accounts');
|
||||||
|
|
||||||
await expect(accountPage.accountName).toHaveText('On Budget Accounts');
|
|
||||||
|
|
||||||
await accountPage.filterByNote('Test Acc Transfer');
|
await accountPage.filterByNote('Test Acc Transfer');
|
||||||
|
|
||||||
@@ -111,7 +109,6 @@ test.describe('Accounts', () => {
|
|||||||
offBudget: false,
|
offBudget: false,
|
||||||
balance: 0,
|
balance: 0,
|
||||||
});
|
});
|
||||||
await accountPage.waitFor();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function importCsv(screenshot = false) {
|
async function importCsv(screenshot = false) {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
@@ -27,12 +27,12 @@ test.describe('Budget', () => {
|
|||||||
test('renders the summary information: available funds, overspent, budgeted and for next month', async () => {
|
test('renders the summary information: available funds, overspent, budgeted and for next month', async () => {
|
||||||
const summary = budgetPage.budgetSummary.first();
|
const summary = budgetPage.budgetSummary.first();
|
||||||
|
|
||||||
await expect(summary.getByText('Available funds')).toBeVisible({
|
await expect(summary.getByText('Available Funds')).toBeVisible({
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
});
|
});
|
||||||
await expect(summary.getByText(/^Overspent in /)).toBeVisible();
|
await expect(summary.getByText(/^Overspent in /)).toBeVisible();
|
||||||
await expect(summary.getByText('Budgeted')).toBeVisible();
|
await expect(summary.getByText('Budgeted')).toBeVisible();
|
||||||
await expect(summary.getByText('For next month')).toBeVisible();
|
await expect(summary.getByText('For Next Month')).toBeVisible();
|
||||||
await expect(page).toMatchThemeScreenshots();
|
await expect(page).toMatchThemeScreenshots();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
@@ -30,10 +30,6 @@ export class AccountPage {
|
|||||||
this.selectTooltip = this.page.getByTestId('transactions-select-tooltip');
|
this.selectTooltip = this.page.getByTestId('transactions-select-tooltip');
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitFor() {
|
|
||||||
await this.transactionTable.waitFor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enter details of a transaction
|
* Enter details of a transaction
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ export class MobileAccountPage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitFor() {
|
|
||||||
await this.transactionList.waitFor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the balance of the account as a number
|
* Retrieve the balance of the account as a number
|
||||||
*/
|
*/
|
||||||
@@ -33,10 +29,6 @@ export class MobileAccountPage {
|
|||||||
await this.searchBox.fill(term);
|
await this.searchBox.fill(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
async clearSearch() {
|
|
||||||
await this.searchBox.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Go to transaction creation page
|
* Go to transaction creation page
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,14 +4,9 @@ export class MobileAccountsPage {
|
|||||||
constructor(page) {
|
constructor(page) {
|
||||||
this.page = page;
|
this.page = page;
|
||||||
|
|
||||||
this.accountList = this.page.getByLabel('Account list');
|
|
||||||
this.accounts = this.page.getByTestId('account');
|
this.accounts = this.page.getByTestId('account');
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitFor() {
|
|
||||||
await this.accountList.waitFor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name and balance of the nth account
|
* Get the name and balance of the nth account
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class MobileBudgetPage {
|
|||||||
name: 'Saved',
|
name: 'Saved',
|
||||||
});
|
});
|
||||||
this.projectedSavingsButton = this.budgetTableHeader.getByRole('button', {
|
this.projectedSavingsButton = this.budgetTableHeader.getByRole('button', {
|
||||||
name: 'Projected savings',
|
name: 'Projected Savings',
|
||||||
});
|
});
|
||||||
this.overspentButton = this.budgetTableHeader.getByRole('button', {
|
this.overspentButton = this.budgetTableHeader.getByRole('button', {
|
||||||
name: 'Overspent',
|
name: 'Overspent',
|
||||||
@@ -294,7 +294,7 @@ export class MobileBudgetPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'None of “Saved”, “Projected savings”, or “Overspent” buttons could be located on the page',
|
'None of “Saved”, “Projected Savings”, or “Overspent” buttons could be located on the page',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { MobileAccountPage } from './mobile-account-page';
|
|
||||||
import { MobileAccountsPage } from './mobile-accounts-page';
|
import { MobileAccountsPage } from './mobile-accounts-page';
|
||||||
import { MobileBudgetPage } from './mobile-budget-page';
|
import { MobileBudgetPage } from './mobile-budget-page';
|
||||||
import { MobileTransactionEntryPage } from './mobile-transaction-entry-page';
|
import { MobileTransactionEntryPage } from './mobile-transaction-entry-page';
|
||||||
@@ -23,13 +22,6 @@ export class MobileNavigation {
|
|||||||
return new MobileAccountsPage(this.page);
|
return new MobileAccountsPage(this.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
async goToUncategorizedPage() {
|
|
||||||
const button = this.page.getByRole('button', { name: /uncategorized/ });
|
|
||||||
await button.click();
|
|
||||||
|
|
||||||
return new MobileAccountPage(this.page);
|
|
||||||
}
|
|
||||||
|
|
||||||
async goToTransactionEntryPage() {
|
async goToTransactionEntryPage() {
|
||||||
const link = this.page.getByRole('link', { name: 'Transaction' });
|
const link = this.page.getByRole('link', { name: 'Transaction' });
|
||||||
await link.click();
|
await link.click();
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export class Navigation {
|
|||||||
async createAccount(data) {
|
async createAccount(data) {
|
||||||
await this.page.getByRole('button', { name: 'Add account' }).click();
|
await this.page.getByRole('button', { name: 'Add account' }).click();
|
||||||
await this.page
|
await this.page
|
||||||
.getByRole('button', { name: 'Create a local account' })
|
.getByRole('button', { name: 'Create local account' })
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
// Fill the form
|
// Fill the form
|
||||||
@@ -66,7 +66,7 @@ export class Navigation {
|
|||||||
await this.page.getByLabel('Balance:').fill(String(data.balance));
|
await this.page.getByLabel('Balance:').fill(String(data.balance));
|
||||||
|
|
||||||
if (data.offBudget) {
|
if (data.offBudget) {
|
||||||
await this.page.getByLabel('Off budget').click();
|
await this.page.getByLabel('Off-budget').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.page
|
await this.page
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ export class ReportsPage {
|
|||||||
|
|
||||||
async goToCustomReportPage() {
|
async goToCustomReportPage() {
|
||||||
await this.pageContent
|
await this.pageContent
|
||||||
.getByRole('button', { name: 'Add new widget' })
|
.getByRole('button', { name: 'Create new custom report' })
|
||||||
.click();
|
.click();
|
||||||
await this.page.getByRole('button', { name: 'New custom report' }).click();
|
|
||||||
return new CustomReportPage(this.page);
|
return new CustomReportPage(this.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,27 @@ export class SettingsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async useBudgetType(budgetType) {
|
async useBudgetType(budgetType) {
|
||||||
|
await this.enableExperimentalFeature('Budget mode toggle');
|
||||||
|
|
||||||
const switchBudgetTypeButton = this.page.getByRole('button', {
|
const switchBudgetTypeButton = this.page.getByRole('button', {
|
||||||
name: `Switch to ${budgetType} budgeting`,
|
name: `Switch to ${budgetType} budgeting`,
|
||||||
});
|
});
|
||||||
|
|
||||||
await switchBudgetTypeButton.click();
|
await switchBudgetTypeButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async enableExperimentalFeature(featureName) {
|
||||||
|
const advancedSettingsButton = this.page.getByTestId('advanced-settings');
|
||||||
|
await advancedSettingsButton.click();
|
||||||
|
|
||||||
|
const experimentalSettingsButton = this.page.getByTestId(
|
||||||
|
'experimental-settings',
|
||||||
|
);
|
||||||
|
await experimentalSettingsButton.click();
|
||||||
|
|
||||||
|
const featureCheckbox = this.page.getByRole('checkbox', {
|
||||||
|
name: featureName,
|
||||||
|
});
|
||||||
|
await featureCheckbox.click();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |