Compare commits

..

5 Commits

Author SHA1 Message Date
Joel Jeremy Marquez
764b396008 Remove console.log 2025-08-21 15:33:53 -07:00
Joel Jeremy Marquez
2d4b39cce8 Coderabbit feedback 2025-08-21 15:33:53 -07:00
github-actions[bot]
fc75f468c2 Update VRT 2025-08-21 15:33:53 -07:00
Joel Jeremy Marquez
2f0dd8f0a9 Cleanup 2025-08-21 15:33:53 -07:00
Joel Jeremy Marquez
afca809d94 [Mobile] Show uncategorized/overspending totals on budget banners 2025-08-21 15:33:53 -07:00
399 changed files with 2361 additions and 6813 deletions

View File

@@ -13,7 +13,6 @@ Code Style and Structure
- Prefer iteration and modularization over code duplication.
- Use descriptive variable names with auxiliary verbs (e.g., isLoaded, hasError).
- Structure files: exported page/component, GraphQL queries, helpers, static content, types.
- When creating a new component, place it in its own file rather than grouping multiple components in a single file.
Naming Conventions

View File

@@ -2,7 +2,6 @@ name: Bug Report
description: File a bug report also known as an issue or problem.
title: '[Bug]: '
labels: ['needs triage', 'bug']
type: Bug
body:
- type: markdown
attributes:

View File

@@ -2,7 +2,6 @@ name: Feature request
description: Request a missing feature
title: '[Feature] '
labels: ['feature']
type: Feature
body:
- type: markdown
attributes:

View File

@@ -0,0 +1,117 @@
#!/usr/bin/env node
// This script is used in GitHub Actions to get the next version based on the current package.json version.
// It supports three types of versioning: nightly, hotfix, and monthly.
const { parseArgs } = require('node:util');
const fs = require('node:fs');
const args = process.argv;
const options = {
'package-json': {
type: 'string',
short: 'p',
},
type: {
type: 'string', // nightly, hotfix, monthly, auto
short: 't',
},
update: {
type: 'boolean',
short: 'u',
default: false,
},
};
const { values } = parseArgs({
args,
options,
allowPositionals: true,
});
if (!values['package-json']) {
console.error(
'Please specify the path to package.json using --package-json or -p option.',
);
process.exit(1);
}
try {
const packageJsonPath = values['package-json'];
// Read and parse package.json
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const currentVersion = packageJson.version;
// Parse year and month from version (e.g. 25.5.1 -> year=2025, month=5)
const versionParts = currentVersion.split('.');
const versionYear = parseInt(versionParts[0]);
const versionMonth = parseInt(versionParts[1]);
const versionHotfix = parseInt(versionParts[2]);
// Create date and add 1 month
const versionDate = new Date(2000 + versionYear, versionMonth - 1, 1); // month is 0-indexed
const nextVersionMonthDate = new Date(
versionDate.getFullYear(),
versionDate.getMonth() + 1,
1,
);
// Format back to YY.M format
const nextVersionYear = nextVersionMonthDate
.getFullYear()
.toString()
.slice(nextVersionMonthDate.getFullYear() < 2100 ? -2 : -3);
const nextVersionMonth = nextVersionMonthDate.getMonth() + 1; // Convert back to 1-indexed
// Get current date string
const currentDate = new Date();
const currentDateString = currentDate
.toISOString()
.split('T')[0]
.replaceAll('-', '');
if (values.type === 'auto') {
if (currentDate.getDate() <= 25) {
values.type = 'hotfix';
} else {
values.type = 'monthly';
}
}
let newVersion;
switch (values.type) {
case 'nightly': {
newVersion = `${nextVersionYear}.${nextVersionMonth}.0-nightly.${currentDateString}`;
break;
}
case 'hotfix': {
newVersion = `${versionYear}.${versionMonth}.${versionHotfix + 1}`;
break;
}
case 'monthly': {
newVersion = `${nextVersionYear}.${nextVersionMonth}.0`;
break;
}
default:
console.error(
'Invalid type specified. Use "auto", "nightly", "hotfix", or "monthly".',
);
process.exit(1);
}
process.stdout.write(newVersion); // return the new version to stdout
if (values.update) {
packageJson.version = newVersion;
fs.writeFileSync(
packageJsonPath,
JSON.stringify(packageJson, null, 2) + '\n',
'utf8',
);
}
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}

View File

@@ -2,7 +2,7 @@ import { Octokit } from '@octokit/rest';
import { minimatch } from 'minimatch';
import pLimit from 'p-limit';
const limit = pLimit(50);
const limit = pLimit(30);
/** Repository-specific configuration for points calculation */
const REPOSITORY_CONFIG = new Map([
@@ -129,13 +129,13 @@ async function countContributorPoints(repo) {
// Get all PRs using search
const searchQuery = `repo:${owner}/${repo} is:pr is:merged merged:${since.toISOString()}..${until.toISOString()}`;
const recentPRs = await octokit.paginate(
'GET /search/issues',
octokit.search.issuesAndPullRequests,
{
q: searchQuery,
per_page: 100,
advanced_search: true,
},
response => response.data.filter(pr => pr.number),
response => response.data,
);
// Get reviews and PR details for each PR

View File

@@ -24,7 +24,7 @@ jobs:
strategy:
matrix:
os:
- ubuntu-22.04
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.os }}

View File

@@ -19,7 +19,7 @@ jobs:
strategy:
matrix:
os:
- ubuntu-22.04
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.os }}

View File

@@ -37,7 +37,7 @@ jobs:
if [[ -n "${{ github.event.inputs.version }}" ]]; then
version="${{ github.event.inputs.version }}"
else
version=$(node ./packages/ci-actions/bin/get-next-package-version.js \
version=$(node ./.github/actions/get-next-package-version.js \
--package-json "./packages/$pkg/package.json" \
--type auto \
--update)

View File

@@ -20,9 +20,9 @@ jobs:
- name: Update package versions
run: |
# Get new nightly versions
NEW_WEB_VERSION=$(node ./packages/ci-actions/bin/get-next-package-version.js --package-json ./packages/desktop-client/package.json --type nightly)
NEW_SYNC_VERSION=$(node ./packages/ci-actions/bin/get-next-package-version.js --package-json ./packages/sync-server/package.json --type nightly)
NEW_API_VERSION=$(node ./packages/ci-actions/bin/get-next-package-version.js --package-json ./packages/api/package.json --type nightly)
NEW_WEB_VERSION=$(node ./.github/actions/get-next-package-version.js --package-json ./packages/desktop-client/package.json --type nightly)
NEW_SYNC_VERSION=$(node ./.github/actions/get-next-package-version.js --package-json ./packages/sync-server/package.json --type nightly)
NEW_API_VERSION=$(node ./.github/actions/get-next-package-version.js --package-json ./packages/api/package.json --type nightly)
# Set package versions
npm version $NEW_WEB_VERSION --no-git-tag-version --workspace=@actual-app/web --no-workspaces-update

1
actual

Submodule actual deleted from 60b64e10ff

View File

@@ -14,8 +14,6 @@ git pull
popd > /dev/null
packages/desktop-client/bin/remove-untranslated-languages
export NODE_OPTIONS="--max-old-space-size=4096"
yarn workspace loot-core build:browser
yarn workspace @actual-app/web build:browser

View File

@@ -39,8 +39,6 @@ git pull
popd > /dev/null
packages/desktop-client/bin/remove-untranslated-languages
export NODE_OPTIONS="--max-old-space-size=4096"
yarn workspace loot-core build:node
yarn workspace @actual-app/web build --mode=desktop # electron specific build

View File

@@ -154,6 +154,9 @@ export default pluginTypescript.config(
{
plugins: {
actual: pluginActual,
'react-hooks': pluginReactHooks,
'jsx-a11y': pluginJSXA11y,
'typescript-paths': pluginTypescriptPaths,
},
rules: {
'actual/no-untranslated-strings': 'error',
@@ -162,10 +165,6 @@ export default pluginTypescript.config(
},
{
files: ['**/*.{js,ts,jsx,tsx}'],
plugins: {
'jsx-a11y': pluginJSXA11y,
'react-hooks': pluginReactHooks,
},
rules: {
// http://eslint.org/docs/rules/
'array-callback-return': 'warn',
@@ -451,7 +450,6 @@ export default pluginTypescript.config(
'actual/typography': 'warn',
'actual/prefer-if-statement': 'warn',
'actual/prefer-logger-over-console': 'error',
// Note: base rule explicitly disabled in favor of the TS one
'no-unused-vars': 'off',
@@ -632,9 +630,6 @@ export default pluginTypescript.config(
},
{
files: ['packages/desktop-client/**/*.{js,ts,jsx,tsx}'],
plugins: {
'typescript-paths': pluginTypescriptPaths,
},
rules: {
'typescript-paths/absolute-parent-import': [
'error',
@@ -776,7 +771,6 @@ export default pluginTypescript.config(
rules: {
'actual/typography': 'off',
'actual/no-untranslated-strings': 'off',
'actual/prefer-logger-over-console': 'off',
},
},
{

View File

@@ -57,15 +57,15 @@
"@octokit/rest": "^22.0.0",
"@types/node": "^22.17.0",
"@types/prompts": "^2.4.9",
"@typescript-eslint/parser": "^8.42.0",
"@typescript-eslint/parser": "^8.32.1",
"cross-env": "^7.0.3",
"eslint": "^9.34.0",
"eslint": "^9.27.0",
"eslint-config-prettier": "^10.1.5",
"eslint-import-resolver-typescript": "^4.3.5",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^6.0.0-rc.2",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-typescript-paths": "^0.0.33",
"globals": "^15.15.0",
"html-to-image": "^1.11.13",
@@ -80,7 +80,7 @@
"source-map-support": "^0.5.21",
"ts-node": "^10.9.2",
"typescript": "^5.9.2",
"typescript-eslint": "^8.42.0",
"typescript-eslint": "^8.32.1",
"typescript-strict-plugin": "^2.4.4"
},
"resolutions": {
@@ -99,7 +99,7 @@
},
"packageManager": "yarn@4.9.1",
"browserslist": [
"electron >= 35.0",
"electron 24.0",
"defaults"
]
}

View File

@@ -42,11 +42,7 @@ export async function init(config: InitConfig = {}) {
export async function shutdown() {
if (actualApp) {
try {
await actualApp.send('sync');
} catch (e) {
// most likely that no budget is loaded, so the sync failed
}
await actualApp.send('sync');
await actualApp.send('close-budget');
actualApp = null;
}

View File

@@ -740,122 +740,3 @@ describe('API CRUD operations', () => {
expect(transactions[0].notes).toBeNull();
});
});
//apis: createSchedule, getSchedules, updateSchedule, deleteSchedule
test('Schedules: successfully complete schedules operations', async () => {
await api.loadBudget(budgetName);
//test a schedule with a recuring configuration
const ScheduleId1 = await api.createSchedule({
name: 'test-schedule 1',
posts_transaction: true,
// amount: -5000,
amountOp: 'is',
date: {
frequency: 'monthly',
interval: 1,
start: '2025-06-13',
patterns: [],
skipWeekend: false,
weekendSolveMode: 'after',
endMode: 'never',
},
});
//test the creation of non recurring schedule
const ScheduleId2 = await api.createSchedule({
name: 'test-schedule 2',
posts_transaction: false,
amount: 4000,
amountOp: 'is',
date: '2025-06-13',
});
let schedules = await api.getSchedules();
// Schedules successfully created
expect(schedules).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'test-schedule 1',
posts_transaction: true,
// amount: -5000,
amountOp: 'is',
date: {
frequency: 'monthly',
interval: 1,
start: '2025-06-13',
patterns: [],
skipWeekend: false,
weekendSolveMode: 'after',
endMode: 'never',
},
}),
expect.objectContaining({
name: 'test-schedule 2',
posts_transaction: false,
amount: 4000,
amountOp: 'is',
date: '2025-06-13',
}),
]),
);
//check getIDByName works on schedules
expect(await api.getIDByName('schedules', 'test-schedule 1')).toEqual(
ScheduleId1,
);
expect(await api.getIDByName('schedules', 'test-schedule 2')).toEqual(
ScheduleId2,
);
//check getIDByName works on accounts
const schedAccountId1 = await api.createAccount(
{ name: 'sched-test-account1', offbudget: true },
1000,
);
expect(await api.getIDByName('accounts', 'sched-test-account1')).toEqual(
schedAccountId1,
);
//check getIDByName works on payees
const schedPayeeId1 = await api.createPayee({ name: 'sched-test-payee1' });
expect(await api.getIDByName('payees', 'sched-test-payee1')).toEqual(
schedPayeeId1,
);
await api.updateSchedule(ScheduleId1, {
amount: -10000,
account: schedAccountId1,
});
await api.deleteSchedule(ScheduleId2);
// schedules successfully updated, and one of them deleted
await api.updateSchedule(ScheduleId1, {
amount: -10000,
account: schedAccountId1,
payee: schedPayeeId1,
});
await api.deleteSchedule(ScheduleId2);
schedules = await api.getSchedules();
expect(schedules).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: ScheduleId1,
posts_transaction: true,
amount: -10000,
account: schedAccountId1,
payee: schedPayeeId1,
amountOp: 'is',
date: {
frequency: 'monthly',
interval: 1,
start: '2025-06-13',
patterns: [],
skipWeekend: false,
weekendSolveMode: 'after',
endMode: 'never',
},
}),
expect.not.objectContaining({ id: ScheduleId2 }),
]),
);
});

View File

@@ -96,7 +96,6 @@ export function addTransactions(
export interface ImportTransactionsOpts {
defaultCleared?: boolean;
dryRun?: boolean;
}
export function importTransactions(
@@ -104,13 +103,11 @@ export function importTransactions(
transactions: ImportTransactionEntity[],
opts: ImportTransactionsOpts = {
defaultCleared: true,
dryRun: false,
},
) {
return send('api/transactions-import', {
accountId,
transactions,
isPreview: opts.dryRun,
opts,
});
}
@@ -242,31 +239,3 @@ export function holdBudgetForNextMonth(month, amount) {
export function resetBudgetHold(month) {
return send('api/budget-reset-hold', { month });
}
export function createSchedule(schedule) {
return send('api/schedule-create', schedule);
}
export function updateSchedule(id, fields, resetNextDate?: boolean) {
return send('api/schedule-update', {
id,
fields,
resetNextDate,
});
}
export function deleteSchedule(scheduleId) {
return send('api/schedule-delete', scheduleId);
}
export function getSchedules() {
return send('api/schedules-get');
}
export function getIDByName(type, name) {
return send('api/get-id-by-name', { type, name });
}
export function getServerVersion() {
return send('api/get-server-version');
}

View File

@@ -1,6 +1,6 @@
{
"name": "@actual-app/api",
"version": "25.9.0",
"version": "25.8.0",
"license": "MIT",
"description": "An API for Actual",
"engines": {

View File

@@ -1,73 +0,0 @@
#!/usr/bin/env node
// This script is used in GitHub Actions to get the next version based on the current package.json version.
// It supports three types of versioning: nightly, hotfix, and monthly.
import fs from 'node:fs';
import { parseArgs } from 'node:util';
// eslint-disable-next-line import/extensions
import { getNextVersion } from '../src/versions/get-next-package-version.js';
const args = process.argv;
const options = {
'package-json': {
type: 'string',
short: 'p',
},
type: {
type: 'string', // nightly, hotfix, monthly, auto
short: 't',
},
update: {
type: 'boolean',
short: 'u',
default: false,
},
};
const { values } = parseArgs({
args,
options,
allowPositionals: true,
});
if (!values['package-json']) {
console.error(
'Please specify the path to package.json using --package-json or -p option.',
);
process.exit(1);
}
try {
const packageJsonPath = values['package-json'];
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const currentVersion = packageJson.version;
let newVersion;
try {
newVersion = getNextVersion({
currentVersion,
type: values.type,
currentDate: new Date(),
});
} catch (e) {
console.error(e.message);
process.exit(1);
}
process.stdout.write(newVersion);
if (values.update) {
packageJson.version = newVersion;
fs.writeFileSync(
packageJsonPath,
JSON.stringify(packageJson, null, 2) + '\n',
'utf8',
);
}
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}

View File

@@ -1,11 +0,0 @@
{
"name": "@actual-app/ci-actions",
"private": true,
"type": "module",
"devDependencies": {
"vitest": "^3.2.4"
},
"scripts": {
"test": "vitest"
}
}

View File

@@ -1,72 +0,0 @@
function parseVersion(version) {
const [y, m, p] = version.split('.');
return {
versionYear: parseInt(y, 10),
versionMonth: parseInt(m, 10),
versionHotfix: parseInt(p, 10),
};
}
function computeNextMonth(versionYear, versionMonth) {
// Create date and add 1 month
const versionDate = new Date(2000 + versionYear, versionMonth - 1, 1); // month is 0-indexed
const nextVersionMonthDate = new Date(
versionDate.getFullYear(),
versionDate.getMonth() + 1,
1,
);
// Format back to YY.M format
const fullYear = nextVersionMonthDate.getFullYear();
const nextVersionYear = fullYear.toString().slice(fullYear < 2100 ? -2 : -3);
const nextVersionMonth = nextVersionMonthDate.getMonth() + 1; // Convert back to 1-indexed
return { nextVersionYear, nextVersionMonth };
}
// Determine logical type from 'auto' based on the current date and version
function resolveType(type, currentDate, versionYear, versionMonth) {
if (type !== 'auto') return type;
const inPatchMonth =
currentDate.getFullYear() === 2000 + versionYear &&
currentDate.getMonth() + 1 === versionMonth;
if (inPatchMonth && currentDate.getDate() <= 25) return 'hotfix';
return 'monthly';
}
export function getNextVersion({
currentVersion,
type,
currentDate = new Date(),
}) {
const { versionYear, versionMonth, versionHotfix } =
parseVersion(currentVersion);
const { nextVersionYear, nextVersionMonth } = computeNextMonth(
versionYear,
versionMonth,
);
const resolvedType = resolveType(
type,
currentDate,
versionYear,
versionMonth,
);
// Format date stamp once for nightly
const currentDateString = currentDate
.toISOString()
.split('T')[0]
.replaceAll('-', '');
switch (resolvedType) {
case 'nightly':
return `${nextVersionYear}.${nextVersionMonth}.0-nightly.${currentDateString}`;
case 'hotfix':
return `${versionYear}.${versionMonth}.${versionHotfix + 1}`;
case 'monthly':
return `${nextVersionYear}.${nextVersionMonth}.0`;
default:
throw new Error(
'Invalid type specified. Use “auto”, “nightly”, “hotfix”, or “monthly”.',
);
}
}

View File

@@ -1,85 +0,0 @@
import { describe, it, expect } from 'vitest';
import { getNextVersion } from './get-next-package-version';
describe('getNextVersion (lib)', () => {
it('hotfix increments patch', () => {
expect(
getNextVersion({
currentVersion: '25.8.1',
type: 'hotfix',
currentDate: new Date('2025-08-10'),
}),
).toBe('25.8.2');
});
it('monthly advances month same year', () => {
expect(
getNextVersion({
currentVersion: '25.8.3',
type: 'monthly',
currentDate: new Date('2025-08-15'),
}),
).toBe('25.9.0');
});
it('monthly wraps year December -> January', () => {
expect(
getNextVersion({
currentVersion: '25.12.3',
type: 'monthly',
currentDate: new Date('2025-12-05'),
}),
).toBe('26.1.0');
});
it('nightly format with date stamp', () => {
expect(
getNextVersion({
currentVersion: '25.8.1',
type: 'nightly',
currentDate: new Date('2025-08-22'),
}),
).toBe('25.9.0-nightly.20250822');
});
it('auto before 25th -> hotfix', () => {
expect(
getNextVersion({
currentVersion: '25.8.4',
type: 'auto',
currentDate: new Date('2025-08-20'),
}),
).toBe('25.8.5');
});
it('auto after 25th (same month) -> monthly', () => {
expect(
getNextVersion({
currentVersion: '25.8.4',
type: 'auto',
currentDate: new Date('2025-08-27'),
}),
).toBe('25.9.0');
});
it('auto after 25th (next month) -> monthly', () => {
expect(
getNextVersion({
currentVersion: '25.8.4',
type: 'auto',
currentDate: new Date('2025-09-02'),
}),
).toBe('25.9.0');
});
it('invalid type throws', () => {
expect(() =>
getNextVersion({
currentVersion: '25.8.4',
type: 'unknown',
currentDate: new Date('2025-08-10'),
}),
).toThrow(/Invalid type/);
});
});

View File

@@ -1,9 +0,0 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
include: ['src/**/*.test.(js|jsx|ts|tsx)'],
environment: 'node',
},
});

View File

@@ -13,9 +13,9 @@
},
"devDependencies": {
"@svgr/cli": "^8.1.0",
"@types/react": "^19.1.12",
"react": "19.1.1",
"react-dom": "19.1.1",
"@types/react": "^19.1.4",
"react": "19.1.0",
"react-dom": "19.1.0",
"vitest": "^3.2.4"
},
"exports": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -85,73 +85,7 @@ test.describe('Schedules', () => {
'Show completed schedules',
);
await expect(page).toMatchThemeScreenshots();
});
test('creates two new schedules, posts both transactions and later completes one', async () => {
test.setTimeout(40000);
// Adding two schedules with the same payee and account and amount, mimicking two different subscriptions
await schedulesPage.addNewSchedule({
payee: 'Apple',
account: 'HSBC',
amount: 5,
});
await schedulesPage.addNewSchedule({
payee: 'Apple',
account: 'HSBC',
amount: 5,
});
const schedule = schedulesPage.getNthSchedule(2);
await expect(schedule.payee).toHaveText('Apple');
await expect(schedule.account).toHaveText('HSBC');
await expect(schedule.amount).toHaveText('~5.00');
await expect(schedule.status).toHaveText('Due');
await expect(page).toMatchThemeScreenshots();
const schedule2 = schedulesPage.getNthSchedule(3);
await expect(schedule2.payee).toHaveText('Apple');
await expect(schedule2.account).toHaveText('HSBC');
await expect(schedule2.amount).toHaveText('~5.00');
await expect(schedule2.status).toHaveText('Due');
await expect(page).toMatchThemeScreenshots();
await schedulesPage.postNthSchedule(2);
await expect(schedulesPage.getNthSchedule(2).status).toHaveText('Paid');
await expect(schedulesPage.getNthSchedule(3).status).toHaveText('Due');
await expect(page).toMatchThemeScreenshots();
await schedulesPage.postNthSchedule(3);
await expect(schedulesPage.getNthSchedule(2).status).toHaveText('Paid');
await expect(schedulesPage.getNthSchedule(3).status).toHaveText('Paid');
await expect(page).toMatchThemeScreenshots();
// Go to transactions page
const accountPage = await navigation.goToAccountPage('HSBC');
const transaction = accountPage.getNthTransaction(0);
await expect(transaction.payee).toHaveText('Apple');
await expect(transaction.category).toHaveText('Categorize');
await expect(transaction.debit).toHaveText('5.00');
await expect(transaction.credit).toHaveText('');
// Go to transactions page
const transaction2 = accountPage.getNthTransaction(1);
await expect(transaction2.payee).toHaveText('Apple');
await expect(transaction2.category).toHaveText('Categorize');
await expect(transaction2.debit).toHaveText('5.00');
await expect(transaction2.credit).toHaveText('');
const icon = transaction.payee.getByTestId('schedule-icon');
await icon.hover();
await expect(page).toMatchThemeScreenshots();
const icon2 = transaction2.payee.getByTestId('schedule-icon');
await icon2.hover();
await expect(page).toMatchThemeScreenshots();
});
test('creates a "full" list of schedules', async () => {
// Schedules search shouldn't shrink with many schedules
for (let i = 0; i < 10; i++) {
await schedulesPage.addNewSchedule({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Some files were not shown because too many files have changed in this diff Show More