Compare commits
47 Commits
v0.14.0
...
test-cover
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f36155f3a5 | ||
![]() |
09335fcd79 | ||
![]() |
823a2c8b73 | ||
![]() |
741866e742 | ||
![]() |
41622678b0 | ||
![]() |
449b953401 | ||
![]() |
6345ce5195 | ||
![]() |
95651f6163 | ||
![]() |
b02c1545b7 | ||
![]() |
2deaab9b24 | ||
![]() |
f0d4853533 | ||
![]() |
af81ae812f | ||
![]() |
bae76064e5 | ||
![]() |
07d9198cc8 | ||
![]() |
a2e07ea2f7 | ||
![]() |
864c762fe2 | ||
![]() |
167bb4e8a0 | ||
![]() |
4cf64ede74 | ||
![]() |
bb309fea6f | ||
![]() |
1133362028 | ||
![]() |
eb9226bd4a | ||
![]() |
a9abdcc37e | ||
![]() |
6ace93bdbf | ||
![]() |
b89197939a | ||
![]() |
da788106af | ||
![]() |
49e92e6f1d | ||
![]() |
a6c3276104 | ||
![]() |
6388bfc714 | ||
![]() |
bebc3b181d | ||
![]() |
5a6d561c1a | ||
![]() |
5ba575fdfd | ||
![]() |
dcf8bbd804 | ||
![]() |
ff93ffd0b1 | ||
![]() |
395c09df92 | ||
![]() |
4c903cd08b | ||
![]() |
64cb98717c | ||
![]() |
b0e4ce54fb | ||
![]() |
d67a37002f | ||
![]() |
965ff8bc3f | ||
![]() |
400a495ad2 | ||
![]() |
09d0822a8d | ||
![]() |
7016c20ccc | ||
![]() |
df54895805 | ||
![]() |
62d5e6fe51 | ||
![]() |
4615a0b7ea | ||
![]() |
280d603b14 | ||
![]() |
36271f0749 |
@@ -5,8 +5,11 @@ BACKEND_PORT=3000
|
||||
WEB_PORT=3001
|
||||
|
||||
echo "Configuring backend environment variables..."
|
||||
|
||||
cd packages/backend
|
||||
|
||||
rm -rf .env
|
||||
|
||||
echo "
|
||||
PORT=$BACKEND_PORT
|
||||
WEB_APP_URL=http://localhost:$WEB_PORT
|
||||
@@ -21,23 +24,35 @@ WEBHOOK_SECRET_KEY=sample_webhook_secret_key
|
||||
APP_SECRET_KEY=sample_app_secret_key
|
||||
REDIS_HOST=redis
|
||||
SERVE_WEB_APP_SEPARATELY=true" >> .env
|
||||
|
||||
echo "Installing backend dependencies..."
|
||||
|
||||
yarn
|
||||
|
||||
cd $CURRENT_DIR
|
||||
|
||||
echo "Configuring web environment variables..."
|
||||
|
||||
cd packages/web
|
||||
|
||||
rm -rf .env
|
||||
|
||||
echo "
|
||||
PORT=$WEB_PORT
|
||||
REACT_APP_BACKEND_URL=http://localhost:$BACKEND_PORT
|
||||
" >> .env
|
||||
cd $CURRENT_DIR
|
||||
|
||||
echo "Installing and linking dependencies..."
|
||||
echo "Installing web dependencies..."
|
||||
|
||||
yarn
|
||||
|
||||
cd $CURRENT_DIR
|
||||
|
||||
echo "Migrating database..."
|
||||
|
||||
cd packages/backend
|
||||
|
||||
yarn db:migrate
|
||||
yarn db:seed:user
|
||||
|
||||
echo "Done!"
|
||||
echo "Done!"
|
||||
|
9
.github/workflows/backend.yml
vendored
9
.github/workflows/backend.yml
vendored
@@ -41,8 +41,11 @@ jobs:
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: cd packages/backend && yarn
|
||||
run: yarn
|
||||
working-directory: packages/backend
|
||||
- name: Copy .env-example.test file to .env.test
|
||||
run: cd packages/backend && cp .env-example.test .env.test
|
||||
run: cp .env-example.test .env.test
|
||||
working-directory: packages/backend
|
||||
- name: Run tests
|
||||
run: cd packages/backend && yarn test
|
||||
run: yarn test:coverage
|
||||
working-directory: packages/backend
|
||||
|
24
.github/workflows/ci.yml
vendored
24
.github/workflows/ci.yml
vendored
@@ -18,11 +18,13 @@ jobs:
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: yarn.lock
|
||||
cache-dependency-path: packages/backend/yarn.lock
|
||||
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: cd packages/backend && yarn lint
|
||||
working-directory: packages/backend
|
||||
- run: yarn lint
|
||||
working-directory: packages/backend
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||
start-backend-server:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -35,11 +37,13 @@ jobs:
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: yarn.lock
|
||||
cache-dependency-path: packages/backend/yarn.lock
|
||||
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: cd packages/backend && yarn start
|
||||
working-directory: packages/backend
|
||||
- run: yarn start
|
||||
working-directory: packages/backend
|
||||
env:
|
||||
ENCRYPTION_KEY: sample_encryption_key
|
||||
WEBHOOK_SECRET_KEY: sample_webhook_secret_key
|
||||
@@ -55,11 +59,13 @@ jobs:
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: yarn.lock
|
||||
cache-dependency-path: packages/backend/yarn.lock
|
||||
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: cd packages/backend && yarn start:worker
|
||||
working-directory: packages/backend
|
||||
- run: yarn start:worker
|
||||
working-directory: packages/backend
|
||||
env:
|
||||
ENCRYPTION_KEY: sample_encryption_key
|
||||
WEBHOOK_SECRET_KEY: sample_webhook_secret_key
|
||||
@@ -75,11 +81,13 @@ jobs:
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: yarn.lock
|
||||
cache-dependency-path: packages/web/yarn.lock
|
||||
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
|
||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: cd packages/web && yarn build
|
||||
working-directory: packages/web
|
||||
- run: yarn build
|
||||
working-directory: packages/web
|
||||
env:
|
||||
CI: false
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||
|
12
.github/workflows/playwright.yml
vendored
12
.github/workflows/playwright.yml
vendored
@@ -58,13 +58,21 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
- name: Install web dependencies
|
||||
run: yarn
|
||||
working-directory: ./packages/web
|
||||
- name: Install backend dependencies
|
||||
run: yarn
|
||||
working-directory: ./packages/backend
|
||||
- name: Install e2e-tests dependencies
|
||||
run: yarn
|
||||
working-directory: ./packages/e2e-tests
|
||||
- name: Install Playwright Browsers
|
||||
run: yarn playwright install --with-deps
|
||||
working-directory: ./packages/e2e-tests
|
||||
- name: Build Automatisch web
|
||||
working-directory: ./packages/web
|
||||
run: yarn build
|
||||
working-directory: ./packages/web
|
||||
env:
|
||||
# Keep this until we clean up warnings in build processes
|
||||
CI: false
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,7 +4,6 @@ logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
|
@@ -11,10 +11,12 @@ WORKDIR /automatisch
|
||||
# copy the app, note .dockerignore
|
||||
COPY . /automatisch
|
||||
|
||||
RUN yarn
|
||||
RUN cd packages/web && yarn
|
||||
|
||||
RUN cd packages/web && yarn build
|
||||
|
||||
RUN cd packages/backend && yarn --production
|
||||
|
||||
RUN \
|
||||
rm -rf /usr/local/share/.cache/ && \
|
||||
apk del build-dependencies
|
||||
|
11
lerna.json
11
lerna.json
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"packages": ["packages/*"],
|
||||
"version": "0.14.0",
|
||||
"npmClient": "yarn",
|
||||
"command": {
|
||||
"add": {
|
||||
"exact": true
|
||||
}
|
||||
},
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
32
package.json
32
package.json
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"name": "@automatisch/root",
|
||||
"license": "See LICENSE file",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "lerna run --stream --parallel --scope=@*/{web,backend} dev",
|
||||
"start:web": "lerna run --stream --scope=@*/web dev",
|
||||
"start:backend": "lerna run --stream --scope=@*/backend dev",
|
||||
"build:docs": "cd ./packages/docs && yarn install && yarn build"
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"nohoist": [
|
||||
"**/babel-loader",
|
||||
"**/webpack",
|
||||
"**/@automatisch/web",
|
||||
"**/ajv"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"lerna": "^8.0.0",
|
||||
"prettier": "^2.5.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
@@ -12,6 +12,7 @@
|
||||
"pretest": "APP_ENV=test node ./test/setup/prepare-test-env.js",
|
||||
"test": "APP_ENV=test vitest run",
|
||||
"test:watch": "APP_ENV=test vitest watch",
|
||||
"test:coverage": "yarn test --coverage",
|
||||
"lint": "eslint .",
|
||||
"db:create": "node ./bin/database/create.js",
|
||||
"db:seed:user": "node ./bin/database/seed-user.js",
|
||||
@@ -23,6 +24,7 @@
|
||||
"dependencies": {
|
||||
"@bull-board/express": "^3.10.1",
|
||||
"@casl/ability": "^6.5.0",
|
||||
"@faker-js/faker": "^9.2.0",
|
||||
"@node-saml/passport-saml": "^4.0.4",
|
||||
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
||||
"@sentry/node": "^7.42.0",
|
||||
@@ -36,6 +38,9 @@
|
||||
"crypto-js": "^4.1.1",
|
||||
"debug": "~2.6.9",
|
||||
"dotenv": "^10.0.0",
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"express": "~4.18.2",
|
||||
"express-async-errors": "^3.1.1",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
@@ -61,6 +66,7 @@
|
||||
"pg": "^8.7.1",
|
||||
"php-serialize": "^4.0.2",
|
||||
"pluralize": "^8.0.0",
|
||||
"prettier": "^2.5.1",
|
||||
"raw-body": "^2.5.2",
|
||||
"showdown": "^2.1.0",
|
||||
"uuid": "^9.0.1",
|
||||
@@ -92,10 +98,11 @@
|
||||
"url": "https://github.com/automatisch/automatisch/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitest/coverage-v8": "^2.1.5",
|
||||
"node-gyp": "^10.1.0",
|
||||
"nodemon": "^2.0.13",
|
||||
"supertest": "^6.3.3",
|
||||
"vitest": "^1.1.3"
|
||||
"vitest": "^2.1.5"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -366,6 +366,18 @@ class User extends Base {
|
||||
return now.getTime() - sentAt.getTime() < fourHoursInMilliseconds;
|
||||
}
|
||||
|
||||
toTestTestCoverage() {
|
||||
if (!this.resetPasswordTokenSentAt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sentAt = new Date(this.resetPasswordTokenSentAt);
|
||||
const now = new Date();
|
||||
const fourHoursInMilliseconds = 1000 * 60 * 60 * 4;
|
||||
|
||||
return now.getTime() - sentAt.getTime() < fourHoursInMilliseconds;
|
||||
}
|
||||
|
||||
async sendInvitationEmail() {
|
||||
await this.generateInvitationToken();
|
||||
|
||||
@@ -407,7 +419,7 @@ class User extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
async startTrialPeriod() {
|
||||
startTrialPeriod() {
|
||||
this.trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate();
|
||||
}
|
||||
|
||||
@@ -590,7 +602,7 @@ class User extends Base {
|
||||
await this.generateHash();
|
||||
|
||||
if (appConfig.isCloud) {
|
||||
await this.startTrialPeriod();
|
||||
this.startTrialPeriod();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { DateTime, Duration } from 'luxon';
|
||||
import appConfig from '../config/app.js';
|
||||
import Base from './base.js';
|
||||
import AccessToken from './access-token.js';
|
||||
@@ -12,6 +13,12 @@ import Step from './step.js';
|
||||
import Subscription from './subscription.ee.js';
|
||||
import UsageData from './usage-data.ee.js';
|
||||
import User from './user.js';
|
||||
import deleteUserQueue from '../queues/delete-user.ee.js';
|
||||
import emailQueue from '../queues/email.js';
|
||||
import {
|
||||
REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||
} from '../helpers/remove-job-configuration.js';
|
||||
import { createUser } from '../../test/factories/user.js';
|
||||
import { createConnection } from '../../test/factories/connection.js';
|
||||
import { createRole } from '../../test/factories/role.js';
|
||||
@@ -604,4 +611,268 @@ describe('User model', () => {
|
||||
).rejects.toThrowError('currentPassword: is incorrect.');
|
||||
});
|
||||
});
|
||||
|
||||
it('softRemove should soft remove the user, its associations and queue it for hard deletion in 30 days', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const date = new Date(2024, 10, 12, 12, 50, 0, 0);
|
||||
vi.setSystemTime(date);
|
||||
|
||||
const user = await createUser();
|
||||
|
||||
const softRemoveAssociationsSpy = vi
|
||||
.spyOn(user, 'softRemoveAssociations')
|
||||
.mockReturnValue();
|
||||
|
||||
const deleteUserQueueAddSpy = vi
|
||||
.spyOn(deleteUserQueue, 'add')
|
||||
.mockResolvedValue();
|
||||
|
||||
await user.softRemove();
|
||||
|
||||
const refetchedSoftDeletedUser = await user.$query().withSoftDeleted();
|
||||
|
||||
const millisecondsFor30Days = Duration.fromObject({ days: 30 }).toMillis();
|
||||
const jobName = `Delete user - ${user.id}`;
|
||||
const jobPayload = { id: user.id };
|
||||
|
||||
const jobOptions = {
|
||||
delay: millisecondsFor30Days,
|
||||
};
|
||||
|
||||
expect(softRemoveAssociationsSpy).toHaveBeenCalledOnce();
|
||||
expect(refetchedSoftDeletedUser.deletedAt).toStrictEqual(date);
|
||||
|
||||
expect(deleteUserQueueAddSpy).toHaveBeenCalledWith(
|
||||
jobName,
|
||||
jobPayload,
|
||||
jobOptions
|
||||
);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it.todo('softRemoveAssociations');
|
||||
|
||||
it('sendResetPasswordEmail should generate reset password token and queue to send reset password email', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const date = new Date(2024, 10, 12, 14, 33, 0, 0);
|
||||
vi.setSystemTime(date);
|
||||
|
||||
const user = await createUser();
|
||||
|
||||
const generateResetPasswordTokenSpy = vi
|
||||
.spyOn(user, 'generateResetPasswordToken')
|
||||
.mockReturnValue();
|
||||
|
||||
const emailQueueAddSpy = vi.spyOn(emailQueue, 'add').mockResolvedValue();
|
||||
|
||||
await user.sendResetPasswordEmail();
|
||||
|
||||
const refetchedUser = await user.$query();
|
||||
const jobName = `Reset Password Email - ${user.id}`;
|
||||
|
||||
const jobPayload = {
|
||||
email: refetchedUser.email,
|
||||
subject: 'Reset Password',
|
||||
template: 'reset-password-instructions.ee',
|
||||
params: {
|
||||
token: refetchedUser.resetPasswordToken,
|
||||
webAppUrl: appConfig.webAppUrl,
|
||||
fullName: refetchedUser.fullName,
|
||||
},
|
||||
};
|
||||
|
||||
const jobOptions = {
|
||||
removeOnComplete: REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||
removeOnFail: REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||
};
|
||||
|
||||
expect(generateResetPasswordTokenSpy).toHaveBeenCalledOnce();
|
||||
|
||||
expect(emailQueueAddSpy).toHaveBeenCalledWith(
|
||||
jobName,
|
||||
jobPayload,
|
||||
jobOptions
|
||||
);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe('isResetPasswordTokenValid', () => {
|
||||
it('should return true when resetPasswordTokenSentAt is within the next four hours', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const date = DateTime.fromObject(
|
||||
{ year: 2024, month: 11, day: 12, hour: 16, minute: 30 },
|
||||
{ zone: 'UTC+0' }
|
||||
);
|
||||
|
||||
vi.setSystemTime(date);
|
||||
|
||||
const user = new User();
|
||||
user.resetPasswordTokenSentAt = '2024-11-12T13:31:00.000Z';
|
||||
|
||||
expect(user.isResetPasswordTokenValid()).toBe(true);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should return false when there is no resetPasswordTokenSentAt', async () => {
|
||||
const user = new User();
|
||||
|
||||
expect(user.isResetPasswordTokenValid()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when resetPasswordTokenSentAt is older than four hours', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const date = DateTime.fromObject(
|
||||
{ year: 2024, month: 11, day: 12, hour: 16, minute: 30 },
|
||||
{ zone: 'UTC+0' }
|
||||
);
|
||||
|
||||
vi.setSystemTime(date);
|
||||
|
||||
const user = new User();
|
||||
user.resetPasswordTokenSentAt = '2024-11-12T12:29:00.000Z';
|
||||
|
||||
expect(user.isResetPasswordTokenValid()).toBe(false);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
it('sendInvitationEmail should generate invitation token and queue to send invitation email', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const date = DateTime.fromObject(
|
||||
{ year: 2024, month: 11, day: 12, hour: 17, minute: 10 },
|
||||
{ zone: 'UTC+0' }
|
||||
);
|
||||
|
||||
vi.setSystemTime(date);
|
||||
|
||||
const user = await createUser();
|
||||
|
||||
const generateInvitationTokenSpy = vi
|
||||
.spyOn(user, 'generateInvitationToken')
|
||||
.mockReturnValue();
|
||||
|
||||
const emailQueueAddSpy = vi.spyOn(emailQueue, 'add').mockResolvedValue();
|
||||
|
||||
await user.sendInvitationEmail();
|
||||
|
||||
const refetchedUser = await user.$query();
|
||||
const jobName = `Invitation Email - ${refetchedUser.id}`;
|
||||
|
||||
const jobPayload = {
|
||||
email: refetchedUser.email,
|
||||
subject: 'You are invited!',
|
||||
template: 'invitation-instructions',
|
||||
params: {
|
||||
fullName: refetchedUser.fullName,
|
||||
acceptInvitationUrl: refetchedUser.acceptInvitationUrl,
|
||||
},
|
||||
};
|
||||
|
||||
const jobOptions = {
|
||||
removeOnComplete: REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||
removeOnFail: REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||
};
|
||||
|
||||
expect(generateInvitationTokenSpy).toHaveBeenCalledOnce();
|
||||
|
||||
expect(emailQueueAddSpy).toHaveBeenCalledWith(
|
||||
jobName,
|
||||
jobPayload,
|
||||
jobOptions
|
||||
);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe('isInvitationTokenValid', () => {
|
||||
it('should return truen when invitationTokenSentAt is within the next four hours', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const date = DateTime.fromObject(
|
||||
{ year: 2024, month: 11, day: 14, hour: 14, minute: 30 },
|
||||
{ zone: 'UTC+0' }
|
||||
);
|
||||
|
||||
vi.setSystemTime(date);
|
||||
|
||||
const user = new User();
|
||||
user.invitationTokenSentAt = '2024-11-14T13:31:00.000Z';
|
||||
|
||||
expect(user.isInvitationTokenValid()).toBe(true);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should return false when there is no invitationTokenSentAt', async () => {
|
||||
const user = new User();
|
||||
|
||||
expect(user.isInvitationTokenValid()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when invitationTokenSentAt is older than seventy two hours', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const date = DateTime.fromObject(
|
||||
{ year: 2024, month: 11, day: 14, hour: 14, minute: 30 },
|
||||
{ zone: 'UTC+0' }
|
||||
);
|
||||
|
||||
vi.setSystemTime(date);
|
||||
|
||||
const user = new User();
|
||||
user.invitationTokenSentAt = '2024-11-11T14:20:00.000Z';
|
||||
|
||||
expect(user.isInvitationTokenValid()).toBe(false);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateHash', () => {
|
||||
it('should hash password and re-assign it', async () => {
|
||||
const user = new User();
|
||||
user.password = 'sample-password';
|
||||
|
||||
await user.generateHash();
|
||||
|
||||
expect(user.password).not.toBe('sample-password');
|
||||
expect(await user.login('sample-password')).toBe(true);
|
||||
});
|
||||
|
||||
it('should do nothing when password does not exist', async () => {
|
||||
const user = new User();
|
||||
|
||||
await user.generateHash();
|
||||
|
||||
expect(user.password).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('startTrialPeriod should assign trialExpiryDate 30 days from now', () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const date = DateTime.fromObject(
|
||||
{ year: 2024, month: 11, day: 14, hour: 16 },
|
||||
{ zone: 'UTC+0' }
|
||||
);
|
||||
|
||||
vi.setSystemTime(date);
|
||||
|
||||
const user = new User();
|
||||
|
||||
user.startTrialPeriod();
|
||||
|
||||
expect(user.trialExpiryDate).toBe('2024-12-14');
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
@@ -2,8 +2,25 @@ import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
root: './',
|
||||
environment: 'node',
|
||||
setupFiles: ['./test/setup/global-hooks.js'],
|
||||
globals: true,
|
||||
reporters: process.env.GITHUB_ACTIONS ? ['dot', 'github-actions'] : ['dot'],
|
||||
coverage: {
|
||||
reportOnFailure: true,
|
||||
provider: 'v8',
|
||||
reportsDirectory: './coverage',
|
||||
reporter: ['text', 'lcov'],
|
||||
all: true,
|
||||
include: ['**/src/models/**', '**/src/controllers/**'],
|
||||
thresholds: {
|
||||
autoUpdate: true,
|
||||
statements: 93.41,
|
||||
branches: 93.46,
|
||||
functions: 95.95,
|
||||
lines: 93.41,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
4821
packages/backend/yarn.lock
Normal file
4821
packages/backend/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
1
packages/docs/.gitignore
vendored
Normal file
1
packages/docs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
pages/.vitepress/cache
|
@@ -4,6 +4,7 @@
|
||||
"license": "See LICENSE file",
|
||||
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vitepress dev pages --port 3002",
|
||||
"build": "vitepress build pages",
|
||||
|
@@ -6,11 +6,19 @@ Clone main branch of Automatisch.
|
||||
git clone git@github.com:automatisch/automatisch.git
|
||||
```
|
||||
|
||||
Then, install the dependencies.
|
||||
Then, install the dependencies for both backend and web packages separately.
|
||||
|
||||
```bash
|
||||
cd automatisch
|
||||
|
||||
# Install backend dependencies
|
||||
cd packages/backend
|
||||
yarn install
|
||||
|
||||
# Install web dependencies
|
||||
cd packages/web
|
||||
yarn install
|
||||
|
||||
```
|
||||
|
||||
## Backend
|
||||
@@ -53,12 +61,14 @@ yarn db:seed:user
|
||||
Start the main backend server.
|
||||
|
||||
```bash
|
||||
cd packages/backend
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Start the worker server in another terminal tab.
|
||||
|
||||
```bash
|
||||
cd packages/backend
|
||||
yarn worker
|
||||
```
|
||||
|
||||
@@ -84,6 +94,7 @@ It will automatically open [http://localhost:3001](http://localhost:3001) in you
|
||||
|
||||
```bash
|
||||
cd packages/docs
|
||||
yarn install
|
||||
yarn dev
|
||||
```
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Repository Structure
|
||||
|
||||
We use `lerna` with `yarn workspaces` to manage the mono repository. We have the following packages:
|
||||
We manage a monorepo structure with the following packages:
|
||||
|
||||
```
|
||||
.
|
||||
@@ -15,3 +15,5 @@ We use `lerna` with `yarn workspaces` to manage the mono repository. We have the
|
||||
- `docs` - The docs package contains the documentation website.
|
||||
- `e2e-tests` - The e2e-tests package contains the end-to-end tests for the internal usage.
|
||||
- `web` - The web package contains the frontend application of Automatisch.
|
||||
|
||||
Each package is independently managed, and has its own package.json file to manage dependencies. This allows for better isolation and flexibility.
|
||||
|
1192
packages/docs/yarn.lock
Normal file
1192
packages/docs/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,10 +29,12 @@
|
||||
"@playwright/test": "^1.45.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"knex": "^2.4.0",
|
||||
"luxon": "^3.4.4",
|
||||
"micro": "^10.0.1",
|
||||
"pg": "^8.12.0",
|
||||
|
1099
packages/e2e-tests/yarn.lock
Normal file
1099
packages/e2e-tests/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"axios": "^1.6.0",
|
||||
"clipboard-copy": "^4.0.1",
|
||||
"compare-versions": "^4.1.3",
|
||||
"lodash": "^4.17.21",
|
||||
|
@@ -6,7 +6,7 @@ import FormHelperText from '@mui/material/FormHelperText';
|
||||
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
||||
import ClearIcon from '@mui/icons-material/Clear';
|
||||
import { ActionButtonsWrapper } from './style';
|
||||
import ClickAwayListener from '@mui/base/ClickAwayListener';
|
||||
import { ClickAwayListener } from '@mui/base/ClickAwayListener';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import { createEditor } from 'slate';
|
||||
import { Editable, ReactEditor } from 'slate-react';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import ClickAwayListener from '@mui/base/ClickAwayListener';
|
||||
import { ClickAwayListener } from '@mui/base/ClickAwayListener';
|
||||
import FormHelperText from '@mui/material/FormHelperText';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import * as React from 'react';
|
||||
|
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
||||
import Button from '@mui/material/Button';
|
||||
import ButtonGroup from '@mui/material/ButtonGroup';
|
||||
import ClickAwayListener from '@mui/material/ClickAwayListener';
|
||||
import { ClickAwayListener } from '@mui/base/ClickAwayListener';
|
||||
import Grow from '@mui/material/Grow';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import MenuList from '@mui/material/MenuList';
|
||||
|
11069
packages/web/yarn.lock
Normal file
11069
packages/web/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user