Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3de18ab46f | ||
![]() |
54f509ee38 | ||
![]() |
978ceaadb6 | ||
![]() |
770b07179f | ||
![]() |
6d15167ad9 | ||
![]() |
39cba6bc74 | ||
![]() |
9558e66abf | ||
![]() |
ff7908955e | ||
![]() |
26b095b835 | ||
![]() |
feba2a32f9 | ||
![]() |
5090ece9b6 | ||
![]() |
221b19586e | ||
![]() |
3346c14255 | ||
![]() |
6e97e023c9 | ||
![]() |
b26e2ecf2e | ||
![]() |
d896238f23 | ||
![]() |
d2c8f5a75c | ||
![]() |
ce430d238c | ||
![]() |
ee397441ed | ||
![]() |
ba82d986c1 | ||
![]() |
2361cb521e | ||
![]() |
05f8d95281 | ||
![]() |
6c60b1c263 | ||
![]() |
0c32a0693c | ||
![]() |
807faa3c93 | ||
![]() |
fb53e37f7a | ||
![]() |
4ffdf98e16 | ||
![]() |
09335fcd79 |
2
.github/workflows/backend.yml
vendored
2
.github/workflows/backend.yml
vendored
@@ -47,5 +47,5 @@ jobs:
|
|||||||
run: cp .env-example.test .env.test
|
run: cp .env-example.test .env.test
|
||||||
working-directory: packages/backend
|
working-directory: packages/backend
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: yarn test
|
run: yarn test:coverage
|
||||||
working-directory: packages/backend
|
working-directory: packages/backend
|
||||||
|
47
.github/workflows/playwright.yml
vendored
47
.github/workflows/playwright.yml
vendored
@@ -3,12 +3,13 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
# TODO: Add pull request after optimizing the total excecution time of the test suite.
|
||||||
paths:
|
# pull_request:
|
||||||
- 'packages/backend/**'
|
# paths:
|
||||||
- 'packages/e2e-tests/**'
|
# - 'packages/backend/**'
|
||||||
- 'packages/web/**'
|
# - 'packages/e2e-tests/**'
|
||||||
- '!packages/backend/src/apps/**'
|
# - 'packages/web/**'
|
||||||
|
# - '!packages/backend/src/apps/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -55,44 +56,27 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: 18
|
||||||
cache: 'yarn'
|
|
||||||
cache-dependency-path: |
|
|
||||||
packages/backend/yarn.lock
|
|
||||||
packages/web/yarn.lock
|
|
||||||
packages/e2e-tests/yarn.lock
|
|
||||||
- name: Install backend dependencies
|
|
||||||
run: yarn --frozen-lockfile
|
|
||||||
working-directory: ./packages/backend
|
|
||||||
- name: Install web dependencies
|
- name: Install web dependencies
|
||||||
run: yarn --frozen-lockfile
|
run: yarn
|
||||||
working-directory: ./packages/web
|
working-directory: ./packages/web
|
||||||
|
- name: Install backend dependencies
|
||||||
|
run: yarn
|
||||||
|
working-directory: ./packages/backend
|
||||||
- name: Install e2e-tests dependencies
|
- name: Install e2e-tests dependencies
|
||||||
run: yarn --frozen-lockfile
|
run: yarn
|
||||||
working-directory: ./packages/e2e-tests
|
working-directory: ./packages/e2e-tests
|
||||||
- name: Get installed Playwright version
|
|
||||||
id: playwright-version
|
|
||||||
run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package.json').devDependencies['@playwright/test'])")" >> $GITHUB_ENV
|
|
||||||
working-directory: ./packages/e2e-tests
|
|
||||||
- name: Cache playwright binaries
|
|
||||||
uses: actions/cache@v3
|
|
||||||
id: playwright-cache
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cache/ms-playwright
|
|
||||||
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: yarn playwright install --with-deps
|
run: yarn playwright install --with-deps
|
||||||
working-directory: ./packages/e2e-tests
|
working-directory: ./packages/e2e-tests
|
||||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
|
||||||
- name: Build Automatisch web
|
- name: Build Automatisch web
|
||||||
run: yarn build
|
run: yarn build
|
||||||
|
working-directory: ./packages/web
|
||||||
env:
|
env:
|
||||||
# Keep this until we clean up warnings in build processes
|
# Keep this until we clean up warnings in build processes
|
||||||
CI: false
|
CI: false
|
||||||
working-directory: ./packages/web
|
|
||||||
- name: Migrate database
|
- name: Migrate database
|
||||||
working-directory: ./packages/backend
|
working-directory: ./packages/backend
|
||||||
run: yarn db:migrate
|
run: yarn db:migrate
|
||||||
@@ -132,7 +116,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
LOGIN_EMAIL: user@automatisch.io
|
LOGIN_EMAIL: user@automatisch.io
|
||||||
LOGIN_PASSWORD: sample
|
LOGIN_PASSWORD: sample
|
||||||
BACKEND_APP_URL: http://localhost:3000
|
|
||||||
BASE_URL: http://localhost:3000
|
BASE_URL: http://localhost:3000
|
||||||
GITHUB_CLIENT_ID: 1c0417daf898adfbd99a
|
GITHUB_CLIENT_ID: 1c0417daf898adfbd99a
|
||||||
GITHUB_CLIENT_SECRET: 3328fa814dd582ccd03dbe785cfd683fb8da92b3
|
GITHUB_CLIENT_SECRET: 3328fa814dd582ccd03dbe785cfd683fb8da92b3
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
"pretest": "APP_ENV=test node ./test/setup/prepare-test-env.js",
|
"pretest": "APP_ENV=test node ./test/setup/prepare-test-env.js",
|
||||||
"test": "APP_ENV=test vitest run",
|
"test": "APP_ENV=test vitest run",
|
||||||
"test:watch": "APP_ENV=test vitest watch",
|
"test:watch": "APP_ENV=test vitest watch",
|
||||||
|
"test:coverage": "yarn test --coverage",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"db:create": "node ./bin/database/create.js",
|
"db:create": "node ./bin/database/create.js",
|
||||||
"db:seed:user": "node ./bin/database/seed-user.js",
|
"db:seed:user": "node ./bin/database/seed-user.js",
|
||||||
@@ -97,10 +98,11 @@
|
|||||||
"url": "https://github.com/automatisch/automatisch/issues"
|
"url": "https://github.com/automatisch/automatisch/issues"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@vitest/coverage-v8": "^2.1.5",
|
||||||
"node-gyp": "^10.1.0",
|
"node-gyp": "^10.1.0",
|
||||||
"nodemon": "^2.0.13",
|
"nodemon": "^2.0.13",
|
||||||
"supertest": "^6.3.3",
|
"supertest": "^6.3.3",
|
||||||
"vitest": "^1.1.3"
|
"vitest": "^2.1.5"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@@ -7,7 +7,7 @@ export default async (request, response) => {
|
|||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const roleMappings = await samlAuthProvider
|
const roleMappings = await samlAuthProvider
|
||||||
.$relatedQuery('samlAuthProvidersRoleMappings')
|
.$relatedQuery('roleMappings')
|
||||||
.orderBy('remote_role_name', 'asc');
|
.orderBy('remote_role_name', 'asc');
|
||||||
|
|
||||||
renderObject(response, roleMappings);
|
renderObject(response, roleMappings);
|
||||||
|
@@ -8,15 +8,14 @@ export default async (request, response) => {
|
|||||||
.findById(samlAuthProviderId)
|
.findById(samlAuthProviderId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const samlAuthProvidersRoleMappings =
|
const roleMappings = await samlAuthProvider.updateRoleMappings(
|
||||||
await samlAuthProvider.updateRoleMappings(
|
roleMappingsParams(request)
|
||||||
samlAuthProvidersRoleMappingsParams(request)
|
);
|
||||||
);
|
|
||||||
|
|
||||||
renderObject(response, samlAuthProvidersRoleMappings);
|
renderObject(response, roleMappings);
|
||||||
};
|
};
|
||||||
|
|
||||||
const samlAuthProvidersRoleMappingsParams = (request) => {
|
const roleMappingsParams = (request) => {
|
||||||
const roleMappings = request.body;
|
const roleMappings = request.body;
|
||||||
|
|
||||||
return roleMappings.map(({ roleId, remoteRoleName }) => ({
|
return roleMappings.map(({ roleId, remoteRoleName }) => ({
|
||||||
|
@@ -6,7 +6,7 @@ import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by
|
|||||||
import { createRole } from '../../../../../../test/factories/role.js';
|
import { createRole } from '../../../../../../test/factories/role.js';
|
||||||
import { createUser } from '../../../../../../test/factories/user.js';
|
import { createUser } from '../../../../../../test/factories/user.js';
|
||||||
import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js';
|
import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js';
|
||||||
import { createSamlAuthProvidersRoleMapping } from '../../../../../../test/factories/saml-auth-providers-role-mapping.js';
|
import { createRoleMapping } from '../../../../../../test/factories/role-mapping.js';
|
||||||
import createRoleMappingsMock from '../../../../../../test/mocks/rest/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js';
|
import createRoleMappingsMock from '../../../../../../test/mocks/rest/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js';
|
||||||
import * as license from '../../../../../helpers/license.ee.js';
|
import * as license from '../../../../../helpers/license.ee.js';
|
||||||
|
|
||||||
@@ -21,12 +21,12 @@ describe('PATCH /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mappi
|
|||||||
|
|
||||||
samlAuthProvider = await createSamlAuthProvider();
|
samlAuthProvider = await createSamlAuthProvider();
|
||||||
|
|
||||||
await createSamlAuthProvidersRoleMapping({
|
await createRoleMapping({
|
||||||
samlAuthProviderId: samlAuthProvider.id,
|
samlAuthProviderId: samlAuthProvider.id,
|
||||||
remoteRoleName: 'Viewer',
|
remoteRoleName: 'Viewer',
|
||||||
});
|
});
|
||||||
|
|
||||||
await createSamlAuthProvidersRoleMapping({
|
await createRoleMapping({
|
||||||
samlAuthProviderId: samlAuthProvider.id,
|
samlAuthProviderId: samlAuthProvider.id,
|
||||||
remoteRoleName: 'Editor',
|
remoteRoleName: 'Editor',
|
||||||
});
|
});
|
||||||
@@ -64,7 +64,7 @@ describe('PATCH /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mappi
|
|||||||
|
|
||||||
it('should delete role mappings when given empty role mappings', async () => {
|
it('should delete role mappings when given empty role mappings', async () => {
|
||||||
const existingRoleMappings = await samlAuthProvider.$relatedQuery(
|
const existingRoleMappings = await samlAuthProvider.$relatedQuery(
|
||||||
'samlAuthProvidersRoleMappings'
|
'roleMappings'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(existingRoleMappings.length).toBe(2);
|
expect(existingRoleMappings.length).toBe(2);
|
||||||
@@ -149,34 +149,4 @@ describe('PATCH /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mappi
|
|||||||
.send(roleMappings)
|
.send(roleMappings)
|
||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not delete existing role mapping when error thrown', async () => {
|
|
||||||
const roleMappings = [
|
|
||||||
{
|
|
||||||
roleId: userRole.id,
|
|
||||||
remoteRoleName: {
|
|
||||||
invalid: 'data',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const roleMappingsBeforeRequest = await samlAuthProvider.$relatedQuery(
|
|
||||||
'samlAuthProvidersRoleMappings'
|
|
||||||
);
|
|
||||||
|
|
||||||
await request(app)
|
|
||||||
.patch(
|
|
||||||
`/api/v1/admin/saml-auth-providers/${samlAuthProvider.id}/role-mappings`
|
|
||||||
)
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send(roleMappings)
|
|
||||||
.expect(422);
|
|
||||||
|
|
||||||
const roleMappingsAfterRequest = await samlAuthProvider.$relatedQuery(
|
|
||||||
'samlAuthProvidersRoleMappings'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(roleMappingsBeforeRequest).toStrictEqual(roleMappingsAfterRequest);
|
|
||||||
expect(roleMappingsAfterRequest.length).toBe(2);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@@ -87,14 +87,14 @@ describe('GET /api/v1/apps/:appKey/connections', () => {
|
|||||||
|
|
||||||
it('should return not found response for invalid connection UUID', async () => {
|
it('should return not found response for invalid connection UUID', async () => {
|
||||||
await createPermission({
|
await createPermission({
|
||||||
action: 'update',
|
action: 'read',
|
||||||
subject: 'Connection',
|
subject: 'Connection',
|
||||||
roleId: currentUserRole.id,
|
roleId: currentUserRole.id,
|
||||||
conditions: ['isCreator'],
|
conditions: ['isCreator'],
|
||||||
});
|
});
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.get('/api/v1/connections/invalid-connection-id/connections')
|
.get('/api/v1/apps/invalid-connection-id/connections')
|
||||||
.set('Authorization', token)
|
.set('Authorization', token)
|
||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
@@ -193,7 +193,7 @@ describe('POST /api/v1/steps/:stepId/dynamic-data', () => {
|
|||||||
const notExistingStepUUID = Crypto.randomUUID();
|
const notExistingStepUUID = Crypto.randomUUID();
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.get(`/api/v1/steps/${notExistingStepUUID}/dynamic-data`)
|
.post(`/api/v1/steps/${notExistingStepUUID}/dynamic-data`)
|
||||||
.set('Authorization', token)
|
.set('Authorization', token)
|
||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
@@ -216,7 +216,7 @@ describe('POST /api/v1/steps/:stepId/dynamic-data', () => {
|
|||||||
const step = await createStep({ appKey: null });
|
const step = await createStep({ appKey: null });
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.get(`/api/v1/steps/${step.id}/dynamic-data`)
|
.post(`/api/v1/steps/${step.id}/dynamic-data`)
|
||||||
.set('Authorization', token)
|
.set('Authorization', token)
|
||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
@@ -118,7 +118,7 @@ describe('POST /api/v1/steps/:stepId/dynamic-fields', () => {
|
|||||||
const notExistingStepUUID = Crypto.randomUUID();
|
const notExistingStepUUID = Crypto.randomUUID();
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.get(`/api/v1/steps/${notExistingStepUUID}/dynamic-fields`)
|
.post(`/api/v1/steps/${notExistingStepUUID}/dynamic-fields`)
|
||||||
.set('Authorization', token)
|
.set('Authorization', token)
|
||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
@@ -138,10 +138,11 @@ describe('POST /api/v1/steps/:stepId/dynamic-fields', () => {
|
|||||||
conditions: [],
|
conditions: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const step = await createStep({ appKey: null });
|
const step = await createStep();
|
||||||
|
await step.$query().patch({ appKey: null });
|
||||||
|
|
||||||
await request(app)
|
await request(app)
|
||||||
.get(`/api/v1/steps/${step.id}/dynamic-fields`)
|
.post(`/api/v1/steps/${step.id}/dynamic-fields`)
|
||||||
.set('Authorization', token)
|
.set('Authorization', token)
|
||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
@@ -0,0 +1,52 @@
|
|||||||
|
export async function up(knex) {
|
||||||
|
await knex.schema.createTable('role_mappings', (table) => {
|
||||||
|
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
||||||
|
table
|
||||||
|
.uuid('saml_auth_provider_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('saml_auth_providers');
|
||||||
|
table.uuid('role_id').references('id').inTable('roles');
|
||||||
|
table.string('remote_role_name').notNullable();
|
||||||
|
|
||||||
|
table.unique(['saml_auth_provider_id', 'remote_role_name']);
|
||||||
|
|
||||||
|
table.timestamps(true, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingRoleMappings = await knex('saml_auth_providers_role_mappings');
|
||||||
|
|
||||||
|
if (existingRoleMappings.length) {
|
||||||
|
await knex('role_mappings').insert(existingRoleMappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await knex.schema.dropTable('saml_auth_providers_role_mappings');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex) {
|
||||||
|
await knex.schema.createTable(
|
||||||
|
'saml_auth_providers_role_mappings',
|
||||||
|
(table) => {
|
||||||
|
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
||||||
|
table
|
||||||
|
.uuid('saml_auth_provider_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('saml_auth_providers');
|
||||||
|
table.uuid('role_id').references('id').inTable('roles');
|
||||||
|
table.string('remote_role_name').notNullable();
|
||||||
|
|
||||||
|
table.unique(['saml_auth_provider_id', 'remote_role_name']);
|
||||||
|
|
||||||
|
table.timestamps(true, true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const existingRoleMappings = await knex('role_mappings');
|
||||||
|
|
||||||
|
if (existingRoleMappings.length) {
|
||||||
|
await knex('saml_auth_providers_role_mappings').insert(
|
||||||
|
existingRoleMappings
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await knex.schema.dropTable('role_mappings');
|
||||||
|
}
|
@@ -30,7 +30,7 @@ const findOrCreateUserBySamlIdentity = async (
|
|||||||
: [mappedUser.role];
|
: [mappedUser.role];
|
||||||
|
|
||||||
const samlAuthProviderRoleMapping = await samlAuthProvider
|
const samlAuthProviderRoleMapping = await samlAuthProvider
|
||||||
.$relatedQuery('samlAuthProvidersRoleMappings')
|
.$relatedQuery('roleMappings')
|
||||||
.whereIn('remote_role_name', mappedRoles)
|
.whereIn('remote_role_name', mappedRoles)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.first();
|
.first();
|
||||||
|
46
packages/backend/src/helpers/user-ability.test.js
Normal file
46
packages/backend/src/helpers/user-ability.test.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import userAbility from './user-ability.js';
|
||||||
|
|
||||||
|
describe('userAbility', () => {
|
||||||
|
it('should return PureAbility instantiated with user permissions', () => {
|
||||||
|
const user = {
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: 'Flow',
|
||||||
|
action: 'read',
|
||||||
|
conditions: ['isCreator'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
role: {
|
||||||
|
name: 'User',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ability = userAbility(user);
|
||||||
|
|
||||||
|
expect(ability.rules).toStrictEqual(user.permissions);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return permission-less PureAbility for user with no role', () => {
|
||||||
|
const user = {
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
subject: 'Flow',
|
||||||
|
action: 'read',
|
||||||
|
conditions: ['isCreator'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
role: null,
|
||||||
|
};
|
||||||
|
const ability = userAbility(user);
|
||||||
|
|
||||||
|
expect(ability.rules).toStrictEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return permission-less PureAbility for user with no permissions', () => {
|
||||||
|
const user = { permissions: null, role: { name: 'User' } };
|
||||||
|
const ability = userAbility(user);
|
||||||
|
|
||||||
|
expect(ability.rules).toStrictEqual([]);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,30 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`RoleMapping model > jsonSchema should have the correct schema 1`] = `
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"format": "uuid",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"remoteRoleName": {
|
||||||
|
"minLength": 1,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"roleId": {
|
||||||
|
"format": "uuid",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"samlAuthProviderId": {
|
||||||
|
"format": "uuid",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"samlAuthProviderId",
|
||||||
|
"roleId",
|
||||||
|
"remoteRoleName",
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
}
|
||||||
|
`;
|
@@ -1,6 +1,6 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`SamlAuthProvidersRoleMapping model > jsonSchema should have the correct schema 1`] = `
|
exports[`RoleMapping model > jsonSchema should have the correct schema 1`] = `
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import Base from './base.js';
|
import Base from './base.js';
|
||||||
import SamlAuthProvider from './saml-auth-provider.ee.js';
|
import SamlAuthProvider from './saml-auth-provider.ee.js';
|
||||||
|
|
||||||
class SamlAuthProvidersRoleMapping extends Base {
|
class RoleMapping extends Base {
|
||||||
static tableName = 'saml_auth_providers_role_mappings';
|
static tableName = 'role_mappings';
|
||||||
|
|
||||||
static jsonSchema = {
|
static jsonSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -21,11 +21,11 @@ class SamlAuthProvidersRoleMapping extends Base {
|
|||||||
relation: Base.BelongsToOneRelation,
|
relation: Base.BelongsToOneRelation,
|
||||||
modelClass: SamlAuthProvider,
|
modelClass: SamlAuthProvider,
|
||||||
join: {
|
join: {
|
||||||
from: 'saml_auth_providers_role_mappings.saml_auth_provider_id',
|
from: 'role_mappings.saml_auth_provider_id',
|
||||||
to: 'saml_auth_providers.id',
|
to: 'saml_auth_providers.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SamlAuthProvidersRoleMapping;
|
export default RoleMapping;
|
@@ -1,28 +1,26 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import SamlAuthProvidersRoleMapping from '../models/saml-auth-providers-role-mapping.ee';
|
import RoleMapping from './role-mapping.ee';
|
||||||
import SamlAuthProvider from './saml-auth-provider.ee';
|
import SamlAuthProvider from './saml-auth-provider.ee';
|
||||||
import Base from './base';
|
import Base from './base';
|
||||||
|
|
||||||
describe('SamlAuthProvidersRoleMapping model', () => {
|
describe('RoleMapping model', () => {
|
||||||
it('tableName should return correct name', () => {
|
it('tableName should return correct name', () => {
|
||||||
expect(SamlAuthProvidersRoleMapping.tableName).toBe(
|
expect(RoleMapping.tableName).toBe('role_mappings');
|
||||||
'saml_auth_providers_role_mappings'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('jsonSchema should have the correct schema', () => {
|
it('jsonSchema should have the correct schema', () => {
|
||||||
expect(SamlAuthProvidersRoleMapping.jsonSchema).toMatchSnapshot();
|
expect(RoleMapping.jsonSchema).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('relationMappings should return correct associations', () => {
|
it('relationMappings should return correct associations', () => {
|
||||||
const relationMappings = SamlAuthProvidersRoleMapping.relationMappings();
|
const relationMappings = RoleMapping.relationMappings();
|
||||||
|
|
||||||
const expectedRelations = {
|
const expectedRelations = {
|
||||||
samlAuthProvider: {
|
samlAuthProvider: {
|
||||||
relation: Base.BelongsToOneRelation,
|
relation: Base.BelongsToOneRelation,
|
||||||
modelClass: SamlAuthProvider,
|
modelClass: SamlAuthProvider,
|
||||||
join: {
|
join: {
|
||||||
from: 'saml_auth_providers_role_mappings.saml_auth_provider_id',
|
from: 'role_mappings.saml_auth_provider_id',
|
||||||
to: 'saml_auth_providers.id',
|
to: 'saml_auth_providers.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
@@ -5,7 +5,7 @@ import appConfig from '../config/app.js';
|
|||||||
import axios from '../helpers/axios-with-proxy.js';
|
import axios from '../helpers/axios-with-proxy.js';
|
||||||
import Base from './base.js';
|
import Base from './base.js';
|
||||||
import Identity from './identity.ee.js';
|
import Identity from './identity.ee.js';
|
||||||
import SamlAuthProvidersRoleMapping from './saml-auth-providers-role-mapping.ee.js';
|
import RoleMapping from './role-mapping.ee.js';
|
||||||
|
|
||||||
class SamlAuthProvider extends Base {
|
class SamlAuthProvider extends Base {
|
||||||
static tableName = 'saml_auth_providers';
|
static tableName = 'saml_auth_providers';
|
||||||
@@ -53,12 +53,12 @@ class SamlAuthProvider extends Base {
|
|||||||
to: 'saml_auth_providers.id',
|
to: 'saml_auth_providers.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
samlAuthProvidersRoleMappings: {
|
roleMappings: {
|
||||||
relation: Base.HasManyRelation,
|
relation: Base.HasManyRelation,
|
||||||
modelClass: SamlAuthProvidersRoleMapping,
|
modelClass: RoleMapping,
|
||||||
join: {
|
join: {
|
||||||
from: 'saml_auth_providers.id',
|
from: 'saml_auth_providers.id',
|
||||||
to: 'saml_auth_providers_role_mappings.saml_auth_provider_id',
|
to: 'role_mappings.saml_auth_provider_id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -133,27 +133,22 @@ class SamlAuthProvider extends Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateRoleMappings(roleMappings) {
|
async updateRoleMappings(roleMappings) {
|
||||||
return await SamlAuthProvider.transaction(async (trx) => {
|
await this.$relatedQuery('roleMappings').delete();
|
||||||
await this.$relatedQuery('samlAuthProvidersRoleMappings', trx).delete();
|
|
||||||
|
|
||||||
if (isEmpty(roleMappings)) {
|
if (isEmpty(roleMappings)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const samlAuthProvidersRoleMappingsData = roleMappings.map(
|
const roleMappingsData = roleMappings.map((roleMapping) => ({
|
||||||
(samlAuthProvidersRoleMapping) => ({
|
...roleMapping,
|
||||||
...samlAuthProvidersRoleMapping,
|
samlAuthProviderId: this.id,
|
||||||
samlAuthProviderId: this.id,
|
}));
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const samlAuthProvidersRoleMappings =
|
const newRoleMappings = await RoleMapping.query().insertAndFetch(
|
||||||
await SamlAuthProvidersRoleMapping.query(trx).insertAndFetch(
|
roleMappingsData
|
||||||
samlAuthProvidersRoleMappingsData
|
);
|
||||||
);
|
|
||||||
|
|
||||||
return samlAuthProvidersRoleMappings;
|
return newRoleMappings;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,14 @@
|
|||||||
import { vi, describe, it, expect } from 'vitest';
|
import { vi, beforeEach, describe, it, expect } from 'vitest';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import SamlAuthProvider from '../models/saml-auth-provider.ee';
|
import SamlAuthProvider from '../models/saml-auth-provider.ee';
|
||||||
import SamlAuthProvidersRoleMapping from '../models/saml-auth-providers-role-mapping.ee';
|
import RoleMapping from '../models/role-mapping.ee';
|
||||||
|
import axios from '../helpers/axios-with-proxy.js';
|
||||||
import Identity from './identity.ee';
|
import Identity from './identity.ee';
|
||||||
import Base from './base';
|
import Base from './base';
|
||||||
import appConfig from '../config/app';
|
import appConfig from '../config/app';
|
||||||
|
import { createSamlAuthProvider } from '../../test/factories/saml-auth-provider.ee.js';
|
||||||
|
import { createRoleMapping } from '../../test/factories/role-mapping.js';
|
||||||
|
import { createRole } from '../../test/factories/role.js';
|
||||||
|
|
||||||
describe('SamlAuthProvider model', () => {
|
describe('SamlAuthProvider model', () => {
|
||||||
it('tableName should return correct name', () => {
|
it('tableName should return correct name', () => {
|
||||||
@@ -26,12 +31,12 @@ describe('SamlAuthProvider model', () => {
|
|||||||
to: 'saml_auth_providers.id',
|
to: 'saml_auth_providers.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
samlAuthProvidersRoleMappings: {
|
roleMappings: {
|
||||||
relation: Base.HasManyRelation,
|
relation: Base.HasManyRelation,
|
||||||
modelClass: SamlAuthProvidersRoleMapping,
|
modelClass: RoleMapping,
|
||||||
join: {
|
join: {
|
||||||
from: 'saml_auth_providers.id',
|
from: 'saml_auth_providers.id',
|
||||||
to: 'saml_auth_providers_role_mappings.saml_auth_provider_id',
|
to: 'role_mappings.saml_auth_provider_id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -81,4 +86,146 @@ describe('SamlAuthProvider model', () => {
|
|||||||
'https://example.com/saml/logout'
|
'https://example.com/saml/logout'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('config should return the correct configuration object', () => {
|
||||||
|
const samlAuthProvider = new SamlAuthProvider();
|
||||||
|
|
||||||
|
samlAuthProvider.certificate = 'sample-certificate';
|
||||||
|
samlAuthProvider.signatureAlgorithm = 'sha256';
|
||||||
|
samlAuthProvider.entryPoint = 'https://example.com/saml';
|
||||||
|
samlAuthProvider.issuer = 'sample-issuer';
|
||||||
|
|
||||||
|
vi.spyOn(appConfig, 'baseUrl', 'get').mockReturnValue(
|
||||||
|
'https://automatisch.io'
|
||||||
|
);
|
||||||
|
|
||||||
|
const expectedConfig = {
|
||||||
|
callbackUrl: 'https://automatisch.io/login/saml/sample-issuer/callback',
|
||||||
|
cert: 'sample-certificate',
|
||||||
|
entryPoint: 'https://example.com/saml',
|
||||||
|
issuer: 'sample-issuer',
|
||||||
|
signatureAlgorithm: 'sha256',
|
||||||
|
logoutUrl: 'https://example.com/saml',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(samlAuthProvider.config).toStrictEqual(expectedConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generateLogoutRequestBody should return a correctly encoded SAML logout request', () => {
|
||||||
|
vi.mock('uuid', () => ({
|
||||||
|
v4: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const samlAuthProvider = new SamlAuthProvider();
|
||||||
|
|
||||||
|
samlAuthProvider.entryPoint = 'https://example.com/saml';
|
||||||
|
samlAuthProvider.issuer = 'sample-issuer';
|
||||||
|
|
||||||
|
const mockUuid = '123e4567-e89b-12d3-a456-426614174000';
|
||||||
|
uuidv4.mockReturnValue(mockUuid);
|
||||||
|
|
||||||
|
const sessionId = 'test-session-id';
|
||||||
|
|
||||||
|
const logoutRequest = samlAuthProvider.generateLogoutRequestBody(sessionId);
|
||||||
|
|
||||||
|
const expectedLogoutRequest = `
|
||||||
|
<samlp:LogoutRequest
|
||||||
|
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
|
||||||
|
ID="${mockUuid}"
|
||||||
|
Version="2.0"
|
||||||
|
IssueInstant="${new Date().toISOString()}"
|
||||||
|
Destination="https://example.com/saml">
|
||||||
|
|
||||||
|
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">sample-issuer</saml:Issuer>
|
||||||
|
<samlp:SessionIndex>test-session-id</samlp:SessionIndex>
|
||||||
|
</samlp:LogoutRequest>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const expectedEncodedRequest = Buffer.from(expectedLogoutRequest).toString(
|
||||||
|
'base64'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(logoutRequest).toBe(expectedEncodedRequest);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('terminateRemoteSession should send the correct POST request and return the response', async () => {
|
||||||
|
vi.mock('../helpers/axios-with-proxy.js', () => ({
|
||||||
|
default: {
|
||||||
|
post: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const samlAuthProvider = new SamlAuthProvider();
|
||||||
|
|
||||||
|
samlAuthProvider.entryPoint = 'https://example.com/saml';
|
||||||
|
samlAuthProvider.generateLogoutRequestBody = vi
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue('mockEncodedLogoutRequest');
|
||||||
|
|
||||||
|
const sessionId = 'test-session-id';
|
||||||
|
|
||||||
|
const mockResponse = { data: 'Logout Successful' };
|
||||||
|
axios.post.mockResolvedValue(mockResponse);
|
||||||
|
|
||||||
|
const response = await samlAuthProvider.terminateRemoteSession(sessionId);
|
||||||
|
|
||||||
|
expect(samlAuthProvider.generateLogoutRequestBody).toHaveBeenCalledWith(
|
||||||
|
sessionId
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(axios.post).toHaveBeenCalledWith(
|
||||||
|
'https://example.com/saml',
|
||||||
|
'SAMLRequest=mockEncodedLogoutRequest',
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response).toBe(mockResponse);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateRoleMappings', () => {
|
||||||
|
let samlAuthProvider;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
samlAuthProvider = await createSamlAuthProvider();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove all existing role mappings', async () => {
|
||||||
|
await createRoleMapping({
|
||||||
|
samlAuthProviderId: samlAuthProvider.id,
|
||||||
|
remoteRoleName: 'Admin',
|
||||||
|
});
|
||||||
|
|
||||||
|
await createRoleMapping({
|
||||||
|
samlAuthProviderId: samlAuthProvider.id,
|
||||||
|
remoteRoleName: 'User',
|
||||||
|
});
|
||||||
|
|
||||||
|
await samlAuthProvider.updateRoleMappings([]);
|
||||||
|
|
||||||
|
const roleMappings = await samlAuthProvider.$relatedQuery('roleMappings');
|
||||||
|
expect(roleMappings).toStrictEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the updated role mappings when new ones are provided', async () => {
|
||||||
|
const adminRole = await createRole({ name: 'Admin' });
|
||||||
|
const userRole = await createRole({ name: 'User' });
|
||||||
|
|
||||||
|
const newRoleMappings = [
|
||||||
|
{ remoteRoleName: 'Admin', roleId: adminRole.id },
|
||||||
|
{ remoteRoleName: 'User', roleId: userRole.id },
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = await samlAuthProvider.updateRoleMappings(newRoleMappings);
|
||||||
|
|
||||||
|
const refetchedRoleMappings = await samlAuthProvider.$relatedQuery(
|
||||||
|
'roleMappings'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toStrictEqual(refetchedRoleMappings);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -212,6 +212,10 @@ class User extends Base {
|
|||||||
return `${appConfig.webAppUrl}/accept-invitation?token=${this.invitationToken}`;
|
return `${appConfig.webAppUrl}/accept-invitation?token=${this.invitationToken}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get ability() {
|
||||||
|
return userAbility(this);
|
||||||
|
}
|
||||||
|
|
||||||
static async authenticate(email, password) {
|
static async authenticate(email, password) {
|
||||||
const user = await User.query().findOne({
|
const user = await User.query().findOne({
|
||||||
email: email?.toLowerCase() || null,
|
email: email?.toLowerCase() || null,
|
||||||
@@ -583,62 +587,6 @@ class User extends Base {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
async $beforeInsert(queryContext) {
|
|
||||||
await super.$beforeInsert(queryContext);
|
|
||||||
|
|
||||||
this.email = this.email.toLowerCase();
|
|
||||||
await this.generateHash();
|
|
||||||
|
|
||||||
if (appConfig.isCloud) {
|
|
||||||
this.startTrialPeriod();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async $beforeUpdate(opt, queryContext) {
|
|
||||||
await super.$beforeUpdate(opt, queryContext);
|
|
||||||
|
|
||||||
if (this.email) {
|
|
||||||
this.email = this.email.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.generateHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
async $afterInsert(queryContext) {
|
|
||||||
await super.$afterInsert(queryContext);
|
|
||||||
|
|
||||||
if (appConfig.isCloud) {
|
|
||||||
await this.$relatedQuery('usageData').insert({
|
|
||||||
userId: this.id,
|
|
||||||
consumedTaskCount: 0,
|
|
||||||
nextResetAt: DateTime.now().plus({ days: 30 }).toISODate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async $afterFind() {
|
|
||||||
if (await hasValidLicense()) return this;
|
|
||||||
|
|
||||||
if (Array.isArray(this.permissions)) {
|
|
||||||
this.permissions = this.permissions.filter((permission) => {
|
|
||||||
const restrictedSubjects = [
|
|
||||||
'App',
|
|
||||||
'Role',
|
|
||||||
'SamlAuthProvider',
|
|
||||||
'Config',
|
|
||||||
];
|
|
||||||
|
|
||||||
return !restrictedSubjects.includes(permission.subject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
get ability() {
|
|
||||||
return userAbility(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
can(action, subject) {
|
can(action, subject) {
|
||||||
const can = this.ability.can(action, subject);
|
const can = this.ability.can(action, subject);
|
||||||
|
|
||||||
@@ -654,12 +602,68 @@ class User extends Base {
|
|||||||
return conditionMap;
|
return conditionMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
cannot(action, subject) {
|
lowercaseEmail() {
|
||||||
const cannot = this.ability.cannot(action, subject);
|
if (this.email) {
|
||||||
|
this.email = this.email.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cannot) throw new NotAuthorizedError();
|
async createUsageData() {
|
||||||
|
if (appConfig.isCloud) {
|
||||||
|
return await this.$relatedQuery('usageData').insertAndFetch({
|
||||||
|
userId: this.id,
|
||||||
|
consumedTaskCount: 0,
|
||||||
|
nextResetAt: DateTime.now().plus({ days: 30 }).toISODate(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return cannot;
|
async omitEnterprisePermissionsWithoutValidLicense() {
|
||||||
|
if (await hasValidLicense()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(this.permissions)) {
|
||||||
|
this.permissions = this.permissions.filter((permission) => {
|
||||||
|
const restrictedSubjects = [
|
||||||
|
'App',
|
||||||
|
'Role',
|
||||||
|
'SamlAuthProvider',
|
||||||
|
'Config',
|
||||||
|
];
|
||||||
|
|
||||||
|
return !restrictedSubjects.includes(permission.subject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async $beforeInsert(queryContext) {
|
||||||
|
await super.$beforeInsert(queryContext);
|
||||||
|
|
||||||
|
this.lowercaseEmail();
|
||||||
|
await this.generateHash();
|
||||||
|
|
||||||
|
if (appConfig.isCloud) {
|
||||||
|
this.startTrialPeriod();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async $beforeUpdate(opt, queryContext) {
|
||||||
|
await super.$beforeUpdate(opt, queryContext);
|
||||||
|
|
||||||
|
this.lowercaseEmail();
|
||||||
|
|
||||||
|
await this.generateHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
async $afterInsert(queryContext) {
|
||||||
|
await super.$afterInsert(queryContext);
|
||||||
|
|
||||||
|
await this.createUsageData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async $afterFind() {
|
||||||
|
await this.omitEnterprisePermissionsWithoutValidLicense();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { describe, it, expect, vi } from 'vitest';
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
import { DateTime, Duration } from 'luxon';
|
import { DateTime, Duration } from 'luxon';
|
||||||
import appConfig from '../config/app.js';
|
import appConfig from '../config/app.js';
|
||||||
|
import * as licenseModule from '../helpers/license.ee.js';
|
||||||
import Base from './base.js';
|
import Base from './base.js';
|
||||||
import AccessToken from './access-token.js';
|
import AccessToken from './access-token.js';
|
||||||
import Config from './config.js';
|
import Config from './config.js';
|
||||||
@@ -20,6 +21,7 @@ import {
|
|||||||
REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||||
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||||
} from '../helpers/remove-job-configuration.js';
|
} from '../helpers/remove-job-configuration.js';
|
||||||
|
import * as userAbilityModule from '../helpers/user-ability.js';
|
||||||
import { createUser } from '../../test/factories/user.js';
|
import { createUser } from '../../test/factories/user.js';
|
||||||
import { createConnection } from '../../test/factories/connection.js';
|
import { createConnection } from '../../test/factories/connection.js';
|
||||||
import { createRole } from '../../test/factories/role.js';
|
import { createRole } from '../../test/factories/role.js';
|
||||||
@@ -205,64 +207,6 @@ describe('User model', () => {
|
|||||||
expect(virtualAttributes).toStrictEqual(expectedAttributes);
|
expect(virtualAttributes).toStrictEqual(expectedAttributes);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('acceptInvitationUrl should return accept invitation page URL with invitation token', async () => {
|
|
||||||
const user = new User();
|
|
||||||
user.invitationToken = 'invitation-token';
|
|
||||||
|
|
||||||
vi.spyOn(appConfig, 'webAppUrl', 'get').mockReturnValue(
|
|
||||||
'https://automatisch.io'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(user.acceptInvitationUrl).toBe(
|
|
||||||
'https://automatisch.io/accept-invitation?token=invitation-token'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('authenticate', () => {
|
|
||||||
it('should create and return the token for correct email and password', async () => {
|
|
||||||
const user = await createUser({
|
|
||||||
email: 'test-user@automatisch.io',
|
|
||||||
password: 'sample-password',
|
|
||||||
});
|
|
||||||
|
|
||||||
const token = await User.authenticate(
|
|
||||||
'test-user@automatisch.io',
|
|
||||||
'sample-password'
|
|
||||||
);
|
|
||||||
|
|
||||||
const persistedToken = await AccessToken.query().findOne({
|
|
||||||
userId: user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(token).toBe(persistedToken.token);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return undefined for existing email and incorrect password', async () => {
|
|
||||||
await createUser({
|
|
||||||
email: 'test-user@automatisch.io',
|
|
||||||
password: 'sample-password',
|
|
||||||
});
|
|
||||||
|
|
||||||
const token = await User.authenticate(
|
|
||||||
'test-user@automatisch.io',
|
|
||||||
'wrong-password'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(token).toBe(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return undefined for non-existing email', async () => {
|
|
||||||
await createUser({
|
|
||||||
email: 'test-user@automatisch.io',
|
|
||||||
password: 'sample-password',
|
|
||||||
});
|
|
||||||
|
|
||||||
const token = await User.authenticate('non-existing-user@automatisch.io');
|
|
||||||
|
|
||||||
expect(token).toBe(undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('authorizedFlows', () => {
|
describe('authorizedFlows', () => {
|
||||||
it('should return user flows with isCreator condition', async () => {
|
it('should return user flows with isCreator condition', async () => {
|
||||||
const userRole = await createRole({ name: 'User' });
|
const userRole = await createRole({ name: 'User' });
|
||||||
@@ -505,6 +449,76 @@ describe('User model', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('acceptInvitationUrl should return accept invitation page URL with invitation token', async () => {
|
||||||
|
const user = new User();
|
||||||
|
user.invitationToken = 'invitation-token';
|
||||||
|
|
||||||
|
vi.spyOn(appConfig, 'webAppUrl', 'get').mockReturnValue(
|
||||||
|
'https://automatisch.io'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(user.acceptInvitationUrl).toBe(
|
||||||
|
'https://automatisch.io/accept-invitation?token=invitation-token'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ability should return userAbility for the user', () => {
|
||||||
|
const user = new User();
|
||||||
|
user.fullName = 'Sample user';
|
||||||
|
|
||||||
|
const userAbilitySpy = vi
|
||||||
|
.spyOn(userAbilityModule, 'default')
|
||||||
|
.mockReturnValue('user-ability');
|
||||||
|
|
||||||
|
expect(user.ability).toStrictEqual('user-ability');
|
||||||
|
expect(userAbilitySpy).toHaveBeenNthCalledWith(1, user);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('authenticate', () => {
|
||||||
|
it('should create and return the token for correct email and password', async () => {
|
||||||
|
const user = await createUser({
|
||||||
|
email: 'test-user@automatisch.io',
|
||||||
|
password: 'sample-password',
|
||||||
|
});
|
||||||
|
|
||||||
|
const token = await User.authenticate(
|
||||||
|
'test-user@automatisch.io',
|
||||||
|
'sample-password'
|
||||||
|
);
|
||||||
|
|
||||||
|
const persistedToken = await AccessToken.query().findOne({
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(token).toBe(persistedToken.token);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined for existing email and incorrect password', async () => {
|
||||||
|
await createUser({
|
||||||
|
email: 'test-user@automatisch.io',
|
||||||
|
password: 'sample-password',
|
||||||
|
});
|
||||||
|
|
||||||
|
const token = await User.authenticate(
|
||||||
|
'test-user@automatisch.io',
|
||||||
|
'wrong-password'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(token).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined for non-existing email', async () => {
|
||||||
|
await createUser({
|
||||||
|
email: 'test-user@automatisch.io',
|
||||||
|
password: 'sample-password',
|
||||||
|
});
|
||||||
|
|
||||||
|
const token = await User.authenticate('non-existing-user@automatisch.io');
|
||||||
|
|
||||||
|
expect(token).toBe(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('login', () => {
|
describe('login', () => {
|
||||||
it('should return true when the given password matches with the user password', async () => {
|
it('should return true when the given password matches with the user password', async () => {
|
||||||
const user = await createUser({ password: 'sample-password' });
|
const user = await createUser({ password: 'sample-password' });
|
||||||
@@ -982,21 +996,9 @@ describe('User model', () => {
|
|||||||
|
|
||||||
const user = await createUser();
|
const user = await createUser();
|
||||||
|
|
||||||
const presentDate = DateTime.fromObject(
|
|
||||||
{ year: 2024, month: 11, day: 17, hour: 11, minute: 30 },
|
|
||||||
{ zone: 'UTC+0' }
|
|
||||||
);
|
|
||||||
|
|
||||||
vi.setSystemTime(presentDate);
|
|
||||||
|
|
||||||
await user.startTrialPeriod();
|
await user.startTrialPeriod();
|
||||||
|
|
||||||
const futureDate = DateTime.fromObject(
|
vi.setSystemTime(DateTime.now().plus({ month: 1 }));
|
||||||
{ year: 2025, month: 1, day: 1 },
|
|
||||||
{ zone: 'UTC+0' }
|
|
||||||
);
|
|
||||||
|
|
||||||
vi.setSystemTime(futureDate);
|
|
||||||
|
|
||||||
const refetchedUser = await user.$query();
|
const refetchedUser = await user.$query();
|
||||||
|
|
||||||
@@ -1104,7 +1106,9 @@ describe('User model', () => {
|
|||||||
|
|
||||||
const user = await createUser();
|
const user = await createUser();
|
||||||
|
|
||||||
expect(() => user.getPlanAndUsage()).rejects.toThrow('NotFoundError');
|
await expect(() => user.getPlanAndUsage()).rejects.toThrow(
|
||||||
|
'NotFoundError'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1175,7 +1179,7 @@ describe('User model', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should throw not found error when user role does not exist', async () => {
|
it('should throw not found error when user role does not exist', async () => {
|
||||||
expect(() =>
|
await expect(() =>
|
||||||
User.registerUser({
|
User.registerUser({
|
||||||
fullName: 'Sample user',
|
fullName: 'Sample user',
|
||||||
email: 'user@automatisch.io',
|
email: 'user@automatisch.io',
|
||||||
@@ -1184,4 +1188,342 @@ describe('User model', () => {
|
|||||||
).rejects.toThrowError('NotFoundError');
|
).rejects.toThrowError('NotFoundError');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('can', () => {
|
||||||
|
it('should return conditions for the given action and subject of the user', async () => {
|
||||||
|
const userRole = await createRole({ name: 'User' });
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'Flow',
|
||||||
|
action: 'read',
|
||||||
|
conditions: ['isCreator'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'Connection',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await createUser({ roleId: userRole.id });
|
||||||
|
|
||||||
|
const userWithRoleAndPermissions = await user
|
||||||
|
.$query()
|
||||||
|
.withGraphFetched({ role: true, permissions: true });
|
||||||
|
|
||||||
|
expect(userWithRoleAndPermissions.can('read', 'Flow')).toStrictEqual({
|
||||||
|
isCreator: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
userWithRoleAndPermissions.can('read', 'Connection')
|
||||||
|
).toStrictEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return not authorized error when the user is not permitted for the given action and subject', async () => {
|
||||||
|
const userRole = await createRole({ name: 'User' });
|
||||||
|
const user = await createUser({ roleId: userRole.id });
|
||||||
|
|
||||||
|
const userWithRoleAndPermissions = await user
|
||||||
|
.$query()
|
||||||
|
.withGraphFetched({ role: true, permissions: true });
|
||||||
|
|
||||||
|
expect(() => userWithRoleAndPermissions.can('read', 'Flow')).toThrowError(
|
||||||
|
'The user is not authorized!'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('lowercaseEmail should lowercase the user email', () => {
|
||||||
|
const user = new User();
|
||||||
|
user.email = 'USER@AUTOMATISCH.IO';
|
||||||
|
|
||||||
|
user.lowercaseEmail();
|
||||||
|
|
||||||
|
expect(user.email).toBe('user@automatisch.io');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createUsageData', () => {
|
||||||
|
it('should create usage data if Automatisch is a cloud installation', async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
|
||||||
|
|
||||||
|
const user = await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.setSystemTime(DateTime.now().plus({ month: 1 }));
|
||||||
|
|
||||||
|
const usageData = await user.createUsageData();
|
||||||
|
const currentUsageData = await user.$relatedQuery('currentUsageData');
|
||||||
|
|
||||||
|
expect(usageData).toStrictEqual(currentUsageData);
|
||||||
|
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not create usage data if Automatisch is not a cloud installation', async () => {
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
|
||||||
|
|
||||||
|
const user = await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
});
|
||||||
|
|
||||||
|
const usageData = await user.createUsageData();
|
||||||
|
|
||||||
|
expect(usageData).toBe(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('omitEnterprisePermissionsWithoutValidLicense', () => {
|
||||||
|
it('should return user as-is with valid license', async () => {
|
||||||
|
const userRole = await createRole({ name: 'User' });
|
||||||
|
const user = await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
roleId: userRole.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const readFlowPermission = await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'Flow',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'App',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'Role',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'Config',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'SamlAuthProvider',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const userWithRoleAndPermissions = await user
|
||||||
|
.$query()
|
||||||
|
.withGraphFetched({ role: true, permissions: true });
|
||||||
|
|
||||||
|
expect(userWithRoleAndPermissions.permissions).toStrictEqual([
|
||||||
|
readFlowPermission,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should omit enterprise permissions without valid license', async () => {
|
||||||
|
vi.spyOn(licenseModule, 'hasValidLicense').mockResolvedValue(false);
|
||||||
|
|
||||||
|
const userRole = await createRole({ name: 'User' });
|
||||||
|
const user = await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
roleId: userRole.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const readFlowPermission = await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'Flow',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'App',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'Role',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'Config',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: userRole.id,
|
||||||
|
subject: 'SamlAuthProvider',
|
||||||
|
action: 'read',
|
||||||
|
conditions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const userWithRoleAndPermissions = await user
|
||||||
|
.$query()
|
||||||
|
.withGraphFetched({ role: true, permissions: true });
|
||||||
|
|
||||||
|
expect(userWithRoleAndPermissions.permissions).toStrictEqual([
|
||||||
|
readFlowPermission,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('$beforeInsert', () => {
|
||||||
|
it('should call super.$beforeInsert', async () => {
|
||||||
|
const superBeforeInsertSpy = vi
|
||||||
|
.spyOn(User.prototype, '$beforeInsert')
|
||||||
|
.mockResolvedValue();
|
||||||
|
|
||||||
|
await createUser();
|
||||||
|
|
||||||
|
expect(superBeforeInsertSpy).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should lowercase the user email', async () => {
|
||||||
|
const user = await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'USER@AUTOMATISCH.IO',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.email).toBe('user@automatisch.io');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate password hash', async () => {
|
||||||
|
const user = await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
password: 'sample-password',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.password).not.toBe('sample-password');
|
||||||
|
expect(await user.login('sample-password')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should start trial period if Automatisch is a cloud installation', async () => {
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
|
||||||
|
|
||||||
|
const startTrialPeriodSpy = vi.spyOn(User.prototype, 'startTrialPeriod');
|
||||||
|
|
||||||
|
await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(startTrialPeriodSpy).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not start trial period if Automatisch is not a cloud installation', async () => {
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
|
||||||
|
|
||||||
|
const startTrialPeriodSpy = vi.spyOn(User.prototype, 'startTrialPeriod');
|
||||||
|
|
||||||
|
await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(startTrialPeriodSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('$beforeUpdate', () => {
|
||||||
|
it('should call super.$beforeUpdate', async () => {
|
||||||
|
const superBeforeUpdateSpy = vi
|
||||||
|
.spyOn(User.prototype, '$beforeUpdate')
|
||||||
|
.mockResolvedValue();
|
||||||
|
|
||||||
|
const user = await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.$query().patch({ fullName: 'Updated user name' });
|
||||||
|
|
||||||
|
expect(superBeforeUpdateSpy).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should lowercase the user email if given', async () => {
|
||||||
|
const user = await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.$query().patchAndFetch({ email: 'NEW_EMAIL@AUTOMATISCH.IO' });
|
||||||
|
|
||||||
|
expect(user.email).toBe('new_email@automatisch.io');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate password hash', async () => {
|
||||||
|
const user = await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
password: 'sample-password',
|
||||||
|
});
|
||||||
|
|
||||||
|
await user.$query().patchAndFetch({ password: 'new-password' });
|
||||||
|
|
||||||
|
expect(user.password).not.toBe('new-password');
|
||||||
|
expect(await user.login('new-password')).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('$afterInsert', () => {
|
||||||
|
it('should call super.$afterInsert', async () => {
|
||||||
|
const superAfterInsertSpy = vi.spyOn(User.prototype, '$afterInsert');
|
||||||
|
|
||||||
|
await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(superAfterInsertSpy).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call createUsageData', async () => {
|
||||||
|
const createUsageDataSpy = vi.spyOn(User.prototype, 'createUsageData');
|
||||||
|
|
||||||
|
await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createUsageDataSpy).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('$afterFind should invoke omitEnterprisePermissionsWithoutValidLicense method', async () => {
|
||||||
|
const omitEnterprisePermissionsWithoutValidLicenseSpy = vi.spyOn(
|
||||||
|
User.prototype,
|
||||||
|
'omitEnterprisePermissionsWithoutValidLicense'
|
||||||
|
);
|
||||||
|
|
||||||
|
await createUser({
|
||||||
|
fullName: 'Sample user',
|
||||||
|
email: 'user@automatisch.io',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
omitEnterprisePermissionsWithoutValidLicenseSpy
|
||||||
|
).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -11,10 +11,6 @@ const redisConnection = {
|
|||||||
|
|
||||||
const actionQueue = new Queue('action', redisConnection);
|
const actionQueue = new Queue('action', redisConnection);
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
|
||||||
await actionQueue.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
actionQueue.on('error', (error) => {
|
actionQueue.on('error', (error) => {
|
||||||
if (error.code === CONNECTION_REFUSED) {
|
if (error.code === CONNECTION_REFUSED) {
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@@ -11,10 +11,6 @@ const redisConnection = {
|
|||||||
|
|
||||||
const deleteUserQueue = new Queue('delete-user', redisConnection);
|
const deleteUserQueue = new Queue('delete-user', redisConnection);
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
|
||||||
await deleteUserQueue.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
deleteUserQueue.on('error', (error) => {
|
deleteUserQueue.on('error', (error) => {
|
||||||
if (error.code === CONNECTION_REFUSED) {
|
if (error.code === CONNECTION_REFUSED) {
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@@ -11,10 +11,6 @@ const redisConnection = {
|
|||||||
|
|
||||||
const emailQueue = new Queue('email', redisConnection);
|
const emailQueue = new Queue('email', redisConnection);
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
|
||||||
await emailQueue.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
emailQueue.on('error', (error) => {
|
emailQueue.on('error', (error) => {
|
||||||
if (error.code === CONNECTION_REFUSED) {
|
if (error.code === CONNECTION_REFUSED) {
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@@ -11,10 +11,6 @@ const redisConnection = {
|
|||||||
|
|
||||||
const flowQueue = new Queue('flow', redisConnection);
|
const flowQueue = new Queue('flow', redisConnection);
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
|
||||||
await flowQueue.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
flowQueue.on('error', (error) => {
|
flowQueue.on('error', (error) => {
|
||||||
if (error.code === CONNECTION_REFUSED) {
|
if (error.code === CONNECTION_REFUSED) {
|
||||||
logger.error(
|
logger.error(
|
||||||
|
21
packages/backend/src/queues/index.js
Normal file
21
packages/backend/src/queues/index.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import appConfig from '../config/app.js';
|
||||||
|
import actionQueue from './action.js';
|
||||||
|
import emailQueue from './email.js';
|
||||||
|
import flowQueue from './flow.js';
|
||||||
|
import triggerQueue from './trigger.js';
|
||||||
|
import deleteUserQueue from './delete-user.ee.js';
|
||||||
|
import removeCancelledSubscriptionsQueue from './remove-cancelled-subscriptions.ee.js';
|
||||||
|
|
||||||
|
const queues = [
|
||||||
|
actionQueue,
|
||||||
|
emailQueue,
|
||||||
|
flowQueue,
|
||||||
|
triggerQueue,
|
||||||
|
deleteUserQueue,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (appConfig.isCloud) {
|
||||||
|
queues.push(removeCancelledSubscriptionsQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default queues;
|
@@ -14,10 +14,6 @@ const removeCancelledSubscriptionsQueue = new Queue(
|
|||||||
redisConnection
|
redisConnection
|
||||||
);
|
);
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
|
||||||
await removeCancelledSubscriptionsQueue.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
removeCancelledSubscriptionsQueue.on('error', (error) => {
|
removeCancelledSubscriptionsQueue.on('error', (error) => {
|
||||||
if (error.code === CONNECTION_REFUSED) {
|
if (error.code === CONNECTION_REFUSED) {
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@@ -11,10 +11,6 @@ const redisConnection = {
|
|||||||
|
|
||||||
const triggerQueue = new Queue('trigger', redisConnection);
|
const triggerQueue = new Queue('trigger', redisConnection);
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
|
||||||
await triggerQueue.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
triggerQueue.on('error', (error) => {
|
triggerQueue.on('error', (error) => {
|
||||||
if (error.code === CONNECTION_REFUSED) {
|
if (error.code === CONNECTION_REFUSED) {
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@@ -26,7 +26,7 @@ const serializers = {
|
|||||||
Permission: permissionSerializer,
|
Permission: permissionSerializer,
|
||||||
AdminSamlAuthProvider: adminSamlAuthProviderSerializer,
|
AdminSamlAuthProvider: adminSamlAuthProviderSerializer,
|
||||||
SamlAuthProvider: samlAuthProviderSerializer,
|
SamlAuthProvider: samlAuthProviderSerializer,
|
||||||
SamlAuthProvidersRoleMapping: samlAuthProviderRoleMappingSerializer,
|
RoleMapping: samlAuthProviderRoleMappingSerializer,
|
||||||
AppAuthClient: appAuthClientSerializer,
|
AppAuthClient: appAuthClientSerializer,
|
||||||
AppConfig: appConfigSerializer,
|
AppConfig: appConfigSerializer,
|
||||||
Flow: flowSerializer,
|
Flow: flowSerializer,
|
||||||
|
@@ -1,20 +1,22 @@
|
|||||||
import * as Sentry from './helpers/sentry.ee.js';
|
import * as Sentry from './helpers/sentry.ee.js';
|
||||||
import appConfig from './config/app.js';
|
import process from 'node:process';
|
||||||
|
|
||||||
Sentry.init();
|
Sentry.init();
|
||||||
|
|
||||||
import './config/orm.js';
|
import './config/orm.js';
|
||||||
import './helpers/check-worker-readiness.js';
|
import './helpers/check-worker-readiness.js';
|
||||||
import './workers/flow.js';
|
import queues from './queues/index.js';
|
||||||
import './workers/trigger.js';
|
import workers from './workers/index.js';
|
||||||
import './workers/action.js';
|
|
||||||
import './workers/email.js';
|
|
||||||
import './workers/delete-user.ee.js';
|
|
||||||
|
|
||||||
if (appConfig.isCloud) {
|
process.on('SIGTERM', async () => {
|
||||||
import('./workers/remove-cancelled-subscriptions.ee.js');
|
for (const queue of queues) {
|
||||||
import('./queues/remove-cancelled-subscriptions.ee.js');
|
await queue.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const worker of workers) {
|
||||||
|
await worker.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
import telemetry from './helpers/telemetry/index.js';
|
import telemetry from './helpers/telemetry/index.js';
|
||||||
|
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { Worker } from 'bullmq';
|
import { Worker } from 'bullmq';
|
||||||
import process from 'node:process';
|
|
||||||
|
|
||||||
import * as Sentry from '../helpers/sentry.ee.js';
|
import * as Sentry from '../helpers/sentry.ee.js';
|
||||||
import redisConfig from '../config/redis.js';
|
import redisConfig from '../config/redis.js';
|
||||||
@@ -15,7 +14,7 @@ import delayAsMilliseconds from '../helpers/delay-as-milliseconds.js';
|
|||||||
|
|
||||||
const DEFAULT_DELAY_DURATION = 0;
|
const DEFAULT_DELAY_DURATION = 0;
|
||||||
|
|
||||||
export const worker = new Worker(
|
const actionWorker = new Worker(
|
||||||
'action',
|
'action',
|
||||||
async (job) => {
|
async (job) => {
|
||||||
const { stepId, flowId, executionId, computedParameters, executionStep } =
|
const { stepId, flowId, executionId, computedParameters, executionStep } =
|
||||||
@@ -55,11 +54,11 @@ export const worker = new Worker(
|
|||||||
{ connection: redisConfig }
|
{ connection: redisConfig }
|
||||||
);
|
);
|
||||||
|
|
||||||
worker.on('completed', (job) => {
|
actionWorker.on('completed', (job) => {
|
||||||
logger.info(`JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has started!`);
|
logger.info(`JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has started!`);
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('failed', (job, err) => {
|
actionWorker.on('failed', (job, err) => {
|
||||||
const errorMessage = `
|
const errorMessage = `
|
||||||
JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has failed to start with ${err.message}
|
JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has failed to start with ${err.message}
|
||||||
\n ${err.stack}
|
\n ${err.stack}
|
||||||
@@ -74,6 +73,4 @@ worker.on('failed', (job, err) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
export default actionWorker;
|
||||||
await worker.close();
|
|
||||||
});
|
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { Worker } from 'bullmq';
|
import { Worker } from 'bullmq';
|
||||||
import process from 'node:process';
|
|
||||||
|
|
||||||
import * as Sentry from '../helpers/sentry.ee.js';
|
import * as Sentry from '../helpers/sentry.ee.js';
|
||||||
import redisConfig from '../config/redis.js';
|
import redisConfig from '../config/redis.js';
|
||||||
@@ -8,7 +7,7 @@ import appConfig from '../config/app.js';
|
|||||||
import User from '../models/user.js';
|
import User from '../models/user.js';
|
||||||
import ExecutionStep from '../models/execution-step.js';
|
import ExecutionStep from '../models/execution-step.js';
|
||||||
|
|
||||||
export const worker = new Worker(
|
const deleteUserWorker = new Worker(
|
||||||
'delete-user',
|
'delete-user',
|
||||||
async (job) => {
|
async (job) => {
|
||||||
const { id } = job.data;
|
const { id } = job.data;
|
||||||
@@ -46,13 +45,13 @@ export const worker = new Worker(
|
|||||||
{ connection: redisConfig }
|
{ connection: redisConfig }
|
||||||
);
|
);
|
||||||
|
|
||||||
worker.on('completed', (job) => {
|
deleteUserWorker.on('completed', (job) => {
|
||||||
logger.info(
|
logger.info(
|
||||||
`JOB ID: ${job.id} - The user with the ID of '${job.data.id}' has been deleted!`
|
`JOB ID: ${job.id} - The user with the ID of '${job.data.id}' has been deleted!`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('failed', (job, err) => {
|
deleteUserWorker.on('failed', (job, err) => {
|
||||||
const errorMessage = `
|
const errorMessage = `
|
||||||
JOB ID: ${job.id} - The user with the ID of '${job.data.id}' has failed to be deleted! ${err.message}
|
JOB ID: ${job.id} - The user with the ID of '${job.data.id}' has failed to be deleted! ${err.message}
|
||||||
\n ${err.stack}
|
\n ${err.stack}
|
||||||
@@ -67,6 +66,4 @@ worker.on('failed', (job, err) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
export default deleteUserWorker;
|
||||||
await worker.close();
|
|
||||||
});
|
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { Worker } from 'bullmq';
|
import { Worker } from 'bullmq';
|
||||||
import process from 'node:process';
|
|
||||||
|
|
||||||
import * as Sentry from '../helpers/sentry.ee.js';
|
import * as Sentry from '../helpers/sentry.ee.js';
|
||||||
import redisConfig from '../config/redis.js';
|
import redisConfig from '../config/redis.js';
|
||||||
@@ -16,7 +15,7 @@ const isAutomatischEmail = (email) => {
|
|||||||
return email.endsWith('@automatisch.io');
|
return email.endsWith('@automatisch.io');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const worker = new Worker(
|
const emailWorker = new Worker(
|
||||||
'email',
|
'email',
|
||||||
async (job) => {
|
async (job) => {
|
||||||
const { email, subject, template, params } = job.data;
|
const { email, subject, template, params } = job.data;
|
||||||
@@ -39,13 +38,13 @@ export const worker = new Worker(
|
|||||||
{ connection: redisConfig }
|
{ connection: redisConfig }
|
||||||
);
|
);
|
||||||
|
|
||||||
worker.on('completed', (job) => {
|
emailWorker.on('completed', (job) => {
|
||||||
logger.info(
|
logger.info(
|
||||||
`JOB ID: ${job.id} - ${job.data.subject} email sent to ${job.data.email}!`
|
`JOB ID: ${job.id} - ${job.data.subject} email sent to ${job.data.email}!`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('failed', (job, err) => {
|
emailWorker.on('failed', (job, err) => {
|
||||||
const errorMessage = `
|
const errorMessage = `
|
||||||
JOB ID: ${job.id} - ${job.data.subject} email to ${job.data.email} has failed to send with ${err.message}
|
JOB ID: ${job.id} - ${job.data.subject} email to ${job.data.email} has failed to send with ${err.message}
|
||||||
\n ${err.stack}
|
\n ${err.stack}
|
||||||
@@ -60,6 +59,4 @@ worker.on('failed', (job, err) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
export default emailWorker;
|
||||||
await worker.close();
|
|
||||||
});
|
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { Worker } from 'bullmq';
|
import { Worker } from 'bullmq';
|
||||||
import process from 'node:process';
|
|
||||||
|
|
||||||
import * as Sentry from '../helpers/sentry.ee.js';
|
import * as Sentry from '../helpers/sentry.ee.js';
|
||||||
import redisConfig from '../config/redis.js';
|
import redisConfig from '../config/redis.js';
|
||||||
@@ -13,7 +12,7 @@ import {
|
|||||||
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||||
} from '../helpers/remove-job-configuration.js';
|
} from '../helpers/remove-job-configuration.js';
|
||||||
|
|
||||||
export const worker = new Worker(
|
const flowWorker = new Worker(
|
||||||
'flow',
|
'flow',
|
||||||
async (job) => {
|
async (job) => {
|
||||||
const { flowId } = job.data;
|
const { flowId } = job.data;
|
||||||
@@ -64,11 +63,11 @@ export const worker = new Worker(
|
|||||||
{ connection: redisConfig }
|
{ connection: redisConfig }
|
||||||
);
|
);
|
||||||
|
|
||||||
worker.on('completed', (job) => {
|
flowWorker.on('completed', (job) => {
|
||||||
logger.info(`JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has started!`);
|
logger.info(`JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has started!`);
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('failed', async (job, err) => {
|
flowWorker.on('failed', async (job, err) => {
|
||||||
const errorMessage = `
|
const errorMessage = `
|
||||||
JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has failed to start with ${err.message}
|
JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has failed to start with ${err.message}
|
||||||
\n ${err.stack}
|
\n ${err.stack}
|
||||||
@@ -95,6 +94,4 @@ worker.on('failed', async (job, err) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
export default flowWorker;
|
||||||
await worker.close();
|
|
||||||
});
|
|
||||||
|
21
packages/backend/src/workers/index.js
Normal file
21
packages/backend/src/workers/index.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import appConfig from '../config/app.js';
|
||||||
|
import actionWorker from './action.js';
|
||||||
|
import emailWorker from './email.js';
|
||||||
|
import flowWorker from './flow.js';
|
||||||
|
import triggerWorker from './trigger.js';
|
||||||
|
import deleteUserWorker from './delete-user.ee.js';
|
||||||
|
import removeCancelledSubscriptionsWorker from './remove-cancelled-subscriptions.ee.js';
|
||||||
|
|
||||||
|
const workers = [
|
||||||
|
actionWorker,
|
||||||
|
emailWorker,
|
||||||
|
flowWorker,
|
||||||
|
triggerWorker,
|
||||||
|
deleteUserWorker,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (appConfig.isCloud) {
|
||||||
|
workers.push(removeCancelledSubscriptionsWorker);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default workers;
|
@@ -1,12 +1,11 @@
|
|||||||
import { Worker } from 'bullmq';
|
import { Worker } from 'bullmq';
|
||||||
import process from 'node:process';
|
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import * as Sentry from '../helpers/sentry.ee.js';
|
import * as Sentry from '../helpers/sentry.ee.js';
|
||||||
import redisConfig from '../config/redis.js';
|
import redisConfig from '../config/redis.js';
|
||||||
import logger from '../helpers/logger.js';
|
import logger from '../helpers/logger.js';
|
||||||
import Subscription from '../models/subscription.ee.js';
|
import Subscription from '../models/subscription.ee.js';
|
||||||
|
|
||||||
export const worker = new Worker(
|
const removeCancelledSubscriptionsWorker = new Worker(
|
||||||
'remove-cancelled-subscriptions',
|
'remove-cancelled-subscriptions',
|
||||||
async () => {
|
async () => {
|
||||||
await Subscription.query()
|
await Subscription.query()
|
||||||
@@ -23,13 +22,13 @@ export const worker = new Worker(
|
|||||||
{ connection: redisConfig }
|
{ connection: redisConfig }
|
||||||
);
|
);
|
||||||
|
|
||||||
worker.on('completed', (job) => {
|
removeCancelledSubscriptionsWorker.on('completed', (job) => {
|
||||||
logger.info(
|
logger.info(
|
||||||
`JOB ID: ${job.id} - The cancelled subscriptions have been removed!`
|
`JOB ID: ${job.id} - The cancelled subscriptions have been removed!`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('failed', (job, err) => {
|
removeCancelledSubscriptionsWorker.on('failed', (job, err) => {
|
||||||
const errorMessage = `
|
const errorMessage = `
|
||||||
JOB ID: ${job.id} - ERROR: The cancelled subscriptions can not be removed! ${err.message}
|
JOB ID: ${job.id} - ERROR: The cancelled subscriptions can not be removed! ${err.message}
|
||||||
\n ${err.stack}
|
\n ${err.stack}
|
||||||
@@ -42,6 +41,4 @@ worker.on('failed', (job, err) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
export default removeCancelledSubscriptionsWorker;
|
||||||
await worker.close();
|
|
||||||
});
|
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { Worker } from 'bullmq';
|
import { Worker } from 'bullmq';
|
||||||
import process from 'node:process';
|
|
||||||
|
|
||||||
import * as Sentry from '../helpers/sentry.ee.js';
|
import * as Sentry from '../helpers/sentry.ee.js';
|
||||||
import redisConfig from '../config/redis.js';
|
import redisConfig from '../config/redis.js';
|
||||||
@@ -12,7 +11,7 @@ import {
|
|||||||
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||||
} from '../helpers/remove-job-configuration.js';
|
} from '../helpers/remove-job-configuration.js';
|
||||||
|
|
||||||
export const worker = new Worker(
|
const triggerWorker = new Worker(
|
||||||
'trigger',
|
'trigger',
|
||||||
async (job) => {
|
async (job) => {
|
||||||
const { flowId, executionId, stepId, executionStep } = await processTrigger(
|
const { flowId, executionId, stepId, executionStep } = await processTrigger(
|
||||||
@@ -41,11 +40,11 @@ export const worker = new Worker(
|
|||||||
{ connection: redisConfig }
|
{ connection: redisConfig }
|
||||||
);
|
);
|
||||||
|
|
||||||
worker.on('completed', (job) => {
|
triggerWorker.on('completed', (job) => {
|
||||||
logger.info(`JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has started!`);
|
logger.info(`JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has started!`);
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('failed', (job, err) => {
|
triggerWorker.on('failed', (job, err) => {
|
||||||
const errorMessage = `
|
const errorMessage = `
|
||||||
JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has failed to start with ${err.message}
|
JOB ID: ${job.id} - FLOW ID: ${job.data.flowId} has failed to start with ${err.message}
|
||||||
\n ${err.stack}
|
\n ${err.stack}
|
||||||
@@ -60,6 +59,4 @@ worker.on('failed', (job, err) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGTERM', async () => {
|
export default triggerWorker;
|
||||||
await worker.close();
|
|
||||||
});
|
|
||||||
|
@@ -1,16 +1,15 @@
|
|||||||
|
import { faker } from '@faker-js/faker';
|
||||||
import { createRole } from './role.js';
|
import { createRole } from './role.js';
|
||||||
|
import RoleMapping from '../../src/models/role-mapping.ee.js';
|
||||||
import { createSamlAuthProvider } from './saml-auth-provider.ee.js';
|
import { createSamlAuthProvider } from './saml-auth-provider.ee.js';
|
||||||
import SamlAuthProviderRoleMapping from '../../src/models/saml-auth-providers-role-mapping.ee.js';
|
|
||||||
|
|
||||||
export const createRoleMapping = async (params = {}) => {
|
export const createRoleMapping = async (params = {}) => {
|
||||||
params.roleId = params?.roleId || (await createRole()).id;
|
params.roleId = params.roleId || (await createRole()).id;
|
||||||
params.samlAuthProviderId =
|
params.samlAuthProviderId =
|
||||||
params?.samlAuthProviderId || (await createSamlAuthProvider()).id;
|
params.samlAuthProviderId || (await createSamlAuthProvider()).id;
|
||||||
|
params.remoteRoleName = params.remoteRoleName || faker.person.jobType();
|
||||||
|
|
||||||
params.remoteRoleName = params?.remoteRoleName || 'User';
|
const roleMapping = await RoleMapping.query().insertAndFetch(params);
|
||||||
|
|
||||||
const samlAuthProviderRoleMapping =
|
return roleMapping;
|
||||||
await SamlAuthProviderRoleMapping.query().insertAndFetch(params);
|
|
||||||
|
|
||||||
return samlAuthProviderRoleMapping;
|
|
||||||
};
|
};
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
import { faker } from '@faker-js/faker';
|
|
||||||
import { createRole } from './role.js';
|
|
||||||
import SamlAuthProvidersRoleMapping from '../../src/models/saml-auth-providers-role-mapping.ee.js';
|
|
||||||
import { createSamlAuthProvider } from './saml-auth-provider.ee.js';
|
|
||||||
|
|
||||||
export const createSamlAuthProvidersRoleMapping = async (params = {}) => {
|
|
||||||
params.roleId = params.roleId || (await createRole()).id;
|
|
||||||
params.samlAuthProviderId =
|
|
||||||
params.samlAuthProviderId || (await createSamlAuthProvider()).id;
|
|
||||||
params.remoteRoleName = params.remoteRoleName || faker.person.jobType();
|
|
||||||
|
|
||||||
const samlAuthProvider =
|
|
||||||
await SamlAuthProvidersRoleMapping.query().insertAndFetch(params);
|
|
||||||
|
|
||||||
return samlAuthProvider;
|
|
||||||
};
|
|
@@ -15,7 +15,7 @@ const getRoleMappingsMock = async (roleMappings) => {
|
|||||||
currentPage: null,
|
currentPage: null,
|
||||||
isArray: true,
|
isArray: true,
|
||||||
totalPages: null,
|
totalPages: null,
|
||||||
type: 'SamlAuthProvidersRoleMapping',
|
type: 'RoleMapping',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -15,7 +15,7 @@ const createRoleMappingsMock = async (roleMappings) => {
|
|||||||
currentPage: null,
|
currentPage: null,
|
||||||
isArray: true,
|
isArray: true,
|
||||||
totalPages: null,
|
totalPages: null,
|
||||||
type: 'SamlAuthProvidersRoleMapping',
|
type: 'RoleMapping',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -2,8 +2,25 @@ import { defineConfig } from 'vitest/config';
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
|
root: './',
|
||||||
environment: 'node',
|
environment: 'node',
|
||||||
setupFiles: ['./test/setup/global-hooks.js'],
|
setupFiles: ['./test/setup/global-hooks.js'],
|
||||||
globals: true,
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@@ -2,6 +2,31 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@ampproject/remapping@^2.3.0":
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
|
||||||
|
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.5"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.24"
|
||||||
|
|
||||||
|
"@babel/helper-string-parser@^7.25.9":
|
||||||
|
version "7.25.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
|
||||||
|
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier@^7.25.9":
|
||||||
|
version "7.25.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
|
||||||
|
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
|
||||||
|
|
||||||
|
"@babel/parser@^7.25.4":
|
||||||
|
version "7.26.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.2.tgz#fd7b6f487cfea09889557ef5d4eeb9ff9a5abd11"
|
||||||
|
integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.26.0"
|
||||||
|
|
||||||
"@babel/runtime@^7.15.4":
|
"@babel/runtime@^7.15.4":
|
||||||
version "7.26.0"
|
version "7.26.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
|
||||||
@@ -9,6 +34,19 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
|
"@babel/types@^7.25.4", "@babel/types@^7.26.0":
|
||||||
|
version "7.26.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff"
|
||||||
|
integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser" "^7.25.9"
|
||||||
|
"@babel/helper-validator-identifier" "^7.25.9"
|
||||||
|
|
||||||
|
"@bcoe/v8-coverage@^0.2.3":
|
||||||
|
version "0.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@bull-board/api@3.11.1":
|
"@bull-board/api@3.11.1":
|
||||||
version "3.11.1"
|
version "3.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-3.11.1.tgz#98b2c9556f643718bb5bde4a1306e6706af8192e"
|
resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-3.11.1.tgz#98b2c9556f643718bb5bde4a1306e6706af8192e"
|
||||||
@@ -242,18 +280,43 @@
|
|||||||
wrap-ansi "^8.1.0"
|
wrap-ansi "^8.1.0"
|
||||||
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
|
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
|
||||||
|
|
||||||
"@jest/schemas@^29.6.3":
|
"@istanbuljs/schema@^0.1.2":
|
||||||
version "29.6.3"
|
version "0.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03"
|
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
|
||||||
integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==
|
integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
|
||||||
dependencies:
|
|
||||||
"@sinclair/typebox" "^0.27.8"
|
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.5.0":
|
"@jridgewell/gen-mapping@^0.3.5":
|
||||||
|
version "0.3.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
|
||||||
|
integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/set-array" "^1.2.1"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.24"
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri@^3.1.0":
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
|
||||||
|
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
|
||||||
|
|
||||||
|
"@jridgewell/set-array@^1.2.1":
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
|
||||||
|
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||||
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24":
|
||||||
|
version "0.3.25"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
|
||||||
|
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/resolve-uri" "^3.1.0"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||||
|
|
||||||
"@mapbox/node-pre-gyp@^1.0.11":
|
"@mapbox/node-pre-gyp@^1.0.11":
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
|
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
|
||||||
@@ -574,11 +637,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@sentry/types" "7.120.0"
|
"@sentry/types" "7.120.0"
|
||||||
|
|
||||||
"@sinclair/typebox@^0.27.8":
|
|
||||||
version "0.27.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
|
||||||
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
|
|
||||||
|
|
||||||
"@types/body-parser@*":
|
"@types/body-parser@*":
|
||||||
version "1.19.5"
|
version "1.19.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4"
|
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4"
|
||||||
@@ -770,49 +828,82 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
|
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
|
||||||
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
|
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
|
||||||
|
|
||||||
"@vitest/expect@1.6.0":
|
"@vitest/coverage-v8@^2.1.5":
|
||||||
version "1.6.0"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.6.0.tgz#0b3ba0914f738508464983f4d811bc122b51fb30"
|
resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-2.1.5.tgz#74ef3bf6737f9897a54af22f820d90e85883ff83"
|
||||||
integrity sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==
|
integrity sha512-/RoopB7XGW7UEkUndRXF87A9CwkoZAJW01pj8/3pgmDVsjMH2IKy6H1A38po9tmUlwhSyYs0az82rbKd9Yaynw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vitest/spy" "1.6.0"
|
"@ampproject/remapping" "^2.3.0"
|
||||||
"@vitest/utils" "1.6.0"
|
"@bcoe/v8-coverage" "^0.2.3"
|
||||||
chai "^4.3.10"
|
debug "^4.3.7"
|
||||||
|
istanbul-lib-coverage "^3.2.2"
|
||||||
|
istanbul-lib-report "^3.0.1"
|
||||||
|
istanbul-lib-source-maps "^5.0.6"
|
||||||
|
istanbul-reports "^3.1.7"
|
||||||
|
magic-string "^0.30.12"
|
||||||
|
magicast "^0.3.5"
|
||||||
|
std-env "^3.8.0"
|
||||||
|
test-exclude "^7.0.1"
|
||||||
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
"@vitest/runner@1.6.0":
|
"@vitest/expect@2.1.5":
|
||||||
version "1.6.0"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.6.0.tgz#a6de49a96cb33b0e3ba0d9064a3e8d6ce2f08825"
|
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.5.tgz#5a6afa6314cae7a61847927bb5bc038212ca7381"
|
||||||
integrity sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==
|
integrity sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vitest/utils" "1.6.0"
|
"@vitest/spy" "2.1.5"
|
||||||
p-limit "^5.0.0"
|
"@vitest/utils" "2.1.5"
|
||||||
pathe "^1.1.1"
|
chai "^5.1.2"
|
||||||
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
"@vitest/snapshot@1.6.0":
|
"@vitest/mocker@2.1.5":
|
||||||
version "1.6.0"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.6.0.tgz#deb7e4498a5299c1198136f56e6e0f692e6af470"
|
resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.5.tgz#54ee50648bc0bb606dfc58e13edfacb8b9208324"
|
||||||
integrity sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==
|
integrity sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
magic-string "^0.30.5"
|
"@vitest/spy" "2.1.5"
|
||||||
pathe "^1.1.1"
|
|
||||||
pretty-format "^29.7.0"
|
|
||||||
|
|
||||||
"@vitest/spy@1.6.0":
|
|
||||||
version "1.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.6.0.tgz#362cbd42ccdb03f1613798fde99799649516906d"
|
|
||||||
integrity sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==
|
|
||||||
dependencies:
|
|
||||||
tinyspy "^2.2.0"
|
|
||||||
|
|
||||||
"@vitest/utils@1.6.0":
|
|
||||||
version "1.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.6.0.tgz#5c5675ca7d6f546a7b4337de9ae882e6c57896a1"
|
|
||||||
integrity sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==
|
|
||||||
dependencies:
|
|
||||||
diff-sequences "^29.6.3"
|
|
||||||
estree-walker "^3.0.3"
|
estree-walker "^3.0.3"
|
||||||
loupe "^2.3.7"
|
magic-string "^0.30.12"
|
||||||
pretty-format "^29.7.0"
|
|
||||||
|
"@vitest/pretty-format@2.1.5", "@vitest/pretty-format@^2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.5.tgz#bc79b8826d4a63dc04f2a75d2944694039fa50aa"
|
||||||
|
integrity sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==
|
||||||
|
dependencies:
|
||||||
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
|
"@vitest/runner@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.5.tgz#4d5e2ba2dfc0af74e4b0f9f3f8be020559b26ea9"
|
||||||
|
integrity sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==
|
||||||
|
dependencies:
|
||||||
|
"@vitest/utils" "2.1.5"
|
||||||
|
pathe "^1.1.2"
|
||||||
|
|
||||||
|
"@vitest/snapshot@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.5.tgz#a09a8712547452a84e08b3ec97b270d9cc156b4f"
|
||||||
|
integrity sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==
|
||||||
|
dependencies:
|
||||||
|
"@vitest/pretty-format" "2.1.5"
|
||||||
|
magic-string "^0.30.12"
|
||||||
|
pathe "^1.1.2"
|
||||||
|
|
||||||
|
"@vitest/spy@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.5.tgz#f790d1394a5030644217ce73562e92465e83147e"
|
||||||
|
integrity sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==
|
||||||
|
dependencies:
|
||||||
|
tinyspy "^3.0.2"
|
||||||
|
|
||||||
|
"@vitest/utils@2.1.5":
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.5.tgz#0e19ce677c870830a1573d33ee86b0d6109e9546"
|
||||||
|
integrity sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==
|
||||||
|
dependencies:
|
||||||
|
"@vitest/pretty-format" "2.1.5"
|
||||||
|
loupe "^3.1.2"
|
||||||
|
tinyrainbow "^1.2.0"
|
||||||
|
|
||||||
"@xmldom/xmldom@^0.8.5", "@xmldom/xmldom@^0.8.6", "@xmldom/xmldom@^0.8.8":
|
"@xmldom/xmldom@^0.8.5", "@xmldom/xmldom@^0.8.6", "@xmldom/xmldom@^0.8.8":
|
||||||
version "0.8.10"
|
version "0.8.10"
|
||||||
@@ -847,14 +938,7 @@ acorn-jsx@^5.3.2:
|
|||||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||||
|
|
||||||
acorn-walk@^8.3.2:
|
acorn@^8.9.0:
|
||||||
version "8.3.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7"
|
|
||||||
integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==
|
|
||||||
dependencies:
|
|
||||||
acorn "^8.11.0"
|
|
||||||
|
|
||||||
acorn@^8.11.0, acorn@^8.14.0, acorn@^8.9.0:
|
|
||||||
version "8.14.0"
|
version "8.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
|
||||||
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
|
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
|
||||||
@@ -925,11 +1009,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
color-convert "^2.0.1"
|
color-convert "^2.0.1"
|
||||||
|
|
||||||
ansi-styles@^5.0.0:
|
|
||||||
version "5.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
|
|
||||||
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
|
|
||||||
|
|
||||||
ansi-styles@^6.1.0:
|
ansi-styles@^6.1.0:
|
||||||
version "6.2.1"
|
version "6.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
|
||||||
@@ -976,10 +1055,10 @@ asap@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||||
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
|
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
|
||||||
|
|
||||||
assertion-error@^1.1.0:
|
assertion-error@^2.0.1:
|
||||||
version "1.1.0"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
|
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
|
||||||
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
|
integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
|
||||||
|
|
||||||
async@^3.2.3:
|
async@^3.2.3:
|
||||||
version "3.2.6"
|
version "3.2.6"
|
||||||
@@ -1211,18 +1290,16 @@ callsites@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||||
|
|
||||||
chai@^4.3.10:
|
chai@^5.1.2:
|
||||||
version "4.5.0"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8"
|
resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d"
|
||||||
integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==
|
integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==
|
||||||
dependencies:
|
dependencies:
|
||||||
assertion-error "^1.1.0"
|
assertion-error "^2.0.1"
|
||||||
check-error "^1.0.3"
|
check-error "^2.1.1"
|
||||||
deep-eql "^4.1.3"
|
deep-eql "^5.0.1"
|
||||||
get-func-name "^2.0.2"
|
loupe "^3.1.0"
|
||||||
loupe "^2.3.6"
|
pathval "^2.0.0"
|
||||||
pathval "^1.1.1"
|
|
||||||
type-detect "^4.1.0"
|
|
||||||
|
|
||||||
chalk@^4.0.0, chalk@^4.0.2:
|
chalk@^4.0.0, chalk@^4.0.2:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
@@ -1237,12 +1314,10 @@ charenc@0.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||||
integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
|
integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
|
||||||
|
|
||||||
check-error@^1.0.3:
|
check-error@^2.1.1:
|
||||||
version "1.0.3"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694"
|
resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
|
||||||
integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==
|
integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
|
||||||
dependencies:
|
|
||||||
get-func-name "^2.0.2"
|
|
||||||
|
|
||||||
chokidar@^3.5.2:
|
chokidar@^3.5.2:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
@@ -1379,11 +1454,6 @@ concat-stream@^1.5.2:
|
|||||||
readable-stream "^2.2.2"
|
readable-stream "^2.2.2"
|
||||||
typedarray "^0.0.6"
|
typedarray "^0.0.6"
|
||||||
|
|
||||||
confbox@^0.1.8:
|
|
||||||
version "0.1.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06"
|
|
||||||
integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==
|
|
||||||
|
|
||||||
console-control-strings@^1.0.0, console-control-strings@^1.1.0:
|
console-control-strings@^1.0.0, console-control-strings@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||||
@@ -1441,7 +1511,7 @@ cron-parser@^4.2.1, cron-parser@^4.6.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
luxon "^3.2.1"
|
luxon "^3.2.1"
|
||||||
|
|
||||||
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
cross-spawn@^7.0.0, cross-spawn@^7.0.2:
|
||||||
version "7.0.6"
|
version "7.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||||
@@ -1488,7 +1558,7 @@ debug@2.6.9, debug@~2.6.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
|
debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7:
|
||||||
version "4.3.7"
|
version "4.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
||||||
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||||
@@ -1516,12 +1586,10 @@ decompress-response@^6.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-response "^3.1.0"
|
mimic-response "^3.1.0"
|
||||||
|
|
||||||
deep-eql@^4.1.3:
|
deep-eql@^5.0.1:
|
||||||
version "4.1.4"
|
version "5.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.4.tgz#d0d3912865911bb8fac5afb4e3acfa6a28dc72b7"
|
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
|
||||||
integrity sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==
|
integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==
|
||||||
dependencies:
|
|
||||||
type-detect "^4.0.0"
|
|
||||||
|
|
||||||
deep-extend@^0.6.0:
|
deep-extend@^0.6.0:
|
||||||
version "0.6.0"
|
version "0.6.0"
|
||||||
@@ -1590,11 +1658,6 @@ dezalgo@^1.0.4:
|
|||||||
asap "^2.0.0"
|
asap "^2.0.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
diff-sequences@^29.6.3:
|
|
||||||
version "29.6.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
|
|
||||||
integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
|
|
||||||
|
|
||||||
doctrine@^3.0.0:
|
doctrine@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
|
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
|
||||||
@@ -1722,6 +1785,11 @@ es-errors@^1.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||||
|
|
||||||
|
es-module-lexer@^1.5.4:
|
||||||
|
version "1.5.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78"
|
||||||
|
integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==
|
||||||
|
|
||||||
esbuild@^0.21.3:
|
esbuild@^0.21.3:
|
||||||
version "0.21.5"
|
version "0.21.5"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
|
||||||
@@ -1885,26 +1953,16 @@ etag@~1.8.1:
|
|||||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||||
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
|
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
|
||||||
|
|
||||||
execa@^8.0.1:
|
|
||||||
version "8.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c"
|
|
||||||
integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==
|
|
||||||
dependencies:
|
|
||||||
cross-spawn "^7.0.3"
|
|
||||||
get-stream "^8.0.1"
|
|
||||||
human-signals "^5.0.0"
|
|
||||||
is-stream "^3.0.0"
|
|
||||||
merge-stream "^2.0.0"
|
|
||||||
npm-run-path "^5.1.0"
|
|
||||||
onetime "^6.0.0"
|
|
||||||
signal-exit "^4.1.0"
|
|
||||||
strip-final-newline "^3.0.0"
|
|
||||||
|
|
||||||
expand-template@^2.0.3:
|
expand-template@^2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
|
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
|
||||||
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
|
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
|
||||||
|
|
||||||
|
expect-type@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75"
|
||||||
|
integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==
|
||||||
|
|
||||||
exponential-backoff@^3.1.1:
|
exponential-backoff@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
|
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
|
||||||
@@ -2209,11 +2267,6 @@ gauge@^3.0.0:
|
|||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
wide-align "^1.1.2"
|
wide-align "^1.1.2"
|
||||||
|
|
||||||
get-func-name@^2.0.1, get-func-name@^2.0.2:
|
|
||||||
version "2.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
|
|
||||||
integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
|
|
||||||
|
|
||||||
get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
|
get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
|
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
|
||||||
@@ -2235,11 +2288,6 @@ get-port@^5.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
|
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
|
||||||
integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
|
integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
|
||||||
|
|
||||||
get-stream@^8.0.1:
|
|
||||||
version "8.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2"
|
|
||||||
integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==
|
|
||||||
|
|
||||||
getopts@2.3.0:
|
getopts@2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4"
|
resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4"
|
||||||
@@ -2264,7 +2312,7 @@ glob-parent@~5.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
||||||
glob@^10.2.2, glob@^10.3.10:
|
glob@^10.2.2, glob@^10.3.10, glob@^10.4.1:
|
||||||
version "10.4.5"
|
version "10.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
|
||||||
integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
|
integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
|
||||||
@@ -2384,6 +2432,11 @@ hexoid@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
|
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
|
||||||
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
|
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
|
||||||
|
|
||||||
|
html-escaper@^2.0.0:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
||||||
|
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
||||||
|
|
||||||
http-cache-semantics@^4.1.1:
|
http-cache-semantics@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||||
@@ -2445,11 +2498,6 @@ https-proxy-agent@^7.0.1:
|
|||||||
agent-base "^7.0.2"
|
agent-base "^7.0.2"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
human-signals@^5.0.0:
|
|
||||||
version "5.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
|
|
||||||
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
|
|
||||||
|
|
||||||
iconv-lite@0.4.24:
|
iconv-lite@0.4.24:
|
||||||
version "0.4.24"
|
version "0.4.24"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||||
@@ -2624,11 +2672,6 @@ is-stream@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
|
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
|
||||||
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
||||||
|
|
||||||
is-stream@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
|
|
||||||
integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
|
|
||||||
|
|
||||||
isarray@~1.0.0:
|
isarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||||
@@ -2651,6 +2694,37 @@ isolated-vm@^5.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prebuild-install "^7.1.1"
|
prebuild-install "^7.1.1"
|
||||||
|
|
||||||
|
istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2:
|
||||||
|
version "3.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
|
||||||
|
integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
|
||||||
|
|
||||||
|
istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
|
||||||
|
integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
|
||||||
|
dependencies:
|
||||||
|
istanbul-lib-coverage "^3.0.0"
|
||||||
|
make-dir "^4.0.0"
|
||||||
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
|
istanbul-lib-source-maps@^5.0.6:
|
||||||
|
version "5.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441"
|
||||||
|
integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.23"
|
||||||
|
debug "^4.1.1"
|
||||||
|
istanbul-lib-coverage "^3.0.0"
|
||||||
|
|
||||||
|
istanbul-reports@^3.1.7:
|
||||||
|
version "3.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b"
|
||||||
|
integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==
|
||||||
|
dependencies:
|
||||||
|
html-escaper "^2.0.0"
|
||||||
|
istanbul-lib-report "^3.0.0"
|
||||||
|
|
||||||
jackspeak@^3.1.2:
|
jackspeak@^3.1.2:
|
||||||
version "3.4.3"
|
version "3.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
|
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
|
||||||
@@ -2675,11 +2749,6 @@ join-component@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5"
|
resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5"
|
||||||
integrity sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==
|
integrity sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==
|
||||||
|
|
||||||
js-tokens@^9.0.0:
|
|
||||||
version "9.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.0.tgz#0f893996d6f3ed46df7f0a3b12a03f5fd84223c1"
|
|
||||||
integrity sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==
|
|
||||||
|
|
||||||
js-yaml@^4.1.0:
|
js-yaml@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||||
@@ -2797,14 +2866,6 @@ lie@3.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
immediate "~3.0.5"
|
immediate "~3.0.5"
|
||||||
|
|
||||||
local-pkg@^0.5.0:
|
|
||||||
version "0.5.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.1.tgz#69658638d2a95287534d4c2fff757980100dbb6d"
|
|
||||||
integrity sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==
|
|
||||||
dependencies:
|
|
||||||
mlly "^1.7.3"
|
|
||||||
pkg-types "^1.2.1"
|
|
||||||
|
|
||||||
localforage@^1.8.1:
|
localforage@^1.8.1:
|
||||||
version "1.10.0"
|
version "1.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
|
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
|
||||||
@@ -2896,12 +2957,10 @@ logform@^2.7.0:
|
|||||||
safe-stable-stringify "^2.3.1"
|
safe-stable-stringify "^2.3.1"
|
||||||
triple-beam "^1.3.0"
|
triple-beam "^1.3.0"
|
||||||
|
|
||||||
loupe@^2.3.6, loupe@^2.3.7:
|
loupe@^3.1.0, loupe@^3.1.2:
|
||||||
version "2.3.7"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697"
|
resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240"
|
||||||
integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==
|
integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==
|
||||||
dependencies:
|
|
||||||
get-func-name "^2.0.1"
|
|
||||||
|
|
||||||
lru-cache@^10.0.1, lru-cache@^10.2.0:
|
lru-cache@^10.0.1, lru-cache@^10.2.0:
|
||||||
version "10.4.3"
|
version "10.4.3"
|
||||||
@@ -2918,13 +2977,22 @@ luxon@^3.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20"
|
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20"
|
||||||
integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==
|
integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==
|
||||||
|
|
||||||
magic-string@^0.30.5:
|
magic-string@^0.30.12:
|
||||||
version "0.30.13"
|
version "0.30.13"
|
||||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.13.tgz#92438e3ff4946cf54f18247c981e5c161c46683c"
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.13.tgz#92438e3ff4946cf54f18247c981e5c161c46683c"
|
||||||
integrity sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==
|
integrity sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/sourcemap-codec" "^1.5.0"
|
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||||
|
|
||||||
|
magicast@^0.3.5:
|
||||||
|
version "0.3.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739"
|
||||||
|
integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.25.4"
|
||||||
|
"@babel/types" "^7.25.4"
|
||||||
|
source-map-js "^1.2.0"
|
||||||
|
|
||||||
make-dir@^3.1.0:
|
make-dir@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
||||||
@@ -2932,6 +3000,13 @@ make-dir@^3.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver "^6.0.0"
|
semver "^6.0.0"
|
||||||
|
|
||||||
|
make-dir@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
|
||||||
|
integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
|
||||||
|
dependencies:
|
||||||
|
semver "^7.5.3"
|
||||||
|
|
||||||
make-fetch-happen@^13.0.0:
|
make-fetch-happen@^13.0.0:
|
||||||
version "13.0.1"
|
version "13.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz#273ba2f78f45e1f3a6dca91cede87d9fa4821e36"
|
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz#273ba2f78f45e1f3a6dca91cede87d9fa4821e36"
|
||||||
@@ -2974,11 +3049,6 @@ merge-descriptors@1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||||
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
||||||
|
|
||||||
merge-stream@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
|
||||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
|
||||||
|
|
||||||
methods@^1.1.2, methods@~1.1.2:
|
methods@^1.1.2, methods@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||||
@@ -3006,11 +3076,6 @@ mime@2.6.0:
|
|||||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
|
resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
|
||||||
integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
|
integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
|
||||||
|
|
||||||
mimic-fn@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
|
|
||||||
integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
|
|
||||||
|
|
||||||
mimic-response@^3.1.0:
|
mimic-response@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
|
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
|
||||||
@@ -3123,16 +3188,6 @@ mkdirp@^1.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||||
|
|
||||||
mlly@^1.7.2, mlly@^1.7.3:
|
|
||||||
version "1.7.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.3.tgz#d86c0fcd8ad8e16395eb764a5f4b831590cee48c"
|
|
||||||
integrity sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==
|
|
||||||
dependencies:
|
|
||||||
acorn "^8.14.0"
|
|
||||||
pathe "^1.1.2"
|
|
||||||
pkg-types "^1.2.1"
|
|
||||||
ufo "^1.5.4"
|
|
||||||
|
|
||||||
morgan@^1.10.0:
|
morgan@^1.10.0:
|
||||||
version "1.10.0"
|
version "1.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
|
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
|
||||||
@@ -3320,13 +3375,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||||
|
|
||||||
npm-run-path@^5.1.0:
|
|
||||||
version "5.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f"
|
|
||||||
integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==
|
|
||||||
dependencies:
|
|
||||||
path-key "^4.0.0"
|
|
||||||
|
|
||||||
npmlog@^5.0.1:
|
npmlog@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
|
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
|
||||||
@@ -3401,13 +3449,6 @@ one-time@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fn.name "1.x.x"
|
fn.name "1.x.x"
|
||||||
|
|
||||||
onetime@^6.0.0:
|
|
||||||
version "6.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
|
|
||||||
integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==
|
|
||||||
dependencies:
|
|
||||||
mimic-fn "^4.0.0"
|
|
||||||
|
|
||||||
optionator@^0.9.3:
|
optionator@^0.9.3:
|
||||||
version "0.9.4"
|
version "0.9.4"
|
||||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
|
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
|
||||||
@@ -3427,13 +3468,6 @@ p-limit@^3.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yocto-queue "^0.1.0"
|
yocto-queue "^0.1.0"
|
||||||
|
|
||||||
p-limit@^5.0.0:
|
|
||||||
version "5.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985"
|
|
||||||
integrity sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==
|
|
||||||
dependencies:
|
|
||||||
yocto-queue "^1.0.0"
|
|
||||||
|
|
||||||
p-locate@^5.0.0:
|
p-locate@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
|
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
|
||||||
@@ -3494,11 +3528,6 @@ path-key@^3.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||||
|
|
||||||
path-key@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
|
|
||||||
integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==
|
|
||||||
|
|
||||||
path-parse@^1.0.7:
|
path-parse@^1.0.7:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||||
@@ -3517,15 +3546,15 @@ path-to-regexp@0.1.7:
|
|||||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||||
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
|
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
|
||||||
|
|
||||||
pathe@^1.1.1, pathe@^1.1.2:
|
pathe@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
|
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
|
||||||
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
|
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
|
||||||
|
|
||||||
pathval@^1.1.1:
|
pathval@^2.0.0:
|
||||||
version "1.1.1"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
|
resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25"
|
||||||
integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==
|
integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==
|
||||||
|
|
||||||
pause@0.0.1:
|
pause@0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
@@ -3598,7 +3627,7 @@ php-serialize@^4.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/php-serialize/-/php-serialize-4.1.1.tgz#1a614fde3da42361af05afffbaf967fb6556591e"
|
resolved "https://registry.yarnpkg.com/php-serialize/-/php-serialize-4.1.1.tgz#1a614fde3da42361af05afffbaf967fb6556591e"
|
||||||
integrity sha512-7drCrSZdJ05UdG3hyYEIRW0XyKyUFkxa5A3dpIp3NTjUHpI080pkdBAvqaBtkA+kBkMeXX3XnaSnaLGJRz071A==
|
integrity sha512-7drCrSZdJ05UdG3hyYEIRW0XyKyUFkxa5A3dpIp3NTjUHpI080pkdBAvqaBtkA+kBkMeXX3XnaSnaLGJRz071A==
|
||||||
|
|
||||||
picocolors@^1.0.0, picocolors@^1.1.1:
|
picocolors@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||||
@@ -3608,15 +3637,6 @@ picomatch@^2.0.4, picomatch@^2.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||||
|
|
||||||
pkg-types@^1.2.1:
|
|
||||||
version "1.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.2.1.tgz#6ac4e455a5bb4b9a6185c1c79abd544c901db2e5"
|
|
||||||
integrity sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==
|
|
||||||
dependencies:
|
|
||||||
confbox "^0.1.8"
|
|
||||||
mlly "^1.7.2"
|
|
||||||
pathe "^1.1.2"
|
|
||||||
|
|
||||||
pluralize@^8.0.0:
|
pluralize@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
|
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
|
||||||
@@ -3688,15 +3708,6 @@ prettier@^2.5.1:
|
|||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
||||||
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
||||||
|
|
||||||
pretty-format@^29.7.0:
|
|
||||||
version "29.7.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
|
|
||||||
integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
|
|
||||||
dependencies:
|
|
||||||
"@jest/schemas" "^29.6.3"
|
|
||||||
ansi-styles "^5.0.0"
|
|
||||||
react-is "^18.0.0"
|
|
||||||
|
|
||||||
proc-log@^4.1.0, proc-log@^4.2.0:
|
proc-log@^4.1.0, proc-log@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034"
|
resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034"
|
||||||
@@ -3812,11 +3823,6 @@ rc@^1.2.7:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
react-is@^18.0.0:
|
|
||||||
version "18.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
|
|
||||||
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
|
|
||||||
|
|
||||||
readable-stream@^2.2.2:
|
readable-stream@^2.2.2:
|
||||||
version "2.3.8"
|
version "2.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
|
||||||
@@ -3997,7 +4003,7 @@ semver@^6.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||||
|
|
||||||
semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.4:
|
semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4:
|
||||||
version "7.6.3"
|
version "7.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||||
@@ -4138,7 +4144,7 @@ signal-exit@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
|
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
|
||||||
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
|
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
|
||||||
|
|
||||||
signal-exit@^4.0.1, signal-exit@^4.1.0:
|
signal-exit@^4.0.1:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
|
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
|
||||||
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
|
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
|
||||||
@@ -4193,7 +4199,7 @@ socks@^2.8.3:
|
|||||||
ip-address "^9.0.5"
|
ip-address "^9.0.5"
|
||||||
smart-buffer "^4.2.0"
|
smart-buffer "^4.2.0"
|
||||||
|
|
||||||
source-map-js@^1.2.1:
|
source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||||
@@ -4245,7 +4251,7 @@ statuses@2.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||||
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
|
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
|
||||||
|
|
||||||
std-env@^3.5.0:
|
std-env@^3.8.0:
|
||||||
version "3.8.0"
|
version "3.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5"
|
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5"
|
||||||
integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==
|
integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==
|
||||||
@@ -4255,7 +4261,16 @@ streamsearch@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
|
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
|
||||||
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.3:
|
"string-width-cjs@npm:string-width@^4.2.0":
|
||||||
|
version "4.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
|
dependencies:
|
||||||
|
emoji-regex "^8.0.0"
|
||||||
|
is-fullwidth-code-point "^3.0.0"
|
||||||
|
strip-ansi "^6.0.1"
|
||||||
|
|
||||||
|
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.3:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
@@ -4287,7 +4302,14 @@ string_decoder@~1.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.1.0"
|
safe-buffer "~5.1.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||||
|
version "6.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
|
dependencies:
|
||||||
|
ansi-regex "^5.0.1"
|
||||||
|
|
||||||
|
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
@@ -4301,11 +4323,6 @@ strip-ansi@^7.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex "^6.0.1"
|
ansi-regex "^6.0.1"
|
||||||
|
|
||||||
strip-final-newline@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
|
|
||||||
integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
|
|
||||||
|
|
||||||
strip-json-comments@^3.1.1:
|
strip-json-comments@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||||
@@ -4316,13 +4333,6 @@ strip-json-comments@~2.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||||
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
|
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
|
||||||
|
|
||||||
strip-literal@^2.0.0:
|
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-2.1.0.tgz#6d82ade5e2e74f5c7e8739b6c84692bd65f0bd2a"
|
|
||||||
integrity sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==
|
|
||||||
dependencies:
|
|
||||||
js-tokens "^9.0.0"
|
|
||||||
|
|
||||||
strnum@^1.0.5:
|
strnum@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db"
|
resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db"
|
||||||
@@ -4409,6 +4419,15 @@ tarn@^3.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693"
|
resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693"
|
||||||
integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==
|
integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==
|
||||||
|
|
||||||
|
test-exclude@^7.0.1:
|
||||||
|
version "7.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2"
|
||||||
|
integrity sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==
|
||||||
|
dependencies:
|
||||||
|
"@istanbuljs/schema" "^0.1.2"
|
||||||
|
glob "^10.4.1"
|
||||||
|
minimatch "^9.0.4"
|
||||||
|
|
||||||
text-hex@1.0.x:
|
text-hex@1.0.x:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
|
resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
|
||||||
@@ -4424,20 +4443,30 @@ tildify@2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a"
|
resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a"
|
||||||
integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==
|
integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==
|
||||||
|
|
||||||
tinybench@^2.5.1:
|
tinybench@^2.9.0:
|
||||||
version "2.9.0"
|
version "2.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
|
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
|
||||||
integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
|
integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
|
||||||
|
|
||||||
tinypool@^0.8.3:
|
tinyexec@^0.3.1:
|
||||||
version "0.8.4"
|
version "0.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.4.tgz#e217fe1270d941b39e98c625dcecebb1408c9aa8"
|
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98"
|
||||||
integrity sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==
|
integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==
|
||||||
|
|
||||||
tinyspy@^2.2.0:
|
tinypool@^1.0.1:
|
||||||
version "2.2.1"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.1.tgz#117b2342f1f38a0dbdcc73a50a454883adf861d1"
|
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2"
|
||||||
integrity sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==
|
integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==
|
||||||
|
|
||||||
|
tinyrainbow@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5"
|
||||||
|
integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==
|
||||||
|
|
||||||
|
tinyspy@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
|
||||||
|
integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==
|
||||||
|
|
||||||
to-regex-range@^5.0.1:
|
to-regex-range@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
@@ -4485,11 +4514,6 @@ type-check@^0.4.0, type-check@~0.4.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls "^1.2.1"
|
prelude-ls "^1.2.1"
|
||||||
|
|
||||||
type-detect@^4.0.0, type-detect@^4.1.0:
|
|
||||||
version "4.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c"
|
|
||||||
integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==
|
|
||||||
|
|
||||||
type-fest@^0.20.2:
|
type-fest@^0.20.2:
|
||||||
version "0.20.2"
|
version "0.20.2"
|
||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||||
@@ -4508,11 +4532,6 @@ typedarray@^0.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
|
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
|
||||||
|
|
||||||
ufo@^1.5.4:
|
|
||||||
version "1.5.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.4.tgz#16d6949674ca0c9e0fbbae1fa20a71d7b1ded754"
|
|
||||||
integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==
|
|
||||||
|
|
||||||
uglify-js@^3.1.4:
|
uglify-js@^3.1.4:
|
||||||
version "3.19.3"
|
version "3.19.3"
|
||||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f"
|
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f"
|
||||||
@@ -4579,15 +4598,15 @@ vary@^1, vary@~1.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||||
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
|
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
|
||||||
|
|
||||||
vite-node@1.6.0:
|
vite-node@2.1.5:
|
||||||
version "1.6.0"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.6.0.tgz#2c7e61129bfecc759478fa592754fd9704aaba7f"
|
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.5.tgz#cf28c637b2ebe65921f3118a165b7cf00a1cdf19"
|
||||||
integrity sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==
|
integrity sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==
|
||||||
dependencies:
|
dependencies:
|
||||||
cac "^6.7.14"
|
cac "^6.7.14"
|
||||||
debug "^4.3.4"
|
debug "^4.3.7"
|
||||||
pathe "^1.1.1"
|
es-module-lexer "^1.5.4"
|
||||||
picocolors "^1.0.0"
|
pathe "^1.1.2"
|
||||||
vite "^5.0.0"
|
vite "^5.0.0"
|
||||||
|
|
||||||
vite@^5.0.0:
|
vite@^5.0.0:
|
||||||
@@ -4601,31 +4620,31 @@ vite@^5.0.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.3"
|
fsevents "~2.3.3"
|
||||||
|
|
||||||
vitest@^1.1.3:
|
vitest@^2.1.5:
|
||||||
version "1.6.0"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.6.0.tgz#9d5ad4752a3c451be919e412c597126cffb9892f"
|
resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.5.tgz#a93b7b84a84650130727baae441354e6df118148"
|
||||||
integrity sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==
|
integrity sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vitest/expect" "1.6.0"
|
"@vitest/expect" "2.1.5"
|
||||||
"@vitest/runner" "1.6.0"
|
"@vitest/mocker" "2.1.5"
|
||||||
"@vitest/snapshot" "1.6.0"
|
"@vitest/pretty-format" "^2.1.5"
|
||||||
"@vitest/spy" "1.6.0"
|
"@vitest/runner" "2.1.5"
|
||||||
"@vitest/utils" "1.6.0"
|
"@vitest/snapshot" "2.1.5"
|
||||||
acorn-walk "^8.3.2"
|
"@vitest/spy" "2.1.5"
|
||||||
chai "^4.3.10"
|
"@vitest/utils" "2.1.5"
|
||||||
debug "^4.3.4"
|
chai "^5.1.2"
|
||||||
execa "^8.0.1"
|
debug "^4.3.7"
|
||||||
local-pkg "^0.5.0"
|
expect-type "^1.1.0"
|
||||||
magic-string "^0.30.5"
|
magic-string "^0.30.12"
|
||||||
pathe "^1.1.1"
|
pathe "^1.1.2"
|
||||||
picocolors "^1.0.0"
|
std-env "^3.8.0"
|
||||||
std-env "^3.5.0"
|
tinybench "^2.9.0"
|
||||||
strip-literal "^2.0.0"
|
tinyexec "^0.3.1"
|
||||||
tinybench "^2.5.1"
|
tinypool "^1.0.1"
|
||||||
tinypool "^0.8.3"
|
tinyrainbow "^1.2.0"
|
||||||
vite "^5.0.0"
|
vite "^5.0.0"
|
||||||
vite-node "1.6.0"
|
vite-node "2.1.5"
|
||||||
why-is-node-running "^2.2.2"
|
why-is-node-running "^2.3.0"
|
||||||
|
|
||||||
webidl-conversions@^3.0.0:
|
webidl-conversions@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
@@ -4654,7 +4673,7 @@ which@^4.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isexe "^3.1.1"
|
isexe "^3.1.1"
|
||||||
|
|
||||||
why-is-node-running@^2.2.2:
|
why-is-node-running@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
|
resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
|
||||||
integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==
|
integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==
|
||||||
@@ -4800,8 +4819,3 @@ yocto-queue@^0.1.0:
|
|||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||||
|
|
||||||
yocto-queue@^1.0.0:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110"
|
|
||||||
integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==
|
|
||||||
|
@@ -2,5 +2,4 @@ POSTGRES_DB=automatisch
|
|||||||
POSTGRES_USER=automatisch_user
|
POSTGRES_USER=automatisch_user
|
||||||
POSTGRES_PASSWORD=automatisch_password
|
POSTGRES_PASSWORD=automatisch_password
|
||||||
POSTGRES_PORT=5432
|
POSTGRES_PORT=5432
|
||||||
POSTGRES_HOST=localhost
|
POSTGRES_HOST=localhost
|
||||||
BACKEND_APP_URL=http://localhost:3000
|
|
@@ -1,4 +1,4 @@
|
|||||||
import { BasePage } from "./base-page";
|
import { BasePage } from './base-page';
|
||||||
const { faker } = require('@faker-js/faker');
|
const { faker } = require('@faker-js/faker');
|
||||||
const { expect } = require('@playwright/test');
|
const { expect } = require('@playwright/test');
|
||||||
|
|
||||||
@@ -6,16 +6,18 @@ export class AdminSetupPage extends BasePage {
|
|||||||
path = '/installation';
|
path = '/installation';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('@playwright/test').Page} page
|
* @param {import('@playwright/test').Page} page
|
||||||
*/
|
*/
|
||||||
constructor(page) {
|
constructor(page) {
|
||||||
super(page);
|
super(page);
|
||||||
|
|
||||||
this.fullNameTextField = this.page.getByTestId('fullName-text-field');
|
this.fullNameTextField = this.page.getByTestId('fullName-text-field');
|
||||||
this.emailTextField = this.page.getByTestId('email-text-field');
|
this.emailTextField = this.page.getByTestId('email-text-field');
|
||||||
this.passwordTextField = this.page.getByTestId('password-text-field');
|
this.passwordTextField = this.page.getByTestId('password-text-field');
|
||||||
this.repeatPasswordTextField = this.page.getByTestId('repeat-password-text-field');
|
this.repeatPasswordTextField = this.page.getByTestId(
|
||||||
this.createAdminButton = this.page.getByTestId('signUp-button');
|
'repeat-password-text-field'
|
||||||
|
);
|
||||||
|
this.createAdminButton = this.page.getByTestId('installation-button');
|
||||||
this.invalidFields = this.page.locator('p.Mui-error');
|
this.invalidFields = this.page.locator('p.Mui-error');
|
||||||
this.successAlert = this.page.getByTestId('success-alert');
|
this.successAlert = this.page.getByTestId('success-alert');
|
||||||
}
|
}
|
||||||
@@ -46,7 +48,7 @@ export class AdminSetupPage extends BasePage {
|
|||||||
await this.repeatPasswordTextField.fill(testUser.wronglyRepeatedPassword);
|
await this.repeatPasswordTextField.fill(testUser.wronglyRepeatedPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
async submitAdminForm() {
|
async submitAdminForm() {
|
||||||
await this.createAdminButton.click();
|
await this.createAdminButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +61,10 @@ export class AdminSetupPage extends BasePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async expectSuccessMessageToContainLoginLink() {
|
async expectSuccessMessageToContainLoginLink() {
|
||||||
await expect(await this.successAlert.locator('a')).toHaveAttribute('href', '/login');
|
await expect(await this.successAlert.locator('a')).toHaveAttribute(
|
||||||
|
'href',
|
||||||
|
'/login'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateUser() {
|
generateUser() {
|
||||||
@@ -69,7 +74,7 @@ export class AdminSetupPage extends BasePage {
|
|||||||
fullName: faker.person.fullName(),
|
fullName: faker.person.fullName(),
|
||||||
email: faker.internet.email(),
|
email: faker.internet.email(),
|
||||||
password: faker.internet.password(),
|
password: faker.internet.password(),
|
||||||
wronglyRepeatedPassword: faker.internet.password()
|
wronglyRepeatedPassword: faker.internet.password(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
@@ -20,54 +20,44 @@ export class AdminApplicationSettingsPage extends AuthenticatedPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async allowCustomConnections() {
|
async allowCustomConnections() {
|
||||||
await expect(this.allowCustomConnectionsSwitch).not.toBeChecked();
|
await expect(this.disableConnectionsSwitch).not.toBeChecked();
|
||||||
await this.allowCustomConnectionsSwitch.check();
|
await this.allowCustomConnectionsSwitch.check();
|
||||||
await expect(this.allowCustomConnectionsSwitch).toBeChecked();
|
|
||||||
await this.saveButton.click();
|
await this.saveButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async allowSharedConnections() {
|
async allowSharedConnections() {
|
||||||
await expect(this.allowSharedConnectionsSwitch).not.toBeChecked();
|
await expect(this.disableConnectionsSwitch).not.toBeChecked();
|
||||||
await this.allowSharedConnectionsSwitch.check();
|
await this.allowSharedConnectionsSwitch.check();
|
||||||
await expect(this.allowSharedConnectionsSwitch).toBeChecked();
|
|
||||||
await this.saveButton.click();
|
await this.saveButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async disallowConnections() {
|
async disallowConnections() {
|
||||||
await expect(this.disableConnectionsSwitch).not.toBeChecked();
|
await expect(this.disableConnectionsSwitch).not.toBeChecked();
|
||||||
await this.disableConnectionsSwitch.check();
|
await this.disableConnectionsSwitch.check();
|
||||||
await expect(this.disableConnectionsSwitch).toBeChecked();
|
|
||||||
await this.saveButton.click();
|
await this.saveButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async disallowCustomConnections() {
|
async disallowCustomConnections() {
|
||||||
await expect(this.allowCustomConnectionsSwitch).toBeChecked();
|
await expect(this.disableConnectionsSwitch).toBeChecked();
|
||||||
await this.allowCustomConnectionsSwitch.uncheck();
|
await this.allowCustomConnectionsSwitch.uncheck();
|
||||||
await expect(this.allowCustomConnectionsSwitch).not.toBeChecked();
|
|
||||||
await this.saveButton.click();
|
await this.saveButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async disallowSharedConnections() {
|
async disallowSharedConnections() {
|
||||||
await expect(this.allowSharedConnectionsSwitch).toBeChecked();
|
await expect(this.disableConnectionsSwitch).toBeChecked();
|
||||||
await this.allowSharedConnectionsSwitch.uncheck();
|
await this.allowSharedConnectionsSwitch.uncheck();
|
||||||
await expect(this.allowSharedConnectionsSwitch).not.toBeChecked();
|
|
||||||
await this.saveButton.click();
|
await this.saveButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async allowConnections() {
|
async allowConnections() {
|
||||||
await expect(this.disableConnectionsSwitch).toBeChecked();
|
await expect(this.disableConnectionsSwitch).toBeChecked();
|
||||||
await this.disableConnectionsSwitch.uncheck();
|
await this.disableConnectionsSwitch.uncheck();
|
||||||
await expect(this.disableConnectionsSwitch).not.toBeChecked();
|
|
||||||
await this.saveButton.click();
|
await this.saveButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async expectSuccessSnackbarToBeVisible() {
|
async expectSuccessSnackbarToBeVisible() {
|
||||||
const snackbars = await this.successSnackbar.all();
|
await expect(this.successSnackbar).toHaveCount(1);
|
||||||
for (const snackbar of snackbars) {
|
await this.successSnackbar.click();
|
||||||
await expect(await snackbar.getAttribute('data-snackbar-variant')).toBe(
|
await expect(this.successSnackbar).toHaveCount(0);
|
||||||
'success'
|
|
||||||
);
|
|
||||||
// await snackbar.click();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
import { expect } from '@playwright/test';
|
|
||||||
|
|
||||||
const { AuthenticatedPage } = require('../authenticated-page');
|
const { AuthenticatedPage } = require('../authenticated-page');
|
||||||
const { RoleConditionsModal } = require('./role-conditions-modal');
|
const { RoleConditionsModal } = require('./role-conditions-modal');
|
||||||
|
|
||||||
@@ -18,7 +16,6 @@ export class AdminCreateRolePage extends AuthenticatedPage {
|
|||||||
this.executionRow = page.getByTestId('Execution-permission-row');
|
this.executionRow = page.getByTestId('Execution-permission-row');
|
||||||
this.flowRow = page.getByTestId('Flow-permission-row');
|
this.flowRow = page.getByTestId('Flow-permission-row');
|
||||||
this.pageTitle = page.getByTestId('create-role-title');
|
this.pageTitle = page.getByTestId('create-role-title');
|
||||||
this.permissionsCatalog = page.getByTestId('permissions-catalog');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,8 +104,4 @@ export class AdminCreateRolePage extends AuthenticatedPage {
|
|||||||
throw new Error(`${subject} does not have action ${action}`);
|
throw new Error(`${subject} does not have action ${action}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForPermissionsCatalogToVisible() {
|
|
||||||
await expect(this.permissionsCatalog).toBeVisible();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -14,12 +14,8 @@ export class AdminCreateUserPage extends AuthenticatedPage {
|
|||||||
this.roleInput = page.getByTestId('role.id-autocomplete');
|
this.roleInput = page.getByTestId('role.id-autocomplete');
|
||||||
this.createButton = page.getByTestId('create-button');
|
this.createButton = page.getByTestId('create-button');
|
||||||
this.pageTitle = page.getByTestId('create-user-title');
|
this.pageTitle = page.getByTestId('create-user-title');
|
||||||
this.invitationEmailInfoAlert = page.getByTestId(
|
this.invitationEmailInfoAlert = page.getByTestId('invitation-email-info-alert');
|
||||||
'invitation-email-info-alert'
|
this.acceptInvitationLink = page.getByTestId('invitation-email-info-alert').getByRole('link');
|
||||||
);
|
|
||||||
this.acceptInvitationLink = page
|
|
||||||
.getByTestId('invitation-email-info-alert')
|
|
||||||
.getByRole('link');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
seed(seed) {
|
seed(seed) {
|
||||||
|
@@ -95,6 +95,7 @@ export class AdminUsersPage extends AuthenticatedPage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const rowLocator = await this.getUserRowByEmail(email);
|
const rowLocator = await this.getUserRowByEmail(email);
|
||||||
|
console.log('rowLocator.count', email, await rowLocator.count());
|
||||||
if ((await rowLocator.count()) === 1) {
|
if ((await rowLocator.count()) === 1) {
|
||||||
return rowLocator;
|
return rowLocator;
|
||||||
}
|
}
|
||||||
|
@@ -51,20 +51,10 @@ export class BasePage {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async closeSnackbar() {
|
|
||||||
await this.snackbar.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
async closeSnackbarAndWaitUntilDetached() {
|
|
||||||
const snackbar = await this.snackbar;
|
|
||||||
await snackbar.click();
|
|
||||||
await snackbar.waitFor({ state: 'detached' });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes all snackbars, should be replaced later
|
* Closes all snackbars, should be replaced later
|
||||||
*/
|
*/
|
||||||
async closeAllSnackbars() {
|
async closeSnackbar() {
|
||||||
const snackbars = await this.snackbar.all();
|
const snackbars = await this.snackbar.all();
|
||||||
for (const snackbar of snackbars) {
|
for (const snackbar of snackbars) {
|
||||||
await snackbar.click();
|
await snackbar.click();
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
const { expect } = require('../fixtures/index');
|
|
||||||
|
|
||||||
export const getToken = async (apiRequest) => {
|
|
||||||
const tokenResponse = await apiRequest.post(
|
|
||||||
`${process.env.BACKEND_APP_URL}/api/v1/access-tokens`,
|
|
||||||
{
|
|
||||||
data: {
|
|
||||||
email: process.env.LOGIN_EMAIL,
|
|
||||||
password: process.env.LOGIN_PASSWORD,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await expect(tokenResponse.status()).toBe(200);
|
|
||||||
|
|
||||||
return await tokenResponse.json();
|
|
||||||
};
|
|
@@ -1,69 +0,0 @@
|
|||||||
const { expect } = require('../fixtures/index');
|
|
||||||
|
|
||||||
export const createFlow = async (request, token) => {
|
|
||||||
const response = await request.post(
|
|
||||||
`${process.env.BACKEND_APP_URL}/api/v1/flows`,
|
|
||||||
{ headers: { Authorization: token } }
|
|
||||||
);
|
|
||||||
await expect(response.status()).toBe(201);
|
|
||||||
return await response.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getFlow = async (request, token, flowId) => {
|
|
||||||
const response = await request.get(
|
|
||||||
`${process.env.BACKEND_APP_URL}/api/v1/flows/${flowId}`,
|
|
||||||
{ headers: { Authorization: token } }
|
|
||||||
);
|
|
||||||
await expect(response.status()).toBe(200);
|
|
||||||
return await response.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateFlowName = async (request, token, flowId) => {
|
|
||||||
const updateFlowNameResponse = await request.patch(
|
|
||||||
`${process.env.BACKEND_APP_URL}/api/v1/flows/${flowId}`,
|
|
||||||
{
|
|
||||||
headers: { Authorization: token },
|
|
||||||
data: { name: flowId },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await expect(updateFlowNameResponse.status()).toBe(200);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateFlowStep = async (request, token, stepId, requestBody) => {
|
|
||||||
const updateTriggerStepResponse = await request.patch(
|
|
||||||
`${process.env.BACKEND_APP_URL}/api/v1/steps/${stepId}`,
|
|
||||||
{
|
|
||||||
headers: { Authorization: token },
|
|
||||||
data: requestBody,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await expect(updateTriggerStepResponse.status()).toBe(200);
|
|
||||||
return await updateTriggerStepResponse.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const testStep = async (request, token, stepId) => {
|
|
||||||
const testTriggerStepResponse = await request.post(
|
|
||||||
`${process.env.BACKEND_APP_URL}/api/v1/steps/${stepId}/test`,
|
|
||||||
{
|
|
||||||
headers: { Authorization: token },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await expect(testTriggerStepResponse.status()).toBe(200);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const publishFlow = async (request, token, flowId) => {
|
|
||||||
const publishFlowResponse = await request.patch(
|
|
||||||
`${process.env.BACKEND_APP_URL}/api/v1/flows/${flowId}/status`,
|
|
||||||
{
|
|
||||||
headers: { Authorization: token },
|
|
||||||
data: { active: true },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await expect(publishFlowResponse.status()).toBe(200);
|
|
||||||
return publishFlowResponse.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const triggerFlow = async (request, url) => {
|
|
||||||
const triggerFlowResponse = await request.get(url);
|
|
||||||
await expect(triggerFlowResponse.status()).toBe(204);
|
|
||||||
};
|
|
@@ -1,24 +0,0 @@
|
|||||||
const { expect } = require('../fixtures/index');
|
|
||||||
|
|
||||||
export const addUser = async (apiRequest, token, request) => {
|
|
||||||
const addUserResponse = await apiRequest.post(
|
|
||||||
`${process.env.BACKEND_APP_URL}/api/v1/admin/users`,
|
|
||||||
{
|
|
||||||
headers: { Authorization: token },
|
|
||||||
data: request,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await expect(addUserResponse.status()).toBe(201);
|
|
||||||
|
|
||||||
return await addUserResponse.json();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const acceptInvitation = async (apiRequest, request) => {
|
|
||||||
const acceptInvitationResponse = await apiRequest.post(
|
|
||||||
`${process.env.BACKEND_APP_URL}/api/v1/users/invitation`,
|
|
||||||
{
|
|
||||||
data: request,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await expect(acceptInvitationResponse.status()).toBe(204);
|
|
||||||
};
|
|
@@ -1,5 +1,3 @@
|
|||||||
import { knexSnakeCaseMappers } from 'objection';
|
|
||||||
|
|
||||||
const fileExtension = 'js';
|
const fileExtension = 'js';
|
||||||
|
|
||||||
const knexConfig = {
|
const knexConfig = {
|
||||||
@@ -9,7 +7,7 @@ const knexConfig = {
|
|||||||
user: process.env.POSTGRES_USERNAME,
|
user: process.env.POSTGRES_USERNAME,
|
||||||
port: process.env.POSTGRES_PORT,
|
port: process.env.POSTGRES_PORT,
|
||||||
password: process.env.POSTGRES_PASSWORD,
|
password: process.env.POSTGRES_PASSWORD,
|
||||||
database: process.env.POSTGRES_DATABASE,
|
database: process.env.POSTGRES_DATABASE
|
||||||
},
|
},
|
||||||
searchPath: ['public'],
|
searchPath: ['public'],
|
||||||
pool: { min: 0, max: 20 },
|
pool: { min: 0, max: 20 },
|
||||||
|
@@ -26,8 +26,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^8.2.0",
|
"@faker-js/faker": "^8.2.0",
|
||||||
"@playwright/test": "1.49.0",
|
"@playwright/test": "^1.45.1"
|
||||||
"objection": "^3.1.5"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
|
@@ -15,9 +15,9 @@ module.exports = defineConfig({
|
|||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
retries: process.env.CI ? 1 : 0,
|
retries: 0,
|
||||||
/* Opt out of parallel tests on CI. */
|
/* Opt out of parallel tests on CI. */
|
||||||
workers: undefined,
|
workers: process.env.CI ? 1 : undefined,
|
||||||
/* Timeout threshold for each test */
|
/* Timeout threshold for each test */
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
@@ -30,7 +30,7 @@ module.exports = defineConfig({
|
|||||||
baseURL: process.env.BASE_URL || 'http://localhost:3001',
|
baseURL: process.env.BASE_URL || 'http://localhost:3001',
|
||||||
|
|
||||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
trace: 'on-first-retry',
|
trace: 'retain-on-failure',
|
||||||
testIdAttribute: 'data-test',
|
testIdAttribute: 'data-test',
|
||||||
viewport: { width: 1280, height: 720 },
|
viewport: { width: 1280, height: 720 },
|
||||||
},
|
},
|
||||||
|
@@ -5,18 +5,16 @@ test.describe('Admin Applications', () => {
|
|||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
const deleteAppAuthClients = {
|
const deleteAppAuthClients = {
|
||||||
text: 'DELETE FROM app_auth_clients WHERE app_key in ($1, $2, $3, $4, $5)',
|
text: 'DELETE FROM app_auth_clients WHERE app_key in ($1, $2, $3, $4, $5)',
|
||||||
values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit'],
|
values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit']
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteAppConfigs = {
|
const deleteAppConfigs = {
|
||||||
text: 'DELETE FROM app_configs WHERE key in ($1, $2, $3, $4, $5)',
|
text: 'DELETE FROM app_configs WHERE key in ($1, $2, $3, $4, $5)',
|
||||||
values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit'],
|
values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit']
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const deleteAppAuthClientsResult = await pgPool.query(
|
const deleteAppAuthClientsResult = await pgPool.query(deleteAppAuthClients);
|
||||||
deleteAppAuthClients
|
|
||||||
);
|
|
||||||
expect(deleteAppAuthClientsResult.command).toBe('DELETE');
|
expect(deleteAppAuthClientsResult.command).toBe('DELETE');
|
||||||
const deleteAppConfigsResult = await pgPool.query(deleteAppConfigs);
|
const deleteAppConfigsResult = await pgPool.query(deleteAppConfigs);
|
||||||
expect(deleteAppConfigsResult.command).toBe('DELETE');
|
expect(deleteAppConfigsResult.command).toBe('DELETE');
|
||||||
@@ -30,11 +28,10 @@ test.describe('Admin Applications', () => {
|
|||||||
await adminApplicationsPage.navigateTo();
|
await adminApplicationsPage.navigateTo();
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO skip until https://github.com/automatisch/automatisch/pull/2244
|
test('Admin should be able to toggle Application settings', async ({
|
||||||
test.skip('Admin should be able to toggle Application settings', async ({
|
|
||||||
adminApplicationsPage,
|
adminApplicationsPage,
|
||||||
adminApplicationSettingsPage,
|
adminApplicationSettingsPage,
|
||||||
page,
|
page
|
||||||
}) => {
|
}) => {
|
||||||
await adminApplicationsPage.openApplication('Carbone');
|
await adminApplicationsPage.openApplication('Carbone');
|
||||||
await expect(page.url()).toContain('/admin-settings/apps/carbone/settings');
|
await expect(page.url()).toContain('/admin-settings/apps/carbone/settings');
|
||||||
@@ -60,7 +57,7 @@ test.describe('Admin Applications', () => {
|
|||||||
adminApplicationsPage,
|
adminApplicationsPage,
|
||||||
adminApplicationSettingsPage,
|
adminApplicationSettingsPage,
|
||||||
flowEditorPage,
|
flowEditorPage,
|
||||||
page,
|
page
|
||||||
}) => {
|
}) => {
|
||||||
await adminApplicationsPage.openApplication('Spotify');
|
await adminApplicationsPage.openApplication('Spotify');
|
||||||
await expect(page.url()).toContain('/admin-settings/apps/spotify/settings');
|
await expect(page.url()).toContain('/admin-settings/apps/spotify/settings');
|
||||||
@@ -78,15 +75,11 @@ test.describe('Admin Applications', () => {
|
|||||||
const triggerStep = flowEditorPage.flowStep.last();
|
const triggerStep = flowEditorPage.flowStep.last();
|
||||||
await triggerStep.click();
|
await triggerStep.click();
|
||||||
|
|
||||||
await flowEditorPage.chooseAppAndEvent('Spotify', 'Create Playlist');
|
await flowEditorPage.chooseAppAndEvent("Spotify", "Create Playlist");
|
||||||
await flowEditorPage.connectionAutocomplete.click();
|
await flowEditorPage.connectionAutocomplete.click();
|
||||||
|
|
||||||
const newConnectionOption = page
|
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' });
|
||||||
.getByRole('option')
|
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' });
|
||||||
.filter({ hasText: 'Add new connection' });
|
|
||||||
const newSharedConnectionOption = page
|
|
||||||
.getByRole('option')
|
|
||||||
.filter({ hasText: 'Add new shared connection' });
|
|
||||||
|
|
||||||
await expect(newConnectionOption).toBeEnabled();
|
await expect(newConnectionOption).toBeEnabled();
|
||||||
await expect(newConnectionOption).toHaveCount(1);
|
await expect(newConnectionOption).toHaveCount(1);
|
||||||
@@ -98,7 +91,7 @@ test.describe('Admin Applications', () => {
|
|||||||
adminApplicationSettingsPage,
|
adminApplicationSettingsPage,
|
||||||
adminApplicationAuthClientsPage,
|
adminApplicationAuthClientsPage,
|
||||||
flowEditorPage,
|
flowEditorPage,
|
||||||
page,
|
page
|
||||||
}) => {
|
}) => {
|
||||||
await adminApplicationsPage.openApplication('Reddit');
|
await adminApplicationsPage.openApplication('Reddit');
|
||||||
await expect(page.url()).toContain('/admin-settings/apps/reddit/settings');
|
await expect(page.url()).toContain('/admin-settings/apps/reddit/settings');
|
||||||
@@ -108,21 +101,13 @@ test.describe('Admin Applications', () => {
|
|||||||
|
|
||||||
await adminApplicationAuthClientsPage.openAuthClientsTab();
|
await adminApplicationAuthClientsPage.openAuthClientsTab();
|
||||||
await adminApplicationAuthClientsPage.openFirstAuthClientCreateForm();
|
await adminApplicationAuthClientsPage.openFirstAuthClientCreateForm();
|
||||||
const authClientForm = page.getByTestId('auth-client-form');
|
const authClientForm = page.getByTestId("auth-client-form");
|
||||||
await authClientForm.locator(page.getByTestId('switch')).check();
|
await authClientForm.locator(page.getByTestId('switch')).check();
|
||||||
await authClientForm
|
await authClientForm.locator(page.locator('[name="name"]')).fill('redditAuthClient');
|
||||||
.locator(page.locator('[name="name"]'))
|
await authClientForm.locator(page.locator('[name="clientId"]')).fill('redditClientId');
|
||||||
.fill('redditAuthClient');
|
await authClientForm.locator(page.locator('[name="clientSecret"]')).fill('redditClientSecret');
|
||||||
await authClientForm
|
|
||||||
.locator(page.locator('[name="clientId"]'))
|
|
||||||
.fill('redditClientId');
|
|
||||||
await authClientForm
|
|
||||||
.locator(page.locator('[name="clientSecret"]'))
|
|
||||||
.fill('redditClientSecret');
|
|
||||||
await adminApplicationAuthClientsPage.submitAuthClientForm();
|
await adminApplicationAuthClientsPage.submitAuthClientForm();
|
||||||
await adminApplicationAuthClientsPage.authClientShouldBeVisible(
|
await adminApplicationAuthClientsPage.authClientShouldBeVisible('redditAuthClient');
|
||||||
'redditAuthClient'
|
|
||||||
);
|
|
||||||
|
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
await page.getByTestId('create-flow-button').click();
|
await page.getByTestId('create-flow-button').click();
|
||||||
@@ -134,15 +119,11 @@ test.describe('Admin Applications', () => {
|
|||||||
const triggerStep = flowEditorPage.flowStep.last();
|
const triggerStep = flowEditorPage.flowStep.last();
|
||||||
await triggerStep.click();
|
await triggerStep.click();
|
||||||
|
|
||||||
await flowEditorPage.chooseAppAndEvent('Reddit', 'Create link post');
|
await flowEditorPage.chooseAppAndEvent("Reddit", "Create link post");
|
||||||
await flowEditorPage.connectionAutocomplete.click();
|
await flowEditorPage.connectionAutocomplete.click();
|
||||||
|
|
||||||
const newConnectionOption = page
|
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' });
|
||||||
.getByRole('option')
|
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' });
|
||||||
.filter({ hasText: 'Add new connection' });
|
|
||||||
const newSharedConnectionOption = page
|
|
||||||
.getByRole('option')
|
|
||||||
.filter({ hasText: 'Add new shared connection' });
|
|
||||||
|
|
||||||
await expect(newConnectionOption).toHaveCount(0);
|
await expect(newConnectionOption).toHaveCount(0);
|
||||||
await expect(newSharedConnectionOption).toBeEnabled();
|
await expect(newSharedConnectionOption).toBeEnabled();
|
||||||
@@ -153,7 +134,7 @@ test.describe('Admin Applications', () => {
|
|||||||
adminApplicationsPage,
|
adminApplicationsPage,
|
||||||
adminApplicationSettingsPage,
|
adminApplicationSettingsPage,
|
||||||
flowEditorPage,
|
flowEditorPage,
|
||||||
page,
|
page
|
||||||
}) => {
|
}) => {
|
||||||
await adminApplicationsPage.openApplication('DeepL');
|
await adminApplicationsPage.openApplication('DeepL');
|
||||||
await expect(page.url()).toContain('/admin-settings/apps/deepl/settings');
|
await expect(page.url()).toContain('/admin-settings/apps/deepl/settings');
|
||||||
@@ -171,18 +152,12 @@ test.describe('Admin Applications', () => {
|
|||||||
const triggerStep = flowEditorPage.flowStep.last();
|
const triggerStep = flowEditorPage.flowStep.last();
|
||||||
await triggerStep.click();
|
await triggerStep.click();
|
||||||
|
|
||||||
await flowEditorPage.chooseAppAndEvent('DeepL', 'Translate text');
|
await flowEditorPage.chooseAppAndEvent("DeepL", "Translate text");
|
||||||
await flowEditorPage.connectionAutocomplete.click();
|
await flowEditorPage.connectionAutocomplete.click();
|
||||||
|
|
||||||
const newConnectionOption = page
|
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' });
|
||||||
.getByRole('option')
|
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' });
|
||||||
.filter({ hasText: 'Add new connection' });
|
const noConnectionsOption = page.locator('.MuiAutocomplete-noOptions').filter({ hasText: 'No options' });
|
||||||
const newSharedConnectionOption = page
|
|
||||||
.getByRole('option')
|
|
||||||
.filter({ hasText: 'Add new shared connection' });
|
|
||||||
const noConnectionsOption = page
|
|
||||||
.locator('.MuiAutocomplete-noOptions')
|
|
||||||
.filter({ hasText: 'No options' });
|
|
||||||
|
|
||||||
await expect(noConnectionsOption).toHaveCount(1);
|
await expect(noConnectionsOption).toHaveCount(1);
|
||||||
await expect(newConnectionOption).toHaveCount(0);
|
await expect(newConnectionOption).toHaveCount(0);
|
||||||
@@ -193,11 +168,11 @@ test.describe('Admin Applications', () => {
|
|||||||
adminApplicationsPage,
|
adminApplicationsPage,
|
||||||
adminApplicationSettingsPage,
|
adminApplicationSettingsPage,
|
||||||
flowEditorPage,
|
flowEditorPage,
|
||||||
page,
|
page
|
||||||
}) => {
|
}) => {
|
||||||
const queryUser = {
|
const queryUser = {
|
||||||
text: 'SELECT * FROM users WHERE email = $1',
|
text: 'SELECT * FROM users WHERE email = $1',
|
||||||
values: [process.env.LOGIN_EMAIL],
|
values: [process.env.LOGIN_EMAIL]
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -208,16 +183,14 @@ test.describe('Admin Applications', () => {
|
|||||||
text: 'INSERT INTO connections (key, data, user_id, verified, draft) VALUES ($1, $2, $3, $4, $5)',
|
text: 'INSERT INTO connections (key, data, user_id, verified, draft) VALUES ($1, $2, $3, $4, $5)',
|
||||||
values: [
|
values: [
|
||||||
'mailchimp',
|
'mailchimp',
|
||||||
'U2FsdGVkX1+cAtdHwLiuRL4DaK/T1aljeeKyPMmtWK0AmAIsKhYwQiuyQCYJO3mdZ31z73hqF2Y+yj2Kn2/IIpLRqCxB2sC0rCDCZyolzOZ290YcBXSzYRzRUxhoOcZEtwYDKsy8AHygKK/tkj9uv9k6wOe1LjipNik4VmRhKjEYizzjLrJpbeU1oY+qW0GBpPYomFTeNf+MejSSmsUYyYJ8+E/4GeEfaonvsTSwMT7AId98Lck6Vy4wrfgpm7sZZ8xU15/HqXZNc8UCo2iTdw45xj/Oov9+brX4WUASFPG8aYrK8dl/EdaOvr89P8uIofbSNZ25GjJvVF5ymarrPkTZ7djjJXchzpwBY+7GTJfs3funR/vIk0Hq95jgOFFP1liZyqTXSa49ojG3hzojRQ==',
|
"U2FsdGVkX1+cAtdHwLiuRL4DaK/T1aljeeKyPMmtWK0AmAIsKhYwQiuyQCYJO3mdZ31z73hqF2Y+yj2Kn2/IIpLRqCxB2sC0rCDCZyolzOZ290YcBXSzYRzRUxhoOcZEtwYDKsy8AHygKK/tkj9uv9k6wOe1LjipNik4VmRhKjEYizzjLrJpbeU1oY+qW0GBpPYomFTeNf+MejSSmsUYyYJ8+E/4GeEfaonvsTSwMT7AId98Lck6Vy4wrfgpm7sZZ8xU15/HqXZNc8UCo2iTdw45xj/Oov9+brX4WUASFPG8aYrK8dl/EdaOvr89P8uIofbSNZ25GjJvVF5ymarrPkTZ7djjJXchzpwBY+7GTJfs3funR/vIk0Hq95jgOFFP1liZyqTXSa49ojG3hzojRQ==",
|
||||||
queryUserResult.rows[0].id,
|
queryUserResult.rows[0].id,
|
||||||
'true',
|
'true',
|
||||||
'false',
|
'false'
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const createMailchimpConnectionResult = await pgPool.query(
|
const createMailchimpConnectionResult = await pgPool.query(createMailchimpConnection);
|
||||||
createMailchimpConnection
|
|
||||||
);
|
|
||||||
expect(createMailchimpConnectionResult.rowCount).toBe(1);
|
expect(createMailchimpConnectionResult.rowCount).toBe(1);
|
||||||
expect(createMailchimpConnectionResult.command).toBe('INSERT');
|
expect(createMailchimpConnectionResult.command).toBe('INSERT');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -226,9 +199,7 @@ test.describe('Admin Applications', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await adminApplicationsPage.openApplication('Mailchimp');
|
await adminApplicationsPage.openApplication('Mailchimp');
|
||||||
await expect(page.url()).toContain(
|
await expect(page.url()).toContain('/admin-settings/apps/mailchimp/settings');
|
||||||
'/admin-settings/apps/mailchimp/settings'
|
|
||||||
);
|
|
||||||
|
|
||||||
await adminApplicationSettingsPage.disallowConnections();
|
await adminApplicationSettingsPage.disallowConnections();
|
||||||
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
|
||||||
@@ -243,22 +214,14 @@ test.describe('Admin Applications', () => {
|
|||||||
const triggerStep = flowEditorPage.flowStep.last();
|
const triggerStep = flowEditorPage.flowStep.last();
|
||||||
await triggerStep.click();
|
await triggerStep.click();
|
||||||
|
|
||||||
await flowEditorPage.chooseAppAndEvent('Mailchimp', 'Create campaign');
|
await flowEditorPage.chooseAppAndEvent("Mailchimp", "Create campaign");
|
||||||
await flowEditorPage.connectionAutocomplete.click();
|
await flowEditorPage.connectionAutocomplete.click();
|
||||||
await expect(page.getByRole('option').first()).toHaveText('Unnamed');
|
await expect(page.getByRole('option').first()).toHaveText('Unnamed');
|
||||||
|
|
||||||
const existingConnection = page
|
const existingConnection = page.getByRole('option').filter({ hasText: 'Unnamed' });
|
||||||
.getByRole('option')
|
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' });
|
||||||
.filter({ hasText: 'Unnamed' });
|
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' });
|
||||||
const newConnectionOption = page
|
const noConnectionsOption = page.locator('.MuiAutocomplete-noOptions').filter({ hasText: 'No options' });
|
||||||
.getByRole('option')
|
|
||||||
.filter({ hasText: 'Add new connection' });
|
|
||||||
const newSharedConnectionOption = page
|
|
||||||
.getByRole('option')
|
|
||||||
.filter({ hasText: 'Add new shared connection' });
|
|
||||||
const noConnectionsOption = page
|
|
||||||
.locator('.MuiAutocomplete-noOptions')
|
|
||||||
.filter({ hasText: 'No options' });
|
|
||||||
|
|
||||||
await expect(await existingConnection.count()).toBeGreaterThan(0);
|
await expect(await existingConnection.count()).toBeGreaterThan(0);
|
||||||
await expect(noConnectionsOption).toHaveCount(0);
|
await expect(noConnectionsOption).toHaveCount(0);
|
||||||
|
@@ -22,18 +22,22 @@ test.describe('Role management page', () => {
|
|||||||
await adminRolesPage.navigateTo();
|
await adminRolesPage.navigateTo();
|
||||||
await adminRolesPage.createRoleButton.click();
|
await adminRolesPage.createRoleButton.click();
|
||||||
await adminCreateRolePage.isMounted();
|
await adminCreateRolePage.isMounted();
|
||||||
await adminCreateRolePage.waitForPermissionsCatalogToVisible();
|
|
||||||
await adminCreateRolePage.nameInput.fill('Create Edit Test');
|
await adminCreateRolePage.nameInput.fill('Create Edit Test');
|
||||||
await adminCreateRolePage.descriptionInput.fill('Test description');
|
await adminCreateRolePage.descriptionInput.fill('Test description');
|
||||||
await adminCreateRolePage.createButton.click();
|
await adminCreateRolePage.createButton.click();
|
||||||
|
await adminCreateRolePage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminCreateRolePage.getSnackbarData(
|
const snackbar = await adminCreateRolePage.getSnackbarData(
|
||||||
'snackbar-create-role-success'
|
'snackbar-create-role-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminCreateRolePage.closeSnackbar();
|
||||||
});
|
});
|
||||||
|
|
||||||
let roleRow =
|
let roleRow = await test.step(
|
||||||
await test.step('Make sure role data is correct', async () => {
|
'Make sure role data is correct',
|
||||||
|
async () => {
|
||||||
const roleRow = await adminRolesPage.getRoleRowByName(
|
const roleRow = await adminRolesPage.getRoleRowByName(
|
||||||
'Create Edit Test'
|
'Create Edit Test'
|
||||||
);
|
);
|
||||||
@@ -44,7 +48,8 @@ test.describe('Role management page', () => {
|
|||||||
await expect(roleData.canEdit).toBe(true);
|
await expect(roleData.canEdit).toBe(true);
|
||||||
await expect(roleData.canDelete).toBe(true);
|
await expect(roleData.canDelete).toBe(true);
|
||||||
return roleRow;
|
return roleRow;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await test.step('Edit the role', async () => {
|
await test.step('Edit the role', async () => {
|
||||||
await adminRolesPage.clickEditRole(roleRow);
|
await adminRolesPage.clickEditRole(roleRow);
|
||||||
@@ -52,14 +57,19 @@ test.describe('Role management page', () => {
|
|||||||
await adminEditRolePage.nameInput.fill('Create Update Test');
|
await adminEditRolePage.nameInput.fill('Create Update Test');
|
||||||
await adminEditRolePage.descriptionInput.fill('Update test description');
|
await adminEditRolePage.descriptionInput.fill('Update test description');
|
||||||
await adminEditRolePage.updateButton.click();
|
await adminEditRolePage.updateButton.click();
|
||||||
|
await adminEditRolePage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminEditRolePage.getSnackbarData(
|
const snackbar = await adminEditRolePage.getSnackbarData(
|
||||||
'snackbar-edit-role-success'
|
'snackbar-edit-role-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminEditRolePage.closeSnackbar();
|
||||||
});
|
});
|
||||||
|
|
||||||
roleRow =
|
roleRow = await test.step(
|
||||||
await test.step('Make sure changes reflected on roles page', async () => {
|
'Make sure changes reflected on roles page',
|
||||||
|
async () => {
|
||||||
await adminRolesPage.isMounted();
|
await adminRolesPage.isMounted();
|
||||||
const roleRow = await adminRolesPage.getRoleRowByName(
|
const roleRow = await adminRolesPage.getRoleRowByName(
|
||||||
'Create Update Test'
|
'Create Update Test'
|
||||||
@@ -71,7 +81,8 @@ test.describe('Role management page', () => {
|
|||||||
await expect(roleData.canEdit).toBe(true);
|
await expect(roleData.canEdit).toBe(true);
|
||||||
await expect(roleData.canDelete).toBe(true);
|
await expect(roleData.canDelete).toBe(true);
|
||||||
return roleRow;
|
return roleRow;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await test.step('Delete the role', async () => {
|
await test.step('Delete the role', async () => {
|
||||||
await adminRolesPage.clickDeleteRole(roleRow);
|
await adminRolesPage.clickDeleteRole(roleRow);
|
||||||
@@ -80,10 +91,14 @@ test.describe('Role management page', () => {
|
|||||||
state: 'attached',
|
state: 'attached',
|
||||||
});
|
});
|
||||||
await deleteModal.deleteButton.click();
|
await deleteModal.deleteButton.click();
|
||||||
|
await adminRolesPage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminRolesPage.getSnackbarData(
|
const snackbar = await adminRolesPage.getSnackbarData(
|
||||||
'snackbar-delete-role-success'
|
'snackbar-delete-role-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminRolesPage.closeSnackbar();
|
||||||
await deleteModal.modal.waitFor({
|
await deleteModal.modal.waitFor({
|
||||||
state: 'detached',
|
state: 'detached',
|
||||||
});
|
});
|
||||||
@@ -158,45 +173,60 @@ test.describe('Role management page', () => {
|
|||||||
await test.step('Create a new role', async () => {
|
await test.step('Create a new role', async () => {
|
||||||
await adminRolesPage.createRoleButton.click();
|
await adminRolesPage.createRoleButton.click();
|
||||||
await adminCreateRolePage.isMounted();
|
await adminCreateRolePage.isMounted();
|
||||||
await adminCreateRolePage.waitForPermissionsCatalogToVisible();
|
|
||||||
await adminCreateRolePage.nameInput.fill('Delete Role');
|
await adminCreateRolePage.nameInput.fill('Delete Role');
|
||||||
await adminCreateRolePage.createButton.click();
|
await adminCreateRolePage.createButton.click();
|
||||||
|
await adminCreateRolePage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminCreateRolePage.getSnackbarData(
|
const snackbar = await adminCreateRolePage.getSnackbarData(
|
||||||
'snackbar-create-role-success'
|
'snackbar-create-role-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminCreateRolePage.closeSnackbar();
|
||||||
});
|
});
|
||||||
await test.step('Create a new user with the "Delete Role" role', async () => {
|
await test.step(
|
||||||
await adminUsersPage.navigateTo();
|
'Create a new user with the "Delete Role" role',
|
||||||
await adminUsersPage.createUserButton.click();
|
async () => {
|
||||||
await adminCreateUserPage.fullNameInput.fill('User Role Test');
|
await adminUsersPage.navigateTo();
|
||||||
await adminCreateUserPage.emailInput.fill(
|
await adminUsersPage.createUserButton.click();
|
||||||
'user-role-test@automatisch.io'
|
await adminCreateUserPage.fullNameInput.fill('User Role Test');
|
||||||
);
|
await adminCreateUserPage.emailInput.fill(
|
||||||
await adminCreateUserPage.roleInput.click();
|
'user-role-test@automatisch.io'
|
||||||
await adminCreateUserPage.page
|
);
|
||||||
.getByRole('option', { name: 'Delete Role', exact: true })
|
await adminCreateUserPage.roleInput.click();
|
||||||
.click();
|
await adminCreateUserPage.page
|
||||||
await adminCreateUserPage.createButton.click();
|
.getByRole('option', { name: 'Delete Role', exact: true })
|
||||||
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
|
.click();
|
||||||
state: 'attached',
|
await adminCreateUserPage.createButton.click();
|
||||||
});
|
await adminCreateUserPage.snackbar.waitFor({
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
state: 'attached',
|
||||||
'snackbar-create-user-success'
|
});
|
||||||
);
|
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
|
||||||
await expect(snackbar.variant).toBe('success');
|
state: 'attached',
|
||||||
});
|
});
|
||||||
await test.step('Try to delete "Delete Role" role when new user has it', async () => {
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
await adminRolesPage.navigateTo();
|
'snackbar-create-user-success'
|
||||||
const row = await adminRolesPage.getRoleRowByName('Delete Role');
|
);
|
||||||
const modal = await adminRolesPage.clickDeleteRole(row);
|
await expect(snackbar.variant).toBe('success');
|
||||||
await modal.deleteButton.click();
|
await adminUsersPage.closeSnackbar();
|
||||||
const snackbar = await adminRolesPage.getSnackbarData(
|
}
|
||||||
'snackbar-delete-role-error'
|
);
|
||||||
);
|
await test.step(
|
||||||
await expect(snackbar.variant).toBe('error');
|
'Try to delete "Delete Role" role when new user has it',
|
||||||
await modal.close();
|
async () => {
|
||||||
});
|
await adminRolesPage.navigateTo();
|
||||||
|
const row = await adminRolesPage.getRoleRowByName('Delete Role');
|
||||||
|
const modal = await adminRolesPage.clickDeleteRole(row);
|
||||||
|
await modal.deleteButton.click();
|
||||||
|
await adminRolesPage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
|
const snackbar = await adminRolesPage.getSnackbarData('snackbar-delete-role-error');
|
||||||
|
await expect(snackbar.variant).toBe('error');
|
||||||
|
await adminRolesPage.closeSnackbar();
|
||||||
|
await modal.close();
|
||||||
|
}
|
||||||
|
);
|
||||||
await test.step('Change the role the user has', async () => {
|
await test.step('Change the role the user has', async () => {
|
||||||
await adminUsersPage.navigateTo();
|
await adminUsersPage.navigateTo();
|
||||||
await adminUsersPage.usersLoader.waitFor({
|
await adminUsersPage.usersLoader.waitFor({
|
||||||
@@ -211,10 +241,14 @@ test.describe('Role management page', () => {
|
|||||||
.getByRole('option', { name: 'Admin' })
|
.getByRole('option', { name: 'Admin' })
|
||||||
.click();
|
.click();
|
||||||
await adminEditUserPage.updateButton.click();
|
await adminEditUserPage.updateButton.click();
|
||||||
|
await adminEditUserPage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminEditUserPage.getSnackbarData(
|
const snackbar = await adminEditUserPage.getSnackbarData(
|
||||||
'snackbar-edit-user-success'
|
'snackbar-edit-user-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminEditUserPage.closeSnackbar();
|
||||||
});
|
});
|
||||||
await test.step('Delete the original role', async () => {
|
await test.step('Delete the original role', async () => {
|
||||||
await adminRolesPage.navigateTo();
|
await adminRolesPage.navigateTo();
|
||||||
@@ -222,10 +256,14 @@ test.describe('Role management page', () => {
|
|||||||
const modal = await adminRolesPage.clickDeleteRole(row);
|
const modal = await adminRolesPage.clickDeleteRole(row);
|
||||||
await expect(modal.modal).toBeVisible();
|
await expect(modal.modal).toBeVisible();
|
||||||
await modal.deleteButton.click();
|
await modal.deleteButton.click();
|
||||||
|
await adminRolesPage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminRolesPage.getSnackbarData(
|
const snackbar = await adminRolesPage.getSnackbarData(
|
||||||
'snackbar-delete-role-success'
|
'snackbar-delete-role-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminRolesPage.closeSnackbar();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -239,13 +277,16 @@ test.describe('Role management page', () => {
|
|||||||
await test.step('Create a new role', async () => {
|
await test.step('Create a new role', async () => {
|
||||||
await adminRolesPage.createRoleButton.click();
|
await adminRolesPage.createRoleButton.click();
|
||||||
await adminCreateRolePage.isMounted();
|
await adminCreateRolePage.isMounted();
|
||||||
await adminCreateRolePage.waitForPermissionsCatalogToVisible();
|
|
||||||
await adminCreateRolePage.nameInput.fill('Cannot Delete Role');
|
await adminCreateRolePage.nameInput.fill('Cannot Delete Role');
|
||||||
await adminCreateRolePage.createButton.click();
|
await adminCreateRolePage.createButton.click();
|
||||||
|
await adminCreateRolePage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminCreateRolePage.getSnackbarData(
|
const snackbar = await adminCreateRolePage.getSnackbarData(
|
||||||
'snackbar-create-role-success'
|
'snackbar-create-role-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminCreateRolePage.closeSnackbar();
|
||||||
});
|
});
|
||||||
await test.step('Create a new user with this role', async () => {
|
await test.step('Create a new user with this role', async () => {
|
||||||
await adminUsersPage.navigateTo();
|
await adminUsersPage.navigateTo();
|
||||||
@@ -260,6 +301,9 @@ test.describe('Role management page', () => {
|
|||||||
.getByRole('option', { name: 'Cannot Delete Role' })
|
.getByRole('option', { name: 'Cannot Delete Role' })
|
||||||
.click();
|
.click();
|
||||||
await adminCreateUserPage.createButton.click();
|
await adminCreateUserPage.createButton.click();
|
||||||
|
await adminCreateUserPage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
|
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
|
||||||
state: 'attached',
|
state: 'attached',
|
||||||
});
|
});
|
||||||
@@ -267,34 +311,40 @@ test.describe('Role management page', () => {
|
|||||||
'snackbar-create-user-success'
|
'snackbar-create-user-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminCreateUserPage.closeSnackbar();
|
||||||
});
|
});
|
||||||
await test.step('Delete this user', async () => {
|
await test.step('Delete this user', async () => {
|
||||||
await adminUsersPage.navigateTo();
|
await adminUsersPage.navigateTo();
|
||||||
const row = await adminUsersPage.findUserPageWithEmail(
|
const row = await adminUsersPage.findUserPageWithEmail(
|
||||||
'user-delete-role-test@automatisch.io'
|
'user-delete-role-test@automatisch.io'
|
||||||
);
|
);
|
||||||
|
// await test.waitForTimeout(10000);
|
||||||
const modal = await adminUsersPage.clickDeleteUser(row);
|
const modal = await adminUsersPage.clickDeleteUser(row);
|
||||||
await modal.deleteButton.click();
|
await modal.deleteButton.click();
|
||||||
|
await adminUsersPage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
'snackbar-delete-user-success'
|
'snackbar-delete-user-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminUsersPage.closeSnackbar();
|
||||||
});
|
});
|
||||||
await test.step('Try deleting this role', async () => {
|
await test.step('Try deleting this role', async () => {
|
||||||
await adminRolesPage.navigateTo();
|
await adminRolesPage.navigateTo();
|
||||||
const row = await adminRolesPage.getRoleRowByName('Cannot Delete Role');
|
const row = await adminRolesPage.getRoleRowByName('Cannot Delete Role');
|
||||||
const modal = await adminRolesPage.clickDeleteRole(row);
|
const modal = await adminRolesPage.clickDeleteRole(row);
|
||||||
await modal.deleteButton.click();
|
await modal.deleteButton.click();
|
||||||
const snackbar = await adminRolesPage.getSnackbarData(
|
await adminRolesPage.snackbar.waitFor({
|
||||||
'snackbar-delete-role-error'
|
state: 'attached',
|
||||||
);
|
});
|
||||||
await expect(snackbar.variant).toBe('error');
|
|
||||||
/*
|
/*
|
||||||
* TODO: await snackbar - make assertions based on product
|
* TODO: await snackbar - make assertions based on product
|
||||||
* decisions
|
* decisions
|
||||||
const snackbar = await adminRolesPage.getSnackbarData();
|
const snackbar = await adminRolesPage.getSnackbarData();
|
||||||
await expect(snackbar.variant).toBe('...');
|
await expect(snackbar.variant).toBe('...');
|
||||||
*/
|
*/
|
||||||
|
await adminRolesPage.closeSnackbar();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -312,13 +362,16 @@ test('Accessibility of role management page', async ({
|
|||||||
await adminRolesPage.navigateTo();
|
await adminRolesPage.navigateTo();
|
||||||
await adminRolesPage.createRoleButton.click();
|
await adminRolesPage.createRoleButton.click();
|
||||||
await adminCreateRolePage.isMounted();
|
await adminCreateRolePage.isMounted();
|
||||||
await adminCreateRolePage.waitForPermissionsCatalogToVisible();
|
|
||||||
await adminCreateRolePage.nameInput.fill('Basic Test');
|
await adminCreateRolePage.nameInput.fill('Basic Test');
|
||||||
await adminCreateRolePage.createButton.click();
|
await adminCreateRolePage.createButton.click();
|
||||||
|
await adminCreateRolePage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminCreateRolePage.getSnackbarData(
|
const snackbar = await adminCreateRolePage.getSnackbarData(
|
||||||
'snackbar-create-role-success'
|
'snackbar-create-role-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminCreateRolePage.closeSnackbar();
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('Create a new user with the basic role', async () => {
|
await test.step('Create a new user with the basic role', async () => {
|
||||||
@@ -332,6 +385,9 @@ test('Accessibility of role management page', async ({
|
|||||||
.getByRole('option', { name: 'Basic Test' })
|
.getByRole('option', { name: 'Basic Test' })
|
||||||
.click();
|
.click();
|
||||||
await adminCreateUserPage.createButton.click();
|
await adminCreateUserPage.createButton.click();
|
||||||
|
await adminCreateUserPage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
|
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
|
||||||
state: 'attached',
|
state: 'attached',
|
||||||
});
|
});
|
||||||
@@ -339,46 +395,56 @@ test('Accessibility of role management page', async ({
|
|||||||
'snackbar-create-user-success'
|
'snackbar-create-user-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminCreateUserPage.closeSnackbar();
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('Logout and login to the basic role user', async () => {
|
await test.step('Logout and login to the basic role user', async () => {
|
||||||
const acceptInvitationLink = await adminCreateUserPage.acceptInvitationLink;
|
const acceptInvitationLink = await adminCreateUserPage.acceptInvitationLink;
|
||||||
|
console.log(acceptInvitationLink);
|
||||||
const acceptInvitationUrl = await acceptInvitationLink.textContent();
|
const acceptInvitationUrl = await acceptInvitationLink.textContent();
|
||||||
|
console.log(acceptInvitationUrl);
|
||||||
const acceptInvitatonToken = acceptInvitationUrl.split('?token=')[1];
|
const acceptInvitatonToken = acceptInvitationUrl.split('?token=')[1];
|
||||||
|
|
||||||
await page.getByTestId('profile-menu-button').click();
|
await page.getByTestId('profile-menu-button').click();
|
||||||
await page.getByTestId('logout-item').click();
|
await page.getByTestId('logout-item').click();
|
||||||
|
|
||||||
const acceptInvitationPage = new AcceptInvitation(page);
|
const acceptInvitationPage = new AcceptInvitation(page);
|
||||||
|
|
||||||
await acceptInvitationPage.open(acceptInvitatonToken);
|
await acceptInvitationPage.open(acceptInvitatonToken);
|
||||||
|
|
||||||
await acceptInvitationPage.acceptInvitation('sample');
|
await acceptInvitationPage.acceptInvitation('sample');
|
||||||
|
|
||||||
const loginPage = new LoginPage(page);
|
const loginPage = new LoginPage(page);
|
||||||
|
|
||||||
|
// await loginPage.isMounted();
|
||||||
await loginPage.login('basic-role-test@automatisch.io', 'sample');
|
await loginPage.login('basic-role-test@automatisch.io', 'sample');
|
||||||
await expect(loginPage.loginButton).not.toBeVisible();
|
await expect(loginPage.loginButton).not.toBeVisible();
|
||||||
await expect(page).toHaveURL('/flows');
|
await expect(page).toHaveURL('/flows');
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('Navigate to the admin settings page and make sure it is blank', async () => {
|
await test.step(
|
||||||
const pageUrl = new URL(page.url());
|
'Navigate to the admin settings page and make sure it is blank',
|
||||||
const url = `${pageUrl.origin}/admin-settings/users`;
|
async () => {
|
||||||
await page.goto(url);
|
const pageUrl = new URL(page.url());
|
||||||
await page.waitForTimeout(750);
|
const url = `${pageUrl.origin}/admin-settings/users`;
|
||||||
const isUnmounted = await page.evaluate(() => {
|
await page.goto(url);
|
||||||
// eslint-disable-next-line no-undef
|
await page.waitForTimeout(750);
|
||||||
const root = document.querySelector('#root');
|
const isUnmounted = await page.evaluate(() => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
const root = document.querySelector('#root');
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
// We have react query devtools only in dev env.
|
// We have react query devtools only in dev env.
|
||||||
// In production, there is nothing in root.
|
// In production, there is nothing in root.
|
||||||
// That's why `<= 1`.
|
// That's why `<= 1`.
|
||||||
return root.children.length <= 1;
|
return root.children.length <= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
await expect(isUnmounted).toBe(true);
|
await expect(isUnmounted).toBe(true);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await test.step('Log back into the admin account', async () => {
|
await test.step('Log back into the admin account', async () => {
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
@@ -399,10 +465,10 @@ test('Accessibility of role management page', async ({
|
|||||||
await adminEditUserPage.roleInput.click();
|
await adminEditUserPage.roleInput.click();
|
||||||
await adminEditUserPage.page.getByRole('option', { name: 'Admin' }).click();
|
await adminEditUserPage.page.getByRole('option', { name: 'Admin' }).click();
|
||||||
await adminEditUserPage.updateButton.click();
|
await adminEditUserPage.updateButton.click();
|
||||||
const snackbar = await adminEditUserPage.getSnackbarData(
|
await adminEditUserPage.snackbar.waitFor({
|
||||||
'snackbar-edit-user-success'
|
state: 'attached',
|
||||||
);
|
});
|
||||||
await expect(snackbar.variant).toBe('success');
|
await adminEditUserPage.closeSnackbar();
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('Delete the role', async () => {
|
await test.step('Delete the role', async () => {
|
||||||
@@ -414,10 +480,14 @@ test('Accessibility of role management page', async ({
|
|||||||
state: 'attached',
|
state: 'attached',
|
||||||
});
|
});
|
||||||
await deleteModal.deleteButton.click();
|
await deleteModal.deleteButton.click();
|
||||||
|
await adminRolesPage.snackbar.waitFor({
|
||||||
|
state: 'attached',
|
||||||
|
});
|
||||||
const snackbar = await adminRolesPage.getSnackbarData(
|
const snackbar = await adminRolesPage.getSnackbarData(
|
||||||
'snackbar-delete-role-success'
|
'snackbar-delete-role-success'
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminRolesPage.closeSnackbar();
|
||||||
await deleteModal.modal.waitFor({
|
await deleteModal.modal.waitFor({
|
||||||
state: 'detached',
|
state: 'detached',
|
||||||
});
|
});
|
||||||
|
@@ -5,235 +5,281 @@ const { test, expect } = require('../../fixtures/index');
|
|||||||
* otherwise tests will fail since users are only *soft*-deleted
|
* otherwise tests will fail since users are only *soft*-deleted
|
||||||
*/
|
*/
|
||||||
test.describe('User management page', () => {
|
test.describe('User management page', () => {
|
||||||
|
|
||||||
test.beforeEach(async ({ adminUsersPage }) => {
|
test.beforeEach(async ({ adminUsersPage }) => {
|
||||||
await adminUsersPage.navigateTo();
|
await adminUsersPage.navigateTo();
|
||||||
await adminUsersPage.closeAllSnackbars();
|
await adminUsersPage.closeSnackbar();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('User creation and deletion process', async ({
|
test(
|
||||||
adminCreateUserPage,
|
'User creation and deletion process',
|
||||||
adminEditUserPage,
|
async ({ adminCreateUserPage, adminEditUserPage, adminUsersPage }) => {
|
||||||
adminUsersPage,
|
adminCreateUserPage.seed(9000);
|
||||||
}) => {
|
const user = adminCreateUserPage.generateUser();
|
||||||
adminCreateUserPage.seed(9000);
|
await adminUsersPage.usersLoader.waitFor({
|
||||||
const user = adminCreateUserPage.generateUser();
|
state: 'detached' /* Note: state: 'visible' introduces flakiness
|
||||||
await adminUsersPage.usersLoader.waitFor({
|
|
||||||
state: 'detached' /* Note: state: 'visible' introduces flakiness
|
|
||||||
because visibility: hidden is used as part of the state transition in
|
because visibility: hidden is used as part of the state transition in
|
||||||
notistack, see
|
notistack, see
|
||||||
https://github.com/iamhosseindhv/notistack/blob/122f47057eb7ce5a1abfe923316cf8475303e99a/src/transitions/Collapse/Collapse.tsx#L110
|
https://github.com/iamhosseindhv/notistack/blob/122f47057eb7ce5a1abfe923316cf8475303e99a/src/transitions/Collapse/Collapse.tsx#L110
|
||||||
*/,
|
*/
|
||||||
});
|
|
||||||
await test.step('Create a user', async () => {
|
|
||||||
await adminUsersPage.createUserButton.click();
|
|
||||||
await adminCreateUserPage.fullNameInput.fill(user.fullName);
|
|
||||||
await adminCreateUserPage.emailInput.fill(user.email);
|
|
||||||
await adminCreateUserPage.roleInput.click();
|
|
||||||
await adminCreateUserPage.page
|
|
||||||
.getByRole('option', { name: 'Admin' })
|
|
||||||
.click();
|
|
||||||
await adminCreateUserPage.createButton.click();
|
|
||||||
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
|
|
||||||
state: 'attached',
|
|
||||||
});
|
});
|
||||||
|
await test.step(
|
||||||
|
'Create a user',
|
||||||
|
async () => {
|
||||||
|
await adminUsersPage.createUserButton.click();
|
||||||
|
await adminCreateUserPage.fullNameInput.fill(user.fullName);
|
||||||
|
await adminCreateUserPage.emailInput.fill(user.email);
|
||||||
|
await adminCreateUserPage.roleInput.click();
|
||||||
|
await adminCreateUserPage.page.getByRole(
|
||||||
|
'option', { name: 'Admin' }
|
||||||
|
).click();
|
||||||
|
await adminCreateUserPage.createButton.click();
|
||||||
|
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
|
||||||
|
state: 'attached'
|
||||||
|
});
|
||||||
|
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
'snackbar-create-user-success'
|
'snackbar-create-user-success'
|
||||||
|
);
|
||||||
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminUsersPage.navigateTo();
|
||||||
|
await adminUsersPage.closeSnackbar();
|
||||||
|
}
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await test.step(
|
||||||
await adminUsersPage.navigateTo();
|
'Check the user exists with the expected properties',
|
||||||
});
|
async () => {
|
||||||
await test.step('Check the user exists with the expected properties', async () => {
|
await adminUsersPage.findUserPageWithEmail(user.email);
|
||||||
await adminUsersPage.findUserPageWithEmail(user.email);
|
const userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
||||||
const userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
const data = await adminUsersPage.getRowData(userRow);
|
||||||
const data = await adminUsersPage.getRowData(userRow);
|
await expect(data.email).toBe(user.email);
|
||||||
await expect(data.email).toBe(user.email);
|
await expect(data.fullName).toBe(user.fullName);
|
||||||
await expect(data.fullName).toBe(user.fullName);
|
await expect(data.role).toBe('Admin');
|
||||||
await expect(data.role).toBe('Admin');
|
}
|
||||||
});
|
|
||||||
await test.step('Edit user info and make sure the edit works correctly', async () => {
|
|
||||||
await adminUsersPage.findUserPageWithEmail(user.email);
|
|
||||||
|
|
||||||
let userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
|
||||||
await adminUsersPage.clickEditUser(userRow);
|
|
||||||
await adminEditUserPage.waitForLoad(user.fullName);
|
|
||||||
const newUserInfo = adminEditUserPage.generateUser();
|
|
||||||
await adminEditUserPage.fullNameInput.fill(newUserInfo.fullName);
|
|
||||||
await adminEditUserPage.updateButton.click();
|
|
||||||
|
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
|
||||||
'snackbar-edit-user-success'
|
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await test.step(
|
||||||
|
'Edit user info and make sure the edit works correctly',
|
||||||
|
async () => {
|
||||||
|
await adminUsersPage.findUserPageWithEmail(user.email);
|
||||||
|
|
||||||
await adminUsersPage.findUserPageWithEmail(user.email);
|
let userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
||||||
userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
await adminUsersPage.clickEditUser(userRow);
|
||||||
const rowData = await adminUsersPage.getRowData(userRow);
|
await adminEditUserPage.waitForLoad(user.fullName);
|
||||||
await expect(rowData.fullName).toBe(newUserInfo.fullName);
|
const newUserInfo = adminEditUserPage.generateUser();
|
||||||
});
|
await adminEditUserPage.fullNameInput.fill(newUserInfo.fullName);
|
||||||
await test.step('Delete user and check the page confirms this deletion', async () => {
|
await adminEditUserPage.updateButton.click();
|
||||||
await adminUsersPage.findUserPageWithEmail(user.email);
|
|
||||||
const userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
|
||||||
await adminUsersPage.clickDeleteUser(userRow);
|
|
||||||
const modal = adminUsersPage.deleteUserModal;
|
|
||||||
await modal.deleteButton.click();
|
|
||||||
|
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
'snackbar-delete-user-success'
|
'snackbar-edit-user-success'
|
||||||
|
);
|
||||||
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminUsersPage.closeSnackbar();
|
||||||
|
|
||||||
|
await adminUsersPage.findUserPageWithEmail(user.email);
|
||||||
|
userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
||||||
|
const rowData = await adminUsersPage.getRowData(userRow);
|
||||||
|
await expect(rowData.fullName).toBe(newUserInfo.fullName);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
await test.step(
|
||||||
await expect(userRow).not.toBeVisible(false);
|
'Delete user and check the page confirms this deletion',
|
||||||
});
|
async () => {
|
||||||
});
|
await adminUsersPage.findUserPageWithEmail(user.email);
|
||||||
|
const userRow = await adminUsersPage.getUserRowByEmail(user.email);
|
||||||
|
await adminUsersPage.clickDeleteUser(userRow);
|
||||||
|
const modal = adminUsersPage.deleteUserModal;
|
||||||
|
await modal.deleteButton.click();
|
||||||
|
|
||||||
test('Creating a user which has been deleted', async ({
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
adminCreateUserPage,
|
'snackbar-delete-user-success'
|
||||||
adminUsersPage,
|
);
|
||||||
}) => {
|
await expect(snackbar.variant).toBe('success');
|
||||||
adminCreateUserPage.seed(9100);
|
await adminUsersPage.closeSnackbar();
|
||||||
const testUser = adminCreateUserPage.generateUser();
|
await expect(userRow).not.toBeVisible(false);
|
||||||
|
}
|
||||||
await test.step('Create the test user', async () => {
|
|
||||||
await adminUsersPage.navigateTo();
|
|
||||||
await adminUsersPage.createUserButton.click();
|
|
||||||
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
|
||||||
await adminCreateUserPage.emailInput.fill(testUser.email);
|
|
||||||
await adminCreateUserPage.roleInput.click();
|
|
||||||
await adminCreateUserPage.page
|
|
||||||
.getByRole('option', { name: 'Admin' })
|
|
||||||
.click();
|
|
||||||
await adminCreateUserPage.createButton.click();
|
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
|
||||||
'snackbar-create-user-success'
|
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('Delete the created user', async () => {
|
test(
|
||||||
await adminUsersPage.navigateTo();
|
'Creating a user which has been deleted',
|
||||||
await adminUsersPage.findUserPageWithEmail(testUser.email);
|
async ({ adminCreateUserPage, adminUsersPage }) => {
|
||||||
const userRow = await adminUsersPage.getUserRowByEmail(testUser.email);
|
adminCreateUserPage.seed(9100);
|
||||||
await adminUsersPage.clickDeleteUser(userRow);
|
const testUser = adminCreateUserPage.generateUser();
|
||||||
const modal = adminUsersPage.deleteUserModal;
|
|
||||||
await modal.deleteButton.click();
|
await test.step(
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
'Create the test user',
|
||||||
'snackbar-delete-user-success'
|
async () => {
|
||||||
|
await adminUsersPage.navigateTo();
|
||||||
|
await adminUsersPage.createUserButton.click();
|
||||||
|
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
||||||
|
await adminCreateUserPage.emailInput.fill(testUser.email);
|
||||||
|
await adminCreateUserPage.roleInput.click();
|
||||||
|
await adminCreateUserPage.page.getByRole(
|
||||||
|
'option', { name: 'Admin' }
|
||||||
|
).click();
|
||||||
|
await adminCreateUserPage.createButton.click();
|
||||||
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
|
'snackbar-create-user-success'
|
||||||
|
);
|
||||||
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminUsersPage.closeSnackbar();
|
||||||
|
}
|
||||||
);
|
);
|
||||||
await expect(snackbar).not.toBeNull();
|
|
||||||
await expect(snackbar.variant).toBe('success');
|
|
||||||
await expect(userRow).not.toBeVisible(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('Create the user again', async () => {
|
await test.step(
|
||||||
await adminUsersPage.createUserButton.click();
|
'Delete the created user',
|
||||||
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
async () => {
|
||||||
await adminCreateUserPage.emailInput.fill(testUser.email);
|
await adminUsersPage.navigateTo();
|
||||||
await adminCreateUserPage.roleInput.click();
|
await adminUsersPage.findUserPageWithEmail(testUser.email);
|
||||||
await adminCreateUserPage.page
|
const userRow = await adminUsersPage.getUserRowByEmail(testUser.email);
|
||||||
.getByRole('option', { name: 'Admin' })
|
await adminUsersPage.clickDeleteUser(userRow);
|
||||||
.click();
|
const modal = adminUsersPage.deleteUserModal;
|
||||||
await adminCreateUserPage.createButton.click();
|
await modal.deleteButton.click();
|
||||||
const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
await expect(snackbar.variant).toBe('error');
|
'snackbar-delete-user-success'
|
||||||
});
|
);
|
||||||
});
|
await expect(snackbar).not.toBeNull();
|
||||||
|
await expect(snackbar.variant).toBe('success');
|
||||||
test('Creating a user which already exists', async ({
|
await adminUsersPage.closeSnackbar();
|
||||||
adminCreateUserPage,
|
await expect(userRow).not.toBeVisible(false);
|
||||||
adminUsersPage,
|
}
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
adminCreateUserPage.seed(9200);
|
|
||||||
const testUser = adminCreateUserPage.generateUser();
|
|
||||||
|
|
||||||
await test.step('Create the test user', async () => {
|
|
||||||
await adminUsersPage.createUserButton.click();
|
|
||||||
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
|
||||||
await adminCreateUserPage.emailInput.fill(testUser.email);
|
|
||||||
await adminCreateUserPage.roleInput.click();
|
|
||||||
await adminCreateUserPage.page
|
|
||||||
.getByRole('option', { name: 'Admin' })
|
|
||||||
.click();
|
|
||||||
await adminCreateUserPage.createButton.click();
|
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
|
||||||
'snackbar-create-user-success'
|
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('Create the user again', async () => {
|
await test.step(
|
||||||
await adminUsersPage.navigateTo();
|
'Create the user again',
|
||||||
await adminUsersPage.createUserButton.click();
|
async () => {
|
||||||
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
await adminUsersPage.createUserButton.click();
|
||||||
await adminCreateUserPage.emailInput.fill(testUser.email);
|
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
||||||
const createUserPageUrl = page.url();
|
await adminCreateUserPage.emailInput.fill(testUser.email);
|
||||||
await adminCreateUserPage.roleInput.click();
|
await adminCreateUserPage.roleInput.click();
|
||||||
await adminCreateUserPage.page
|
await adminCreateUserPage.page.getByRole(
|
||||||
.getByRole('option', { name: 'Admin' })
|
'option', { name: 'Admin' }
|
||||||
.click();
|
).click();
|
||||||
await adminCreateUserPage.createButton.click();
|
await adminCreateUserPage.createButton.click();
|
||||||
|
const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
|
||||||
await expect(page.url()).toBe(createUserPageUrl);
|
await expect(snackbar.variant).toBe('error');
|
||||||
const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
|
await adminUsersPage.closeSnackbar();
|
||||||
await expect(snackbar.variant).toBe('error');
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Editing a user to have the same email as another user should not be allowed', async ({
|
|
||||||
adminCreateUserPage,
|
|
||||||
adminEditUserPage,
|
|
||||||
adminUsersPage,
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
adminCreateUserPage.seed(9300);
|
|
||||||
const user1 = adminCreateUserPage.generateUser();
|
|
||||||
const user2 = adminCreateUserPage.generateUser();
|
|
||||||
await test.step('Create the first user', async () => {
|
|
||||||
await adminUsersPage.navigateTo();
|
|
||||||
await adminUsersPage.createUserButton.click();
|
|
||||||
await adminCreateUserPage.fullNameInput.fill(user1.fullName);
|
|
||||||
await adminCreateUserPage.emailInput.fill(user1.email);
|
|
||||||
await adminCreateUserPage.roleInput.click();
|
|
||||||
await adminCreateUserPage.page
|
|
||||||
.getByRole('option', { name: 'Admin' })
|
|
||||||
.click();
|
|
||||||
await adminCreateUserPage.createButton.click();
|
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
|
||||||
'snackbar-create-user-success'
|
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
}
|
||||||
await adminUsersPage.closeAllSnackbars();
|
);
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('Create the second user', async () => {
|
test(
|
||||||
await adminUsersPage.navigateTo();
|
'Creating a user which already exists',
|
||||||
await adminUsersPage.createUserButton.click();
|
async ({ adminCreateUserPage, adminUsersPage, page }) => {
|
||||||
await adminCreateUserPage.fullNameInput.fill(user2.fullName);
|
adminCreateUserPage.seed(9200);
|
||||||
await adminCreateUserPage.emailInput.fill(user2.email);
|
const testUser = adminCreateUserPage.generateUser();
|
||||||
await adminCreateUserPage.roleInput.click();
|
|
||||||
await adminCreateUserPage.page
|
await test.step(
|
||||||
.getByRole('option', { name: 'Admin' })
|
'Create the test user',
|
||||||
.click();
|
async () => {
|
||||||
await adminCreateUserPage.createButton.click();
|
await adminUsersPage.createUserButton.click();
|
||||||
const snackbar = await adminUsersPage.getSnackbarData(
|
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
||||||
'snackbar-create-user-success'
|
await adminCreateUserPage.emailInput.fill(testUser.email);
|
||||||
|
await adminCreateUserPage.roleInput.click();
|
||||||
|
await adminCreateUserPage.page.getByRole(
|
||||||
|
'option', { name: 'Admin' }
|
||||||
|
).click();
|
||||||
|
await adminCreateUserPage.createButton.click();
|
||||||
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
|
'snackbar-create-user-success'
|
||||||
|
);
|
||||||
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminUsersPage.closeSnackbar();
|
||||||
|
}
|
||||||
);
|
);
|
||||||
await expect(snackbar.variant).toBe('success');
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('Try editing the second user to have the email of the first user', async () => {
|
await test.step(
|
||||||
await adminUsersPage.navigateTo();
|
'Create the user again',
|
||||||
await adminUsersPage.findUserPageWithEmail(user2.email);
|
async () => {
|
||||||
let userRow = await adminUsersPage.getUserRowByEmail(user2.email);
|
await adminUsersPage.navigateTo();
|
||||||
await adminUsersPage.clickEditUser(userRow);
|
await adminUsersPage.createUserButton.click();
|
||||||
await adminEditUserPage.waitForLoad(user2.fullName);
|
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
||||||
await adminEditUserPage.emailInput.fill(user1.email);
|
await adminCreateUserPage.emailInput.fill(testUser.email);
|
||||||
const editPageUrl = page.url();
|
const createUserPageUrl = page.url();
|
||||||
await adminEditUserPage.updateButton.click();
|
await adminCreateUserPage.roleInput.click();
|
||||||
|
await adminCreateUserPage.page.getByRole(
|
||||||
|
'option', { name: 'Admin' }
|
||||||
|
).click();
|
||||||
|
await adminCreateUserPage.createButton.click();
|
||||||
|
|
||||||
const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
|
await expect(page.url()).toBe(createUserPageUrl);
|
||||||
await expect(snackbar.variant).toBe('error');
|
const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
|
||||||
await expect(page.url()).toBe(editPageUrl);
|
await expect(snackbar.variant).toBe('error');
|
||||||
});
|
await adminUsersPage.closeSnackbar();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Editing a user to have the same email as another user should not be allowed',
|
||||||
|
async ({
|
||||||
|
adminCreateUserPage, adminEditUserPage, adminUsersPage, page
|
||||||
|
}) => {
|
||||||
|
adminCreateUserPage.seed(9300);
|
||||||
|
const user1 = adminCreateUserPage.generateUser();
|
||||||
|
const user2 = adminCreateUserPage.generateUser();
|
||||||
|
await test.step(
|
||||||
|
'Create the first user',
|
||||||
|
async () => {
|
||||||
|
await adminUsersPage.navigateTo();
|
||||||
|
await adminUsersPage.createUserButton.click();
|
||||||
|
await adminCreateUserPage.fullNameInput.fill(user1.fullName);
|
||||||
|
await adminCreateUserPage.emailInput.fill(user1.email);
|
||||||
|
await adminCreateUserPage.roleInput.click();
|
||||||
|
await adminCreateUserPage.page.getByRole(
|
||||||
|
'option', { name: 'Admin' }
|
||||||
|
).click();
|
||||||
|
await adminCreateUserPage.createButton.click();
|
||||||
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
|
'snackbar-create-user-success'
|
||||||
|
);
|
||||||
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminUsersPage.closeSnackbar();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await test.step(
|
||||||
|
'Create the second user',
|
||||||
|
async () => {
|
||||||
|
await adminUsersPage.navigateTo();
|
||||||
|
await adminUsersPage.createUserButton.click();
|
||||||
|
await adminCreateUserPage.fullNameInput.fill(user2.fullName);
|
||||||
|
await adminCreateUserPage.emailInput.fill(user2.email);
|
||||||
|
await adminCreateUserPage.roleInput.click();
|
||||||
|
await adminCreateUserPage.page.getByRole(
|
||||||
|
'option', { name: 'Admin' }
|
||||||
|
).click();
|
||||||
|
await adminCreateUserPage.createButton.click();
|
||||||
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
|
'snackbar-create-user-success'
|
||||||
|
);
|
||||||
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
await adminUsersPage.closeSnackbar();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await test.step(
|
||||||
|
'Try editing the second user to have the email of the first user',
|
||||||
|
async () => {
|
||||||
|
await adminUsersPage.navigateTo();
|
||||||
|
await adminUsersPage.findUserPageWithEmail(user2.email);
|
||||||
|
let userRow = await adminUsersPage.getUserRowByEmail(user2.email);
|
||||||
|
await adminUsersPage.clickEditUser(userRow);
|
||||||
|
await adminEditUserPage.waitForLoad(user2.fullName);
|
||||||
|
await adminEditUserPage.emailInput.fill(user1.email);
|
||||||
|
const editPageUrl = page.url();
|
||||||
|
await adminEditUserPage.updateButton.click();
|
||||||
|
|
||||||
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
|
'snackbar-error'
|
||||||
|
);
|
||||||
|
await expect(snackbar.variant).toBe('error');
|
||||||
|
await adminUsersPage.closeSnackbar();
|
||||||
|
await expect(page.url()).toBe(editPageUrl);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
@@ -1,55 +1,24 @@
|
|||||||
const { request } = require('@playwright/test');
|
|
||||||
const { test, expect } = require('../../fixtures/index');
|
const { test, expect } = require('../../fixtures/index');
|
||||||
const {
|
const {AddMattermostConnectionModal} = require('../../fixtures/apps/mattermost/add-mattermost-connection-modal');
|
||||||
AddMattermostConnectionModal,
|
|
||||||
} = require('../../fixtures/apps/mattermost/add-mattermost-connection-modal');
|
|
||||||
const { createFlow, updateFlowName, getFlow, updateFlowStep, testStep } = require('../../helpers/flow-api-helper');
|
|
||||||
const { getToken } = require('../../helpers/auth-api-helper');
|
|
||||||
|
|
||||||
test.describe('Pop-up message on connections', () => {
|
test.describe('Pop-up message on connections', () => {
|
||||||
test.beforeEach(async ({ flowEditorPage, page }) => {
|
test.beforeEach(async ({ flowEditorPage, page }) => {
|
||||||
const apiRequest = await request.newContext();
|
await page.getByTestId('create-flow-button').click();
|
||||||
const tokenJsonResponse = await getToken(apiRequest);
|
await page.waitForURL(
|
||||||
const token = tokenJsonResponse.data.token;
|
/\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/
|
||||||
|
);
|
||||||
|
await expect(page.getByTestId('flow-step')).toHaveCount(2);
|
||||||
|
|
||||||
let flow = await createFlow(apiRequest, token);
|
await flowEditorPage.flowName.click();
|
||||||
const flowId = flow.data.id;
|
await flowEditorPage.flowNameInput.fill('PopupFlow');
|
||||||
await updateFlowName(apiRequest, token, flowId);
|
await flowEditorPage.createWebhookTrigger(true);
|
||||||
flow = await getFlow(apiRequest, token, flowId);
|
|
||||||
const flowSteps = flow.data.steps;
|
|
||||||
|
|
||||||
const triggerStepId = flowSteps.find((step) => step.type === 'trigger').id;
|
await flowEditorPage.chooseAppAndEvent('Mattermost', 'Send a message to channel');
|
||||||
const actionStepId = flowSteps.find((step) => step.type === 'action').id;
|
await expect(flowEditorPage.continueButton).toHaveCount(1);
|
||||||
|
await expect(flowEditorPage.continueButton).not.toBeEnabled();
|
||||||
const triggerStep = await updateFlowStep(apiRequest, token, triggerStepId, {
|
|
||||||
appKey: 'webhook',
|
|
||||||
key: 'catchRawWebhook',
|
|
||||||
parameters: {
|
|
||||||
workSynchronously: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await apiRequest.get(triggerStep.data.webhookUrl);
|
|
||||||
await testStep(apiRequest, token, triggerStepId);
|
|
||||||
|
|
||||||
await updateFlowStep(apiRequest, token, actionStepId, {
|
|
||||||
appKey: 'mattermost',
|
|
||||||
key: 'sendMessageToChannel',
|
|
||||||
});
|
|
||||||
await testStep(apiRequest, token, actionStepId);
|
|
||||||
|
|
||||||
await page.reload();
|
|
||||||
|
|
||||||
const flowRow = await page.getByTestId('flow-row').filter({
|
|
||||||
hasText: flowId,
|
|
||||||
});
|
|
||||||
await flowRow.click();
|
|
||||||
const flowTriggerStep = await page.getByTestId('flow-step').nth(1);
|
|
||||||
await flowTriggerStep.click();
|
|
||||||
await page.getByText('Choose connection').click();
|
|
||||||
|
|
||||||
await flowEditorPage.connectionAutocomplete.click();
|
await flowEditorPage.connectionAutocomplete.click();
|
||||||
await flowEditorPage.addNewConnectionItem.click();
|
await flowEditorPage.addNewConnectionItem.click(); });
|
||||||
});
|
|
||||||
|
|
||||||
test('should show error to remind to enable pop-up on connection create', async ({
|
test('should show error to remind to enable pop-up on connection create', async ({
|
||||||
page,
|
page,
|
||||||
@@ -59,7 +28,7 @@ test.describe('Pop-up message on connections', () => {
|
|||||||
// Inject script to override window.open
|
// Inject script to override window.open
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
window.open = function () {
|
window.open = function() {
|
||||||
console.log('Popup blocked!');
|
console.log('Popup blocked!');
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@@ -68,10 +37,8 @@ test.describe('Pop-up message on connections', () => {
|
|||||||
await addMattermostConnectionModal.fillConnectionForm();
|
await addMattermostConnectionModal.fillConnectionForm();
|
||||||
await addMattermostConnectionModal.submitConnectionForm();
|
await addMattermostConnectionModal.submitConnectionForm();
|
||||||
|
|
||||||
await expect(page.getByTestId('add-connection-error')).toHaveCount(1);
|
await expect(page.getByTestId("add-connection-error")).toHaveCount(1);
|
||||||
await expect(page.getByTestId('add-connection-error')).toHaveText(
|
await expect(page.getByTestId("add-connection-error")).toHaveText('Make sure pop-ups are enabled in your browser.');
|
||||||
'Make sure pop-ups are enabled in your browser.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not show pop-up error if pop-ups are enabled on connection create', async ({
|
test('should not show pop-up error if pop-ups are enabled on connection create', async ({
|
||||||
@@ -84,15 +51,13 @@ test.describe('Pop-up message on connections', () => {
|
|||||||
await addMattermostConnectionModal.submitConnectionForm();
|
await addMattermostConnectionModal.submitConnectionForm();
|
||||||
|
|
||||||
const popup = await popupPromise;
|
const popup = await popupPromise;
|
||||||
await expect(popup.url()).toContain('mattermost');
|
await expect(popup.url()).toContain("mattermost");
|
||||||
await expect(page.getByTestId('add-connection-error')).toHaveCount(0);
|
await expect(page.getByTestId("add-connection-error")).toHaveCount(0);
|
||||||
|
|
||||||
await test.step('Should show error on failed credentials verification', async () => {
|
await test.step('Should show error on failed credentials verification', async () => {
|
||||||
await popup.close();
|
await popup.close();
|
||||||
await expect(page.getByTestId('add-connection-error')).toHaveCount(1);
|
await expect(page.getByTestId("add-connection-error")).toHaveCount(1);
|
||||||
await expect(page.getByTestId('add-connection-error')).toHaveText(
|
await expect(page.getByTestId("add-connection-error")).toHaveText('Error occured while verifying credentials!');
|
||||||
'Error occured while verifying credentials!'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
@@ -1,38 +1,57 @@
|
|||||||
const { request } = require('@playwright/test');
|
|
||||||
const { publicTest, expect } = require('../../fixtures/index');
|
const { publicTest, expect } = require('../../fixtures/index');
|
||||||
|
const { AdminUsersPage } = require('../../fixtures/admin/users-page');
|
||||||
const { MyProfilePage } = require('../../fixtures/my-profile-page');
|
const { MyProfilePage } = require('../../fixtures/my-profile-page');
|
||||||
const { LoginPage } = require('../../fixtures/login-page');
|
const { LoginPage } = require('../../fixtures/login-page');
|
||||||
const { addUser, acceptInvitation } = require('../../helpers/user-api-helper');
|
|
||||||
const { getToken } = require('../../helpers/auth-api-helper');
|
|
||||||
|
|
||||||
publicTest.describe('My Profile', () => {
|
publicTest.describe('My Profile', () => {
|
||||||
let testUser;
|
let testUser;
|
||||||
|
|
||||||
publicTest.beforeEach(
|
publicTest.beforeEach(
|
||||||
async ({ adminCreateUserPage, loginPage, page }) => {
|
async ({ acceptInvitationPage, adminCreateUserPage, loginPage, page }) => {
|
||||||
let addUserResponse;
|
let acceptInvitationLink;
|
||||||
const apiRequest = await request.newContext();
|
|
||||||
|
|
||||||
adminCreateUserPage.seed(
|
adminCreateUserPage.seed(
|
||||||
Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
|
Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
|
||||||
);
|
);
|
||||||
testUser = adminCreateUserPage.generateUser();
|
testUser = adminCreateUserPage.generateUser();
|
||||||
|
|
||||||
|
const adminUsersPage = new AdminUsersPage(page);
|
||||||
|
const myProfilePage = new MyProfilePage(page);
|
||||||
|
|
||||||
|
await publicTest.step('login as Admin', async () => {
|
||||||
|
await loginPage.login();
|
||||||
|
await expect(loginPage.page).toHaveURL('/flows');
|
||||||
|
});
|
||||||
|
|
||||||
await publicTest.step('create new user', async () => {
|
await publicTest.step('create new user', async () => {
|
||||||
const tokenJsonResponse = await getToken(apiRequest);
|
await adminUsersPage.navigateTo();
|
||||||
addUserResponse = await addUser(
|
await adminUsersPage.createUserButton.click();
|
||||||
apiRequest,
|
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
|
||||||
tokenJsonResponse.data.token,
|
await adminCreateUserPage.emailInput.fill(testUser.email);
|
||||||
{
|
await adminCreateUserPage.roleInput.click();
|
||||||
fullName: testUser.fullName,
|
await adminCreateUserPage.page
|
||||||
email: testUser.email,
|
.getByRole('option', { name: 'Admin' })
|
||||||
}
|
.click();
|
||||||
|
await adminCreateUserPage.createButton.click();
|
||||||
|
const snackbar = await adminUsersPage.getSnackbarData(
|
||||||
|
'snackbar-create-user-success'
|
||||||
);
|
);
|
||||||
|
await expect(snackbar.variant).toBe('success');
|
||||||
|
});
|
||||||
|
|
||||||
|
await publicTest.step('copy invitation link', async () => {
|
||||||
|
const invitationMessage =
|
||||||
|
await adminCreateUserPage.acceptInvitationLink;
|
||||||
|
acceptInvitationLink = await invitationMessage.getAttribute('href');
|
||||||
|
});
|
||||||
|
|
||||||
|
await publicTest.step('logout', async () => {
|
||||||
|
await myProfilePage.logout();
|
||||||
});
|
});
|
||||||
|
|
||||||
await publicTest.step('accept invitation', async () => {
|
await publicTest.step('accept invitation', async () => {
|
||||||
let acceptToken = addUserResponse.data.acceptInvitationUrl.split('=')[1];
|
await page.goto(acceptInvitationLink);
|
||||||
await acceptInvitation(apiRequest, {token:acceptToken, password:LoginPage.defaultPassword});
|
await acceptInvitationPage.acceptInvitation(LoginPage.defaultPassword);
|
||||||
});
|
});
|
||||||
|
|
||||||
await publicTest.step('login as new Admin', async () => {
|
await publicTest.step('login as new Admin', async () => {
|
||||||
|
@@ -79,7 +79,7 @@
|
|||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
"@playwright/test@1.49.0":
|
"@playwright/test@^1.45.1":
|
||||||
version "1.49.0"
|
version "1.49.0"
|
||||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.0.tgz#74227385b58317ee076b86b56d0e1e1b25cff01e"
|
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.0.tgz#74227385b58317ee076b86b56d0e1e1b25cff01e"
|
||||||
integrity sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==
|
integrity sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==
|
||||||
@@ -101,13 +101,6 @@ acorn@^8.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
|
||||||
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
|
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
|
||||||
|
|
||||||
ajv-formats@^2.1.1:
|
|
||||||
version "2.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
|
|
||||||
integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
|
|
||||||
dependencies:
|
|
||||||
ajv "^8.0.0"
|
|
||||||
|
|
||||||
ajv@^6.12.4:
|
ajv@^6.12.4:
|
||||||
version "6.12.6"
|
version "6.12.6"
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||||
@@ -118,16 +111,6 @@ ajv@^6.12.4:
|
|||||||
json-schema-traverse "^0.4.1"
|
json-schema-traverse "^0.4.1"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
ajv@^8.0.0, ajv@^8.17.1:
|
|
||||||
version "8.17.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
|
|
||||||
integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
|
|
||||||
dependencies:
|
|
||||||
fast-deep-equal "^3.1.3"
|
|
||||||
fast-uri "^3.0.1"
|
|
||||||
json-schema-traverse "^1.0.0"
|
|
||||||
require-from-string "^2.0.2"
|
|
||||||
|
|
||||||
ansi-regex@^5.0.1:
|
ansi-regex@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
|
||||||
@@ -243,11 +226,6 @@ cross-spawn@^7.0.2:
|
|||||||
shebang-command "^2.0.0"
|
shebang-command "^2.0.0"
|
||||||
which "^2.0.1"
|
which "^2.0.1"
|
||||||
|
|
||||||
db-errors@^0.2.3:
|
|
||||||
version "0.2.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/db-errors/-/db-errors-0.2.3.tgz#a6a38952e00b20e790f2695a6446b3c65497ffa2"
|
|
||||||
integrity sha512-OOgqgDuCavHXjYSJoV2yGhv6SeG8nk42aoCSoyXLZUH7VwFG27rxbavU1z+VrZbZjphw5UkDQwUlD21MwZpUng==
|
|
||||||
|
|
||||||
debug@4.3.4:
|
debug@4.3.4:
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
@@ -426,11 +404,6 @@ fast-levenshtein@^2.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||||
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
|
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
|
||||||
|
|
||||||
fast-uri@^3.0.1:
|
|
||||||
version "3.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241"
|
|
||||||
integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==
|
|
||||||
|
|
||||||
fastq@^1.6.0:
|
fastq@^1.6.0:
|
||||||
version "1.17.1"
|
version "1.17.1"
|
||||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
|
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
|
||||||
@@ -649,11 +622,6 @@ json-schema-traverse@^0.4.1:
|
|||||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||||
|
|
||||||
json-schema-traverse@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
|
|
||||||
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
|
|
||||||
|
|
||||||
json-stable-stringify-without-jsonify@^1.0.1:
|
json-stable-stringify-without-jsonify@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||||
@@ -759,15 +727,6 @@ natural-compare@^1.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||||
|
|
||||||
objection@^3.1.5:
|
|
||||||
version "3.1.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/objection/-/objection-3.1.5.tgz#53c32f6b6cba2958bc28cf723de96c2676da8286"
|
|
||||||
integrity sha512-Hx/ipAwXSuRBbOMWFKtRsAN0yITafqXtWB4OT4Z9wED7ty1h7bOnBdhLtcNus23GwLJqcMsRWdodL2p5GwlnfQ==
|
|
||||||
dependencies:
|
|
||||||
ajv "^8.17.1"
|
|
||||||
ajv-formats "^2.1.1"
|
|
||||||
db-errors "^0.2.3"
|
|
||||||
|
|
||||||
once@^1.3.0:
|
once@^1.3.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
@@ -974,11 +933,6 @@ rechoir@^0.8.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
resolve "^1.20.0"
|
resolve "^1.20.0"
|
||||||
|
|
||||||
require-from-string@^2.0.2:
|
|
||||||
version "2.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
|
|
||||||
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
|
|
||||||
|
|
||||||
resolve-from@^4.0.0:
|
resolve-from@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||||
|
@@ -46,7 +46,12 @@ function Form(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<form onSubmit={methods.handleSubmit(onSubmit)} {...formProps}>
|
<form
|
||||||
|
onSubmit={methods.handleSubmit((data, event) =>
|
||||||
|
onSubmit?.(data, event, methods.setError),
|
||||||
|
)}
|
||||||
|
{...formProps}
|
||||||
|
>
|
||||||
{render ? render(methods) : children}
|
{render ? render(methods) : children}
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
|
@@ -2,35 +2,55 @@ import * as React from 'react';
|
|||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink } from 'react-router-dom';
|
||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { Alert } from '@mui/material';
|
import Alert from '@mui/material/Alert';
|
||||||
import LoadingButton from '@mui/lab/LoadingButton';
|
import LoadingButton from '@mui/lab/LoadingButton';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import { enqueueSnackbar } from 'notistack';
|
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import Link from '@mui/material/Link';
|
import Link from '@mui/material/Link';
|
||||||
|
|
||||||
|
import { getGeneralErrorMessage } from 'helpers/errors';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useInstallation from 'hooks/useInstallation';
|
import useInstallation from 'hooks/useInstallation';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import Form from 'components/Form';
|
import Form from 'components/Form';
|
||||||
import TextField from 'components/TextField';
|
import TextField from 'components/TextField';
|
||||||
|
|
||||||
const validationSchema = yup.object().shape({
|
const getValidationSchema = (formatMessage) => {
|
||||||
fullName: yup.string().trim().required('installationForm.mandatoryInput'),
|
const getMandatoryInputMessage = (inputNameId) =>
|
||||||
email: yup
|
formatMessage('installationForm.mandatoryInput', {
|
||||||
.string()
|
inputName: formatMessage(inputNameId),
|
||||||
.trim()
|
});
|
||||||
.email('installationForm.validateEmail')
|
|
||||||
.required('installationForm.mandatoryInput'),
|
|
||||||
password: yup.string().required('installationForm.mandatoryInput'),
|
|
||||||
confirmPassword: yup
|
|
||||||
.string()
|
|
||||||
.required('installationForm.mandatoryInput')
|
|
||||||
.oneOf([yup.ref('password')], 'installationForm.passwordsMustMatch'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const initialValues = {
|
return yup.object().shape({
|
||||||
|
fullName: yup
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.required(
|
||||||
|
getMandatoryInputMessage('installationForm.fullNameFieldLabel'),
|
||||||
|
),
|
||||||
|
email: yup
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.required(getMandatoryInputMessage('installationForm.emailFieldLabel'))
|
||||||
|
.email(formatMessage('installationForm.validateEmail')),
|
||||||
|
password: yup
|
||||||
|
.string()
|
||||||
|
.required(getMandatoryInputMessage('installationForm.passwordFieldLabel'))
|
||||||
|
.min(6, formatMessage('installationForm.passwordMinLength')),
|
||||||
|
confirmPassword: yup
|
||||||
|
.string()
|
||||||
|
.required(
|
||||||
|
getMandatoryInputMessage('installationForm.confirmPasswordFieldLabel'),
|
||||||
|
)
|
||||||
|
.oneOf(
|
||||||
|
[yup.ref('password')],
|
||||||
|
formatMessage('installationForm.passwordsMustMatch'),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultValues = {
|
||||||
fullName: '',
|
fullName: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
@@ -39,7 +59,7 @@ const initialValues = {
|
|||||||
|
|
||||||
function InstallationForm() {
|
function InstallationForm() {
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const install = useInstallation();
|
const { mutateAsync: install, isSuccess, isPending } = useInstallation();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const handleOnRedirect = () => {
|
const handleOnRedirect = () => {
|
||||||
@@ -48,21 +68,38 @@ function InstallationForm() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async (values) => {
|
const handleSubmit = async ({ fullName, email, password }, e, setError) => {
|
||||||
const { fullName, email, password } = values;
|
|
||||||
try {
|
try {
|
||||||
await install.mutateAsync({
|
await install({
|
||||||
fullName,
|
fullName,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
enqueueSnackbar(
|
const errors = error?.response?.data?.errors;
|
||||||
error?.message || formatMessage('installationForm.error'),
|
if (errors) {
|
||||||
{
|
const fieldNames = Object.keys(defaultValues);
|
||||||
variant: 'error',
|
Object.entries(errors).forEach(([fieldName, fieldErrors]) => {
|
||||||
},
|
if (fieldNames.includes(fieldName) && Array.isArray(fieldErrors)) {
|
||||||
);
|
setError(fieldName, {
|
||||||
|
type: 'fieldRequestError',
|
||||||
|
message: fieldErrors.join(', '),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const generalError = getGeneralErrorMessage({
|
||||||
|
error,
|
||||||
|
fallbackMessage: formatMessage('installationForm.error'),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (generalError) {
|
||||||
|
setError('root.general', {
|
||||||
|
type: 'requestError',
|
||||||
|
message: generalError,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,11 +119,13 @@ function InstallationForm() {
|
|||||||
{formatMessage('installationForm.title')}
|
{formatMessage('installationForm.title')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Form
|
<Form
|
||||||
defaultValues={initialValues}
|
automaticValidation={false}
|
||||||
|
noValidate
|
||||||
|
defaultValues={defaultValues}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
resolver={yupResolver(validationSchema)}
|
resolver={yupResolver(getValidationSchema(formatMessage))}
|
||||||
mode="onChange"
|
mode="onChange"
|
||||||
render={({ formState: { errors, touchedFields } }) => (
|
render={({ formState: { errors } }) => (
|
||||||
<>
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
label={formatMessage('installationForm.fullNameFieldLabel')}
|
label={formatMessage('installationForm.fullNameFieldLabel')}
|
||||||
@@ -95,19 +134,12 @@ function InstallationForm() {
|
|||||||
margin="dense"
|
margin="dense"
|
||||||
autoComplete="fullName"
|
autoComplete="fullName"
|
||||||
data-test="fullName-text-field"
|
data-test="fullName-text-field"
|
||||||
error={touchedFields.fullName && !!errors?.fullName}
|
error={!!errors?.fullName}
|
||||||
helperText={
|
helperText={errors?.fullName?.message}
|
||||||
touchedFields.fullName && errors?.fullName?.message
|
|
||||||
? formatMessage(errors?.fullName?.message, {
|
|
||||||
inputName: formatMessage(
|
|
||||||
'installationForm.fullNameFieldLabel',
|
|
||||||
),
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
required
|
required
|
||||||
readOnly={install.isSuccess}
|
readOnly={isSuccess}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
label={formatMessage('installationForm.emailFieldLabel')}
|
label={formatMessage('installationForm.emailFieldLabel')}
|
||||||
name="email"
|
name="email"
|
||||||
@@ -115,19 +147,12 @@ function InstallationForm() {
|
|||||||
margin="dense"
|
margin="dense"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
data-test="email-text-field"
|
data-test="email-text-field"
|
||||||
error={touchedFields.email && !!errors?.email}
|
error={!!errors?.email}
|
||||||
helperText={
|
helperText={errors?.email?.message}
|
||||||
touchedFields.email && errors?.email?.message
|
|
||||||
? formatMessage(errors?.email?.message, {
|
|
||||||
inputName: formatMessage(
|
|
||||||
'installationForm.emailFieldLabel',
|
|
||||||
),
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
required
|
required
|
||||||
readOnly={install.isSuccess}
|
readOnly={isSuccess}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
label={formatMessage('installationForm.passwordFieldLabel')}
|
label={formatMessage('installationForm.passwordFieldLabel')}
|
||||||
name="password"
|
name="password"
|
||||||
@@ -135,19 +160,12 @@ function InstallationForm() {
|
|||||||
margin="dense"
|
margin="dense"
|
||||||
type="password"
|
type="password"
|
||||||
data-test="password-text-field"
|
data-test="password-text-field"
|
||||||
error={touchedFields.password && !!errors?.password}
|
error={!!errors?.password}
|
||||||
helperText={
|
helperText={errors?.password?.message}
|
||||||
touchedFields.password && errors?.password?.message
|
|
||||||
? formatMessage(errors?.password?.message, {
|
|
||||||
inputName: formatMessage(
|
|
||||||
'installationForm.passwordFieldLabel',
|
|
||||||
),
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
required
|
required
|
||||||
readOnly={install.isSuccess}
|
readOnly={isSuccess}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
label={formatMessage(
|
label={formatMessage(
|
||||||
'installationForm.confirmPasswordFieldLabel',
|
'installationForm.confirmPasswordFieldLabel',
|
||||||
@@ -157,52 +175,53 @@ function InstallationForm() {
|
|||||||
margin="dense"
|
margin="dense"
|
||||||
type="password"
|
type="password"
|
||||||
data-test="repeat-password-text-field"
|
data-test="repeat-password-text-field"
|
||||||
error={touchedFields.confirmPassword && !!errors?.confirmPassword}
|
error={!!errors?.confirmPassword}
|
||||||
helperText={
|
helperText={errors?.confirmPassword?.message}
|
||||||
touchedFields.confirmPassword &&
|
|
||||||
errors?.confirmPassword?.message
|
|
||||||
? formatMessage(errors?.confirmPassword?.message, {
|
|
||||||
inputName: formatMessage(
|
|
||||||
'installationForm.confirmPasswordFieldLabel',
|
|
||||||
),
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
required
|
required
|
||||||
readOnly={install.isSuccess}
|
readOnly={isSuccess}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{errors?.root?.general && (
|
||||||
|
<Alert data-test="error-alert" severity="error" sx={{ mt: 2 }}>
|
||||||
|
{errors.root.general.message}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isSuccess && (
|
||||||
|
<Alert
|
||||||
|
data-test="success-alert"
|
||||||
|
severity="success"
|
||||||
|
sx={{ mt: 2 }}
|
||||||
|
>
|
||||||
|
{formatMessage('installationForm.success', {
|
||||||
|
link: (str) => (
|
||||||
|
<Link
|
||||||
|
component={RouterLink}
|
||||||
|
to={URLS.LOGIN}
|
||||||
|
onClick={handleOnRedirect}
|
||||||
|
replace
|
||||||
|
>
|
||||||
|
{str}
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
sx={{ boxShadow: 2, mt: 3 }}
|
sx={{ boxShadow: 2, mt: 2 }}
|
||||||
loading={install.isPending}
|
loading={isPending}
|
||||||
disabled={install.isSuccess}
|
disabled={isSuccess}
|
||||||
fullWidth
|
fullWidth
|
||||||
data-test="signUp-button"
|
data-test="installation-button"
|
||||||
>
|
>
|
||||||
{formatMessage('installationForm.submit')}
|
{formatMessage('installationForm.submit')}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{install.isSuccess && (
|
|
||||||
<Alert data-test="success-alert" severity="success" sx={{ mt: 3 }}>
|
|
||||||
{formatMessage('installationForm.success', {
|
|
||||||
link: (str) => (
|
|
||||||
<Link
|
|
||||||
component={RouterLink}
|
|
||||||
to={URLS.LOGIN}
|
|
||||||
onClick={handleOnRedirect}
|
|
||||||
replace
|
|
||||||
>
|
|
||||||
{str}
|
|
||||||
</Link>
|
|
||||||
),
|
|
||||||
})}
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ const PermissionCatalogField = ({
|
|||||||
if (isPermissionCatalogLoading) return <PermissionCatalogFieldLoader />;
|
if (isPermissionCatalogLoading) return <PermissionCatalogFieldLoader />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableContainer data-test="permissions-catalog" component={Paper}>
|
<TableContainer component={Paper}>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
@@ -3,33 +3,52 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import LoadingButton from '@mui/lab/LoadingButton';
|
import LoadingButton from '@mui/lab/LoadingButton';
|
||||||
|
import Alert from '@mui/material/Alert';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
|
|
||||||
|
import { getGeneralErrorMessage } from 'helpers/errors';
|
||||||
import useAuthentication from 'hooks/useAuthentication';
|
import useAuthentication from 'hooks/useAuthentication';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import Form from 'components/Form';
|
import Form from 'components/Form';
|
||||||
import TextField from 'components/TextField';
|
import TextField from 'components/TextField';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useCreateAccessToken from 'hooks/useCreateAccessToken';
|
import useCreateAccessToken from 'hooks/useCreateAccessToken';
|
||||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
|
||||||
import useRegisterUser from 'hooks/useRegisterUser';
|
import useRegisterUser from 'hooks/useRegisterUser';
|
||||||
|
|
||||||
const validationSchema = yup.object().shape({
|
const getValidationSchema = (formatMessage) => {
|
||||||
fullName: yup.string().trim().required('signupForm.mandatoryInput'),
|
const getMandatoryInputMessage = (inputNameId) =>
|
||||||
email: yup
|
formatMessage('signupForm.mandatoryInput', {
|
||||||
.string()
|
inputName: formatMessage(inputNameId),
|
||||||
.trim()
|
});
|
||||||
.email('signupForm.validateEmail')
|
|
||||||
.required('signupForm.mandatoryInput'),
|
|
||||||
password: yup.string().required('signupForm.mandatoryInput'),
|
|
||||||
confirmPassword: yup
|
|
||||||
.string()
|
|
||||||
.required('signupForm.mandatoryInput')
|
|
||||||
.oneOf([yup.ref('password')], 'signupForm.passwordsMustMatch'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const initialValues = {
|
return yup.object().shape({
|
||||||
|
fullName: yup
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.required(getMandatoryInputMessage('signupForm.fullNameFieldLabel')),
|
||||||
|
email: yup
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.required(getMandatoryInputMessage('signupForm.emailFieldLabel'))
|
||||||
|
.email(formatMessage('signupForm.validateEmail')),
|
||||||
|
password: yup
|
||||||
|
.string()
|
||||||
|
.required(getMandatoryInputMessage('signupForm.passwordFieldLabel'))
|
||||||
|
.min(6, formatMessage('signupForm.passwordMinLength')),
|
||||||
|
confirmPassword: yup
|
||||||
|
.string()
|
||||||
|
.required(
|
||||||
|
getMandatoryInputMessage('signupForm.confirmPasswordFieldLabel'),
|
||||||
|
)
|
||||||
|
.oneOf(
|
||||||
|
[yup.ref('password')],
|
||||||
|
formatMessage('signupForm.passwordsMustMatch'),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultValues = {
|
||||||
fullName: '',
|
fullName: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
@@ -40,7 +59,6 @@ function SignUpForm() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const authentication = useAuthentication();
|
const authentication = useAuthentication();
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const enqueueSnackbar = useEnqueueSnackbar();
|
|
||||||
const { mutateAsync: registerUser, isPending: isRegisterUserPending } =
|
const { mutateAsync: registerUser, isPending: isRegisterUserPending } =
|
||||||
useRegisterUser();
|
useRegisterUser();
|
||||||
const { mutateAsync: createAccessToken, isPending: loginLoading } =
|
const { mutateAsync: createAccessToken, isPending: loginLoading } =
|
||||||
@@ -52,7 +70,7 @@ function SignUpForm() {
|
|||||||
}
|
}
|
||||||
}, [authentication.isAuthenticated]);
|
}, [authentication.isAuthenticated]);
|
||||||
|
|
||||||
const handleSubmit = async (values) => {
|
const handleSubmit = async (values, e, setError) => {
|
||||||
try {
|
try {
|
||||||
const { fullName, email, password } = values;
|
const { fullName, email, password } = values;
|
||||||
await registerUser({
|
await registerUser({
|
||||||
@@ -67,25 +85,28 @@ function SignUpForm() {
|
|||||||
const { token } = data;
|
const { token } = data;
|
||||||
authentication.updateToken(token);
|
authentication.updateToken(token);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errors = error?.response?.data?.errors
|
const errors = error?.response?.data?.errors;
|
||||||
? Object.values(error.response.data.errors)
|
if (errors) {
|
||||||
: [];
|
const fieldNames = Object.keys(defaultValues);
|
||||||
|
Object.entries(errors).forEach(([fieldName, fieldErrors]) => {
|
||||||
|
if (fieldNames.includes(fieldName) && Array.isArray(fieldErrors)) {
|
||||||
|
setError(fieldName, {
|
||||||
|
type: 'fieldRequestError',
|
||||||
|
message: fieldErrors.join(', '),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (errors.length) {
|
const generalError = getGeneralErrorMessage({
|
||||||
for (const [error] of errors) {
|
error,
|
||||||
enqueueSnackbar(error, {
|
fallbackMessage: formatMessage('signupForm.error'),
|
||||||
variant: 'error',
|
});
|
||||||
SnackbarProps: {
|
|
||||||
'data-test': 'snackbar-sign-up-error',
|
if (generalError) {
|
||||||
},
|
setError('root.general', {
|
||||||
});
|
type: 'requestError',
|
||||||
}
|
message: generalError,
|
||||||
} else {
|
|
||||||
enqueueSnackbar(error?.message || formatMessage('signupForm.error'), {
|
|
||||||
variant: 'error',
|
|
||||||
SnackbarProps: {
|
|
||||||
'data-test': 'snackbar-sign-up-error',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,11 +129,13 @@ function SignUpForm() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
defaultValues={initialValues}
|
automaticValidation={false}
|
||||||
|
noValidate
|
||||||
|
defaultValues={defaultValues}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
resolver={yupResolver(validationSchema)}
|
resolver={yupResolver(getValidationSchema(formatMessage))}
|
||||||
mode="onChange"
|
mode="onChange"
|
||||||
render={({ formState: { errors, touchedFields } }) => (
|
render={({ formState: { errors } }) => (
|
||||||
<>
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
label={formatMessage('signupForm.fullNameFieldLabel')}
|
label={formatMessage('signupForm.fullNameFieldLabel')}
|
||||||
@@ -121,14 +144,9 @@ function SignUpForm() {
|
|||||||
margin="dense"
|
margin="dense"
|
||||||
autoComplete="fullName"
|
autoComplete="fullName"
|
||||||
data-test="fullName-text-field"
|
data-test="fullName-text-field"
|
||||||
error={touchedFields.fullName && !!errors?.fullName}
|
error={!!errors?.fullName}
|
||||||
helperText={
|
helperText={errors?.fullName?.message}
|
||||||
touchedFields.fullName && errors?.fullName?.message
|
required
|
||||||
? formatMessage(errors?.fullName?.message, {
|
|
||||||
inputName: formatMessage('signupForm.fullNameFieldLabel'),
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
@@ -138,14 +156,9 @@ function SignUpForm() {
|
|||||||
margin="dense"
|
margin="dense"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
data-test="email-text-field"
|
data-test="email-text-field"
|
||||||
error={touchedFields.email && !!errors?.email}
|
error={!!errors?.email}
|
||||||
helperText={
|
helperText={errors?.email?.message}
|
||||||
touchedFields.email && errors?.email?.message
|
required
|
||||||
? formatMessage(errors?.email?.message, {
|
|
||||||
inputName: formatMessage('signupForm.emailFieldLabel'),
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
@@ -154,14 +167,9 @@ function SignUpForm() {
|
|||||||
fullWidth
|
fullWidth
|
||||||
margin="dense"
|
margin="dense"
|
||||||
type="password"
|
type="password"
|
||||||
error={touchedFields.password && !!errors?.password}
|
error={!!errors?.password}
|
||||||
helperText={
|
helperText={errors?.password?.message}
|
||||||
touchedFields.password && errors?.password?.message
|
required
|
||||||
? formatMessage(errors?.password?.message, {
|
|
||||||
inputName: formatMessage('signupForm.passwordFieldLabel'),
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
@@ -170,19 +178,21 @@ function SignUpForm() {
|
|||||||
fullWidth
|
fullWidth
|
||||||
margin="dense"
|
margin="dense"
|
||||||
type="password"
|
type="password"
|
||||||
error={touchedFields.confirmPassword && !!errors?.confirmPassword}
|
error={!!errors?.confirmPassword}
|
||||||
helperText={
|
helperText={errors?.confirmPassword?.message}
|
||||||
touchedFields.confirmPassword &&
|
required
|
||||||
errors?.confirmPassword?.message
|
|
||||||
? formatMessage(errors?.confirmPassword?.message, {
|
|
||||||
inputName: formatMessage(
|
|
||||||
'signupForm.confirmPasswordFieldLabel',
|
|
||||||
),
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{errors?.root?.general && (
|
||||||
|
<Alert
|
||||||
|
data-test="alert-sign-up-error"
|
||||||
|
severity="error"
|
||||||
|
sx={{ mt: 2 }}
|
||||||
|
>
|
||||||
|
{errors.root.general.message}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
18
packages/web/src/helpers/errors.js
Normal file
18
packages/web/src/helpers/errors.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Helpers to extract errors received from the API
|
||||||
|
|
||||||
|
export const getGeneralErrorMessage = ({ error, fallbackMessage }) => {
|
||||||
|
if (!error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const errors = error?.response?.data?.errors;
|
||||||
|
const generalError = errors?.general;
|
||||||
|
|
||||||
|
if (generalError && Array.isArray(generalError)) {
|
||||||
|
return generalError.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!errors) {
|
||||||
|
return error?.message || fallbackMessage;
|
||||||
|
}
|
||||||
|
};
|
@@ -138,6 +138,7 @@
|
|||||||
"installationForm.submit": "Create admin",
|
"installationForm.submit": "Create admin",
|
||||||
"installationForm.validateEmail": "Email must be valid.",
|
"installationForm.validateEmail": "Email must be valid.",
|
||||||
"installationForm.passwordsMustMatch": "Passwords must match.",
|
"installationForm.passwordsMustMatch": "Passwords must match.",
|
||||||
|
"installationForm.passwordMinLength": "Password must be at least 6 characters long.",
|
||||||
"installationForm.mandatoryInput": "{inputName} is required.",
|
"installationForm.mandatoryInput": "{inputName} is required.",
|
||||||
"installationForm.success": "The admin account has been created, and thus, the installation has been completed. You can now log in <link>here</link>.",
|
"installationForm.success": "The admin account has been created, and thus, the installation has been completed. You can now log in <link>here</link>.",
|
||||||
"installationForm.error": "Something went wrong. Please try again.",
|
"installationForm.error": "Something went wrong. Please try again.",
|
||||||
@@ -149,6 +150,7 @@
|
|||||||
"signupForm.submit": "Sign up",
|
"signupForm.submit": "Sign up",
|
||||||
"signupForm.validateEmail": "Email must be valid.",
|
"signupForm.validateEmail": "Email must be valid.",
|
||||||
"signupForm.passwordsMustMatch": "Passwords must match.",
|
"signupForm.passwordsMustMatch": "Passwords must match.",
|
||||||
|
"signupForm.passwordMinLength": "Password must be at least 6 characters long.",
|
||||||
"signupForm.mandatoryInput": "{inputName} is required.",
|
"signupForm.mandatoryInput": "{inputName} is required.",
|
||||||
"signupForm.error": "Something went wrong. Please try again.",
|
"signupForm.error": "Something went wrong. Please try again.",
|
||||||
"loginForm.title": "Login",
|
"loginForm.title": "Login",
|
||||||
|
@@ -66,8 +66,8 @@ function RoleMappings({ provider, providerLoading }) {
|
|||||||
const enqueueSnackbar = useEnqueueSnackbar();
|
const enqueueSnackbar = useEnqueueSnackbar();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: updateSamlAuthProvidersRoleMappings,
|
mutateAsync: updateRoleMappings,
|
||||||
isPending: isUpdateSamlAuthProvidersRoleMappingsPending,
|
isPending: isUpdateRoleMappingsPending,
|
||||||
} = useAdminUpdateSamlAuthProviderRoleMappings(provider?.id);
|
} = useAdminUpdateSamlAuthProviderRoleMappings(provider?.id);
|
||||||
|
|
||||||
const { data, isLoading: isAdminSamlAuthProviderRoleMappingsLoading } =
|
const { data, isLoading: isAdminSamlAuthProviderRoleMappingsLoading } =
|
||||||
@@ -79,7 +79,7 @@ function RoleMappings({ provider, providerLoading }) {
|
|||||||
const handleRoleMappingsUpdate = async (values) => {
|
const handleRoleMappingsUpdate = async (values) => {
|
||||||
try {
|
try {
|
||||||
if (provider?.id) {
|
if (provider?.id) {
|
||||||
await updateSamlAuthProvidersRoleMappings(
|
await updateRoleMappings(
|
||||||
values.roleMappings.map(({ roleId, remoteRoleName }) => ({
|
values.roleMappings.map(({ roleId, remoteRoleName }) => ({
|
||||||
roleId,
|
roleId,
|
||||||
remoteRoleName,
|
remoteRoleName,
|
||||||
@@ -148,7 +148,7 @@ function RoleMappings({ provider, providerLoading }) {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
sx={{ boxShadow: 2 }}
|
sx={{ boxShadow: 2 }}
|
||||||
loading={isUpdateSamlAuthProvidersRoleMappingsPending}
|
loading={isUpdateRoleMappingsPending}
|
||||||
>
|
>
|
||||||
{formatMessage('roleMappingsForm.save')}
|
{formatMessage('roleMappingsForm.save')}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
|
Reference in New Issue
Block a user