mirror of
https://github.com/lingble/twenty.git
synced 2025-10-30 12:22:29 +00:00
feat: add integration tests (#6923)
### Summary This PR introduces several integration tests, a mix of manually written tests and those generated using the `generate-integration-tests` Python script located in the `scripts` folder. ### Tests Added: - **Authentication tests**: Validating login, registration, and token handling. - **FindMany queries**: Fetching multiple records for all existing entities that do not require input arguments. ### How the Integration Tests Work: - A `setupTest` function is called during the Jest test run. This function initializes a test instance of the application and exposes it on a dedicated port. - Since tests are executed in isolated workers, they do not have direct access to the in-memory app instance. Instead, the tests query the application through the exposed port. - A static accessToken is used, this one as a big expiration time so it will never expire (365 years) - The queries are executed, and the results are validated against expected outcomes. ### Current State and Next Steps: - These tests currently run using the existing development seed data. We plan to introduce more comprehensive test data using `faker` to improve coverage. - At the moment, the only mutation tests implemented are for authentication. Future updates should include broader mutation testing for other entities. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
75
.github/workflows/ci-server.yaml
vendored
75
.github/workflows/ci-server.yaml
vendored
@@ -17,7 +17,7 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
server-test:
|
server-setup:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
|
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
|
||||||
@@ -29,6 +29,10 @@ jobs:
|
|||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
redis:
|
||||||
|
image: redis
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
steps:
|
steps:
|
||||||
- name: Fetch custom Github Actions and base branch history
|
- name: Fetch custom Github Actions and base branch history
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -36,7 +40,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
uses: ./.github/workflows/actions/yarn-install
|
uses: ./.github/workflows/actions/yarn-install
|
||||||
- name: Server / Restore Tasks Cache
|
- name: Server / Restore Task Cache
|
||||||
uses: ./.github/workflows/actions/task-cache
|
uses: ./.github/workflows/actions/task-cache
|
||||||
with:
|
with:
|
||||||
tag: scope:backend
|
tag: scope:backend
|
||||||
@@ -45,14 +49,71 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tag: scope:backend
|
tag: scope:backend
|
||||||
tasks: lint,typecheck
|
tasks: lint,typecheck
|
||||||
- name: Server / Run tests
|
|
||||||
uses: ./.github/workflows/actions/nx-affected
|
|
||||||
with:
|
|
||||||
tag: scope:backend
|
|
||||||
tasks: test
|
|
||||||
- name: Server / Build
|
- name: Server / Build
|
||||||
run: npx nx build twenty-server
|
run: npx nx build twenty-server
|
||||||
- name: Server / Write .env
|
- name: Server / Write .env
|
||||||
run: npx nx reset:env twenty-server
|
run: npx nx reset:env twenty-server
|
||||||
- name: Worker / Run
|
- name: Worker / Run
|
||||||
run: MESSAGE_QUEUE_TYPE=sync npx nx worker twenty-server
|
run: MESSAGE_QUEUE_TYPE=sync npx nx worker twenty-server
|
||||||
|
|
||||||
|
server-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: server-setup
|
||||||
|
env:
|
||||||
|
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
|
||||||
|
steps:
|
||||||
|
- name: Fetch custom Github Actions and base branch history
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Install dependencies
|
||||||
|
uses: ./.github/workflows/actions/yarn-install
|
||||||
|
- name: Server / Restore Task Cache
|
||||||
|
uses: ./.github/workflows/actions/task-cache
|
||||||
|
with:
|
||||||
|
tag: scope:backend
|
||||||
|
- name: Server / Run Tests
|
||||||
|
uses: ./.github/workflows/actions/nx-affected
|
||||||
|
with:
|
||||||
|
tag: scope:backend
|
||||||
|
tasks: test
|
||||||
|
|
||||||
|
server-integration-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: server-setup
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: twentycrm/twenty-postgres
|
||||||
|
env:
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
redis:
|
||||||
|
image: redis
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
env:
|
||||||
|
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
|
||||||
|
steps:
|
||||||
|
- name: Fetch custom Github Actions and base branch history
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Install dependencies
|
||||||
|
uses: ./.github/workflows/actions/yarn-install
|
||||||
|
- name: Server / Restore Task Cache
|
||||||
|
uses: ./.github/workflows/actions/task-cache
|
||||||
|
with:
|
||||||
|
tag: scope:backend
|
||||||
|
- name: Server / Run Integration Tests
|
||||||
|
uses: ./.github/workflows/actions/nx-affected
|
||||||
|
with:
|
||||||
|
tag: scope:backend
|
||||||
|
tasks: "test:integration"
|
||||||
|
- name: Server / Upload reset-logs file
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: reset-logs
|
||||||
|
path: reset-logs.log
|
||||||
|
|||||||
4
.vscode/twenty.code-workspace
vendored
4
.vscode/twenty.code-workspace
vendored
@@ -20,6 +20,10 @@
|
|||||||
"name": "packages/twenty-ui",
|
"name": "packages/twenty-ui",
|
||||||
"path": "../packages/twenty-ui"
|
"path": "../packages/twenty-ui"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "packages/twenty-emails",
|
||||||
|
"path": "../packages/twenty-emails"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "packages/twenty-postgres",
|
"name": "packages/twenty-postgres",
|
||||||
"path": "../packages/twenty-postgres"
|
"path": "../packages/twenty-postgres"
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
|
PG_DATABASE_URL=postgres://twenty:twenty@localhost:5432/test
|
||||||
|
|
||||||
DEBUG_MODE=true
|
DEBUG_MODE=true
|
||||||
PG_DATABASE_URL=postgres://twenty:twenty@localhost:5432/test?connection_limit=1
|
DEBUG_PORT=9000
|
||||||
# Use this for docker setup
|
|
||||||
# PG_DATABASE_URL=postgres://twenty:twenty@postgres:5432/default?connection_limit=1
|
|
||||||
|
|
||||||
# the URL of the front-end app
|
|
||||||
FRONT_BASE_URL=http://localhost:3001
|
FRONT_BASE_URL=http://localhost:3001
|
||||||
# random keys used to generate JWT tokens
|
ACCESS_TOKEN_SECRET=replace_me_with_a_random_string_access
|
||||||
ACCESS_TOKEN_SECRET=secret_jwt
|
LOGIN_TOKEN_SECRET=replace_me_with_a_random_string_login
|
||||||
LOGIN_TOKEN_SECRET=secret_login_tokens
|
REFRESH_TOKEN_SECRET=replace_me_with_a_random_string_refresh
|
||||||
REFRESH_TOKEN_SECRET=secret_refresh_token
|
SIGN_IN_PREFILLED=true
|
||||||
FILE_TOKEN_SECRET=secret_file_token
|
EXCEPTION_HANDLER_DRIVER=console
|
||||||
|
SENTRY_DSN=https://ba869cb8fd72d5faeb6643560939cee0@o4505516959793152.ingest.sentry.io/4506660900306944
|
||||||
|
DEMO_WORKSPACE_IDS=63db4589-590f-42b3-bdf1-85268b3da02f,8de58f3f-7e86-4a0b-998d-b2cbe314ee3a,4d957b72-0b37-4bad-9468-8dc828ee082d,daa0b739-269e-49b6-9be5-5f0215941489,59c15f6a-909a-4495-9cf4-3ce1b0abbb6a,7202cc9d-92da-4b52-a323-d29d38cd3b4e,5f071b0d-646b-411a-94f1-5d9ba9d5c6ac,7bc10973-897b-4767-ab2f-35cdac3b2aec,4b3ba0be-2d29-4b1e-be66-8ac7eb65d000,edfb500d-cc4e-4f22-8e2b-f139a9758a68,eee459c9-9057-4459-ae0d-d51d14c01635,3dd2f505-0075-4217-ba33-fc4244aeaaa9,3d1a9165-3f3f-494e-a99d-f858eae95144,84db6ded-cfce-4aee-9160-6553b05c8143,96fb1540-269b-4d13-af21-2a8268eff8ca,b2463e69-d121-4ea5-80c9-bba82403e93e,5af30c15-867d-49ed-b939-d4856bed8514,b5677aa1-68fa-4818-aaaa-434a07ae2ed4,1ec7fa9a-d6bf-4fa2-a753-9a235d75ee3f,753a6fa2-df27-4c87-8c90-4da78fcb30dd,2138f2f2-bbe9-41df-b483-687a9075f94e,a885cfef-4636-4c3a-9788-1ff6e6b92df5,5458f7fb-9431-47a2-b7a0-32f31d115e23,6c09929f-11c3-4f92-9508-aa0e6b934d1e,57ae0a2c-7a4e-4c7d-8f43-68548e7f1206,cc7f0b85-6868-4c2d-85c5-3ce9977ea346,21871a7f-f067-45ea-989e-44339bb5ad07,c3efedab-84f5-4656-8297-55964b3d26cb,647dcdd1-4540-4003-9f58-fd84d4d759b7,fc5e6857-8d67-47b8-98f2-edeb0671e326,1ad8d72c-1826-40ed-8b44-d15a1d2aab70,eac6c90a-d25d-4c8c-a053-cfbc7cde0afb,023a70de-a85e-43fc-bbc6-757fbf6562f0,f3f0a7fb-1409-443b-8e39-4e58e628796e,62828804-97d4-40ec-82fa-2992a6ce4a81,af5441fe-b16f-4996-87f4-1a433ec53dd6,e8857860-f7b1-4478-9741-1eb9e7c11f2c,6bca9c44-c8c0-49f8-b0b5-1bb2ca7842b8,d50da092-09df-448f-84ea-3ebddfe1d9f6,9efd5d6d-db64-47d4-9ad3-5e4d8b65ff7f,6f089094-2dd2-4b0e-b5b7-8bb52b93ea8e,299b0822-68e9-4bfa-af35-da799012e80e,a3dd579c-93be-45a0-ad35-f518d8ed45dd,023b1b3e-4891-4061-aae0-f34368644f40,50174445-33c5-4482-bb8c-3ef6c511c8cd,9933c048-07a7-4735-9af2-940c2f9b6683,beadc568-3962-46bd-ad4d-06e23b37615b,0cdafc9f-d4c1-4576-837e-d7f6ec28643d,50bb24ce-1709-4928-a87b-d9d9e147a2ab,7690ed72-910d-4357-8e0e-17aa702b0b94,1ad0d69f-60fa-414f-bf79-4f94c2abba43,946d84a4-db4d-48cb-a5d3-03081b5c7e8e,1a080055-d2bf-4b14-8957-88a7d08769b8,ed343e38-e405-4fae-9486-27b09c98bdad,c8bdef75-a98c-4646-a372-3251340d2dea,87a8c6fa-f93e-4950-aff2-5f956ca1a6ba,604781ba-23c2-4220-a717-b5615431fcd9,31af6841-ad9f-4f28-a637-b5c5e6589447,cf067451-7b88-4ff2-a96d-3fc9c5d6fea0,26a8ad5e-29d9-4e7d-aa1f-e6221e8ea32a,fd14db29-e4df-44a7-9b3f-d00384458122,73b477a8-fcf4-4860-a685-65a0a79b8653,82e0f305-4c6c-4160-be1d-b0de834124e6,e38567ab-a6e2-4a94-99c5-a7db31c0aae8,faf3d6dc-66ff-4c1b-9658-f65a9cd9fcf1,6df6bb90-200e-4290-b73d-9bb374554229,2ff10cf4-a871-404a-9e7b-5ca7a232567e,06c614e2-0f36-4b72-8c82-59631680add2,5e508c81-3453-4185-ae8c-4c9b841f8c15,21b5c371-6010-4b1b-be67-7538eb877efb,54e61442-e291-4eea-8d49-7f11b5f85bd2,b6b7260a-4eea-40b0-9f7f-1dfd4c3cc7a8,e163fe76-30fb-44fb-b51a-50cc78745a21,4da672f2-29b4-4a98-b27c-b39a4aecc858,2fdb0601-c882-4aaf-ad49-ae17e530d47a,49525e1b-1b47-4545-a98c-0ba58778179f,f958ab32-b152-4004-9228-18148f7380f1,0ff5025a-62cd-4a10-a722-79f7cf360f01,642df445-e314-409a-a97d-64fc2aa2a15e,38b0dab5-d4fb-44f9-8cf9-bb35cf82e91d,62054133-f35a-4f64-a2ee-a31e48952835,536dbe8c-af33-4eab-a0a8-8d039a00db40,a04998ba-52c9-4538-b6d9-6d04408dbaf2,89016c7a-3d36-4619-a5c6-4f31795eebf7,7708b9a9-776c-46fc-94a4-dc28e7880958,5c92bc69-b328-4c66-a791-a05dbaf7a6f8,ad580a50-80b4-44be-9bc4-f2b57cd23207,36c0241c-891e-4b74-bd10-5e99df96bbc8,a96842ff-18be-4536-a23d-20d973d91621,0ea549b0-9558-4bdf-9944-5abc707c7660,0186c353-5ed2-4c94-b71a-fc0b48c90288,1508a165-2217-4911-b31c-1ea42a08f097,1731e392-dfdf-4fc4-863b-27ae62b0e374,0b245cea-96a6-4a3a-af6a-ef43496c239c,a844e208-7078-43a2-8bd0-86f31498cd3f,53d112b5-87f2-490b-a788-df1f4624f9ad,0d5794d4-3a52-482b-9a6a-f8185018bad1,df877aa6-231c-47fb-9be0-906e61677356,c56c6d1a-3418-49d2-82ce-bd9370668043,6e0b6f34-3cd0-4aa0-ae1f-25f5545dca68
|
||||||
|
MUTATION_MAXIMUM_RECORD_AFFECTED=100
|
||||||
|
MESSAGE_QUEUE_TYPE=pg-boss
|
||||||
|
CACHE_STORAGE_TYPE=redis
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_USERNAME=default
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
# ———————— Optional ————————
|
AUTH_GOOGLE_ENABLED=false
|
||||||
# DEBUG_MODE=false
|
MESSAGING_PROVIDER_GMAIL_ENABLED=false
|
||||||
# SIGN_IN_PREFILLED=false
|
CALENDAR_PROVIDER_GOOGLE_ENABLED=false
|
||||||
# ACCESS_TOKEN_EXPIRES_IN=30m
|
AUTH_GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/redirect
|
||||||
# LOGIN_TOKEN_EXPIRES_IN=15m
|
AUTH_GOOGLE_APIS_CALLBACK_URL=http://localhost:3000/auth/google-apis/get-access-token
|
||||||
# REFRESH_TOKEN_EXPIRES_IN=90d
|
MESSAGING_PROVIDER_GMAIL_CALLBACK_URL=http://localhost:3000/auth/google-gmail/get-access-token
|
||||||
# FILE_TOKEN_EXPIRES_IN=1d
|
|
||||||
# FRONT_AUTH_CALLBACK_URL=http://localhost:3001/verify
|
|
||||||
# AUTH_GOOGLE_ENABLED=false
|
|
||||||
# MESSAGING_PROVIDER_GMAIL_ENABLED=false
|
|
||||||
# STORAGE_TYPE=local
|
|
||||||
# STORAGE_LOCAL_PATH=.local-storage
|
|
||||||
# MUTATION_MAXIMUM_AFFECTED_RECORDS=100
|
|
||||||
|
|||||||
@@ -93,5 +93,11 @@ module.exports = {
|
|||||||
'@nx/workspace-inject-workspace-repository': 'warn',
|
'@nx/workspace-inject-workspace-repository': 'warn',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
files: ['scripts/**/*.ts'],
|
||||||
|
parserOptions: {
|
||||||
|
project: ['packages/twenty-server/tsconfig.scripts.json'],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
1
packages/twenty-server/.gitignore
vendored
1
packages/twenty-server/.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
dist/*
|
dist/*
|
||||||
.local-storage
|
.local-storage
|
||||||
logs/**/*
|
logs/**/*
|
||||||
|
*.log
|
||||||
|
|||||||
17
packages/twenty-server/@types/jest.d.ts
vendored
Normal file
17
packages/twenty-server/@types/jest.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'jest';
|
||||||
|
|
||||||
|
declare module '@jest/types' {
|
||||||
|
namespace Config {
|
||||||
|
interface ConfigGlobals {
|
||||||
|
APP_PORT: number;
|
||||||
|
ACCESS_TOKEN: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
const APP_PORT: number;
|
||||||
|
const ACCESS_TOKEN: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
34
packages/twenty-server/jest-integration.config.ts
Normal file
34
packages/twenty-server/jest-integration.config.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { JestConfigWithTsJest, pathsToModuleNameMapper } from 'ts-jest';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const tsConfig = require('./tsconfig.json');
|
||||||
|
|
||||||
|
const jestConfig: JestConfigWithTsJest = {
|
||||||
|
silent: false,
|
||||||
|
verbose: true,
|
||||||
|
moduleFileExtensions: ['js', 'json', 'ts'],
|
||||||
|
rootDir: '.',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
testRegex: '.integration-spec.ts$',
|
||||||
|
modulePathIgnorePatterns: ['<rootDir>/dist'],
|
||||||
|
globalSetup: '<rootDir>/test/utils/setup-test.ts',
|
||||||
|
globalTeardown: '<rootDir>/test/utils/teardown-test.ts',
|
||||||
|
testTimeout: 15000,
|
||||||
|
moduleNameMapper: {
|
||||||
|
...pathsToModuleNameMapper(tsConfig.compilerOptions.paths),
|
||||||
|
'twenty-emails': '<rootDir>/../twenty-emails/dist/index.js',
|
||||||
|
},
|
||||||
|
fakeTimers: {
|
||||||
|
enableGlobally: true,
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
'^.+\\.(t|j)s$': 'ts-jest',
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
APP_PORT: 4000,
|
||||||
|
ACCESS_TOKEN:
|
||||||
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwiaWF0IjoxNzI2NDkyNTAyLCJleHAiOjEzMjQ1MDE2NTAyfQ.zM6TbfeOqYVH5Sgryc2zf02hd9uqUOSL1-iJlMgwzsI',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default jestConfig;
|
||||||
@@ -1,22 +1,27 @@
|
|||||||
module.exports = {
|
import { JestConfigWithTsJest } from 'ts-jest';
|
||||||
|
|
||||||
|
const jestConfig: JestConfigWithTsJest = {
|
||||||
// to enable logs, comment out the following line
|
// to enable logs, comment out the following line
|
||||||
silent: true,
|
silent: true,
|
||||||
clearMocks: true,
|
clearMocks: true,
|
||||||
preset: 'ts-jest',
|
displayName: 'twenty-server',
|
||||||
testEnvironment: 'node',
|
|
||||||
modulePathIgnorePatterns: ['<rootDir>/dist'],
|
|
||||||
moduleFileExtensions: ['js', 'json', 'ts'],
|
|
||||||
moduleNameMapper: {
|
|
||||||
'^src/(.*)': '<rootDir>/src/$1',
|
|
||||||
},
|
|
||||||
rootDir: './',
|
rootDir: './',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
transformIgnorePatterns: ['../../node_modules/'],
|
||||||
testRegex: '.*\\.spec\\.ts$',
|
testRegex: '.*\\.spec\\.ts$',
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.(t|j)s$': 'ts-jest',
|
'^.+\\.(t|j)s$': 'ts-jest',
|
||||||
},
|
},
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^src/(.*)': '<rootDir>/src/$1',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['js', 'json', 'ts'],
|
||||||
|
modulePathIgnorePatterns: ['<rootDir>/dist'],
|
||||||
fakeTimers: {
|
fakeTimers: {
|
||||||
enableGlobally: true,
|
enableGlobally: true,
|
||||||
},
|
},
|
||||||
collectCoverageFrom: ['**/*.(t|j)s'],
|
collectCoverageFrom: ['**/*.(t|j)s'],
|
||||||
coverageDirectory: '../coverage',
|
coverageDirectory: '../coverage',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default jestConfig;
|
||||||
|
|||||||
@@ -11,6 +11,16 @@
|
|||||||
"commands": ["rimraf dist", "nest build --path ./tsconfig.build.json"]
|
"commands": ["rimraf dist", "nest build --path ./tsconfig.build.json"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"test:integration": {
|
||||||
|
"executor": "nx:run-commands",
|
||||||
|
"options": {
|
||||||
|
"cwd": "packages/twenty-server",
|
||||||
|
"commands": [
|
||||||
|
"NODE_ENV=test nx database:reset > reset-logs.log && NODE_ENV=test nx jest --config ./jest-integration.config.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"parallel": false
|
||||||
|
},
|
||||||
"build:packageJson": {
|
"build:packageJson": {
|
||||||
"executor": "@nx/js:tsc",
|
"executor": "@nx/js:tsc",
|
||||||
"options": {
|
"options": {
|
||||||
@@ -108,12 +118,11 @@
|
|||||||
"command": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register ../../node_modules/.bin/jest --runInBand"
|
"command": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register ../../node_modules/.bin/jest --runInBand"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test:e2e": {
|
"jest": {
|
||||||
"executor": "nx:run-commands",
|
"executor": "nx:run-commands",
|
||||||
"dependsOn": ["build"],
|
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "packages/twenty-server",
|
"cwd": "packages/twenty-server",
|
||||||
"command": "./scripts/run-integration.sh"
|
"command": "jest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"database:migrate": {
|
"database:migrate": {
|
||||||
@@ -140,6 +149,16 @@
|
|||||||
"parallel": false
|
"parallel": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"generate:integration-test": {
|
||||||
|
"executor": "nx:run-commands",
|
||||||
|
"options": {
|
||||||
|
"cwd": "packages/twenty-server",
|
||||||
|
"commands": [
|
||||||
|
"nx ts-node-no-deps -- ./scripts/generate-integration-tests/index.ts"
|
||||||
|
],
|
||||||
|
"parallel": false
|
||||||
|
}
|
||||||
|
},
|
||||||
"database:reset": {
|
"database:reset": {
|
||||||
"executor": "nx:run-commands",
|
"executor": "nx:run-commands",
|
||||||
"dependsOn": ["build"],
|
"dependsOn": ["build"],
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as process from 'process';
|
||||||
|
|
||||||
|
import { INTROSPECTION_QUERY } from './introspection-query';
|
||||||
|
import {
|
||||||
|
Field,
|
||||||
|
InputValue,
|
||||||
|
IntrospectionResponse,
|
||||||
|
TypeRef,
|
||||||
|
} from './introspection.interface';
|
||||||
|
|
||||||
|
const GRAPHQL_URL = 'http://localhost:3000/graphql';
|
||||||
|
const BEARER_TOKEN =
|
||||||
|
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwiaWF0IjoxNzI2NDkyNTAyLCJleHAiOjEzMjQ1MDE2NTAyfQ.zM6TbfeOqYVH5Sgryc2zf02hd9uqUOSL1-iJlMgwzsI';
|
||||||
|
const TEST_OUTPUT_DIR = './test';
|
||||||
|
|
||||||
|
const fetchGraphQLSchema = async (): Promise<IntrospectionResponse> => {
|
||||||
|
const headers = {
|
||||||
|
Authorization: BEARER_TOKEN,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
const response = await fetch(GRAPHQL_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify({ query: INTROSPECTION_QUERY }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch schema: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
const toKebabCase = (name: string): string => {
|
||||||
|
return name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
const unwrapType = (typeInfo: TypeRef): any => {
|
||||||
|
while (typeInfo.ofType) {
|
||||||
|
typeInfo = typeInfo.ofType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasRequiredArgs = (args: InputValue[]): boolean => {
|
||||||
|
return args.some((arg) => unwrapType(arg.type).kind === 'NON_NULL');
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateTestContent = (
|
||||||
|
queryName: string,
|
||||||
|
fields: Field[],
|
||||||
|
): string | null => {
|
||||||
|
const fieldNames = fields
|
||||||
|
.filter((f) => ['SCALAR', 'ENUM'].includes(unwrapType(f.type).kind))
|
||||||
|
.map((f) => f.name);
|
||||||
|
|
||||||
|
if (fieldNames.length === 0) {
|
||||||
|
console.log(`Skipping ${queryName}: No usable fields found.`);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldSelection = fieldNames.join('\n ');
|
||||||
|
const expectSelection = fieldNames
|
||||||
|
.map((f) => `expect(${queryName}).toHaveProperty('${f}');`)
|
||||||
|
.join('\n ');
|
||||||
|
|
||||||
|
return `import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(\`http://localhost:\${APP_PORT}\`);
|
||||||
|
|
||||||
|
describe('${queryName}Resolver (e2e)', () => {
|
||||||
|
it('should find many ${queryName}', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: \`
|
||||||
|
query ${queryName} {
|
||||||
|
${queryName} {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
${fieldSelection}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', \`Bearer \${ACCESS_TOKEN}\`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.${queryName};
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const ${queryName} = edges[0].node;
|
||||||
|
|
||||||
|
${expectSelection}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const writeTestFile = (
|
||||||
|
queryName: string,
|
||||||
|
content: string | null,
|
||||||
|
force = false,
|
||||||
|
): string => {
|
||||||
|
if (!content) return 'skipped';
|
||||||
|
|
||||||
|
const fileName = `${toKebabCase(queryName)}.integration-spec.ts`;
|
||||||
|
const filePath = path.join(TEST_OUTPUT_DIR, fileName);
|
||||||
|
|
||||||
|
if (fs.existsSync(filePath) && !force) {
|
||||||
|
return 'skipped';
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, content);
|
||||||
|
|
||||||
|
return force ? 'updated' : 'created';
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateTests = async (force = false) => {
|
||||||
|
fs.mkdirSync(TEST_OUTPUT_DIR, { recursive: true });
|
||||||
|
const schemaData = await fetchGraphQLSchema();
|
||||||
|
const types = schemaData.data.__schema.types;
|
||||||
|
|
||||||
|
const queryTypeName = schemaData.data.__schema.queryType.name;
|
||||||
|
const queryType = types.find((t: any) => t.name === queryTypeName);
|
||||||
|
|
||||||
|
let createdCount = 0;
|
||||||
|
let updatedCount = 0;
|
||||||
|
let totalCount = 0;
|
||||||
|
|
||||||
|
if (!queryType?.fields) {
|
||||||
|
console.log('No query fields found.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const query of queryType.fields) {
|
||||||
|
const queryName = query.name;
|
||||||
|
|
||||||
|
if (hasRequiredArgs(query.args)) continue;
|
||||||
|
if (queryName.includes('Duplicates')) continue;
|
||||||
|
|
||||||
|
const queryReturnType = unwrapType(query.type);
|
||||||
|
|
||||||
|
if (
|
||||||
|
queryReturnType.kind === 'OBJECT' &&
|
||||||
|
queryReturnType.name.includes('Connection')
|
||||||
|
) {
|
||||||
|
totalCount++;
|
||||||
|
const connectionTypeInfo = types.find(
|
||||||
|
(f: any) => f.name === queryReturnType.name,
|
||||||
|
);
|
||||||
|
const edgeTypeInfo = connectionTypeInfo?.fields?.find(
|
||||||
|
(f: any) => f.name === 'edges',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (edgeTypeInfo) {
|
||||||
|
const returnType = unwrapType(edgeTypeInfo.type);
|
||||||
|
const returnTypeInfo = types.find(
|
||||||
|
(t: any) => t.name === returnType.name,
|
||||||
|
);
|
||||||
|
const returnNodeTypeInfo = returnTypeInfo?.fields?.find(
|
||||||
|
(f: any) => f.name === 'node',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (returnNodeTypeInfo) {
|
||||||
|
const nodeType = unwrapType(returnNodeTypeInfo.type);
|
||||||
|
const nodeTypeInfo = types.find((t: any) => t.name === nodeType.name);
|
||||||
|
|
||||||
|
if (!nodeTypeInfo?.fields) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = generateTestContent(queryName, nodeTypeInfo?.fields);
|
||||||
|
const result = writeTestFile(queryName, content, force);
|
||||||
|
|
||||||
|
if (result === 'created') createdCount++;
|
||||||
|
if (result === 'updated') updatedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Number of tests created: ${createdCount}/${totalCount}`);
|
||||||
|
if (force) {
|
||||||
|
console.log(`Number of tests updated: ${updatedCount}/${totalCount}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Basic command-line argument parsing
|
||||||
|
const forceArg = process.argv.includes('--force');
|
||||||
|
|
||||||
|
// Call the function with the parsed argument
|
||||||
|
generateTests(forceArg);
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
export const INTROSPECTION_QUERY = `
|
||||||
|
query IntrospectionQuery {
|
||||||
|
__schema {
|
||||||
|
queryType { name }
|
||||||
|
mutationType { name }
|
||||||
|
subscriptionType { name }
|
||||||
|
types {
|
||||||
|
...FullType
|
||||||
|
}
|
||||||
|
directives {
|
||||||
|
name
|
||||||
|
description
|
||||||
|
locations
|
||||||
|
args {
|
||||||
|
...InputValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment FullType on __Type {
|
||||||
|
kind
|
||||||
|
name
|
||||||
|
description
|
||||||
|
fields(includeDeprecated: true) {
|
||||||
|
name
|
||||||
|
description
|
||||||
|
args {
|
||||||
|
...InputValue
|
||||||
|
}
|
||||||
|
type {
|
||||||
|
...TypeRef
|
||||||
|
}
|
||||||
|
isDeprecated
|
||||||
|
deprecationReason
|
||||||
|
}
|
||||||
|
inputFields {
|
||||||
|
...InputValue
|
||||||
|
}
|
||||||
|
interfaces {
|
||||||
|
...TypeRef
|
||||||
|
}
|
||||||
|
enumValues(includeDeprecated: true) {
|
||||||
|
name
|
||||||
|
description
|
||||||
|
isDeprecated
|
||||||
|
deprecationReason
|
||||||
|
}
|
||||||
|
possibleTypes {
|
||||||
|
...TypeRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment InputValue on __InputValue {
|
||||||
|
name
|
||||||
|
description
|
||||||
|
type { ...TypeRef }
|
||||||
|
defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment TypeRef on __Type {
|
||||||
|
kind
|
||||||
|
name
|
||||||
|
ofType {
|
||||||
|
kind
|
||||||
|
name
|
||||||
|
ofType {
|
||||||
|
kind
|
||||||
|
name
|
||||||
|
ofType {
|
||||||
|
kind
|
||||||
|
name
|
||||||
|
ofType {
|
||||||
|
kind
|
||||||
|
name
|
||||||
|
ofType {
|
||||||
|
kind
|
||||||
|
name
|
||||||
|
ofType {
|
||||||
|
kind
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
export interface IntrospectionResponse {
|
||||||
|
data: {
|
||||||
|
__schema: Schema;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Schema {
|
||||||
|
queryType: { name: string };
|
||||||
|
mutationType: { name: string | null };
|
||||||
|
subscriptionType: { name: string | null };
|
||||||
|
types: GraphQLType[];
|
||||||
|
directives: Directive[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Directive {
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
locations: string[];
|
||||||
|
args: InputValue[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GraphQLType {
|
||||||
|
kind: string;
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
fields?: Field[];
|
||||||
|
inputFields?: InputValue[];
|
||||||
|
interfaces?: TypeRef[];
|
||||||
|
enumValues?: EnumValue[];
|
||||||
|
possibleTypes?: TypeRef[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Field {
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
args: InputValue[];
|
||||||
|
type: TypeRef;
|
||||||
|
isDeprecated: boolean;
|
||||||
|
deprecationReason: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputValue {
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
type: TypeRef;
|
||||||
|
defaultValue: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TypeRef {
|
||||||
|
kind: string;
|
||||||
|
name: string | null;
|
||||||
|
ofType: TypeRef | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnumValue {
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
isDeprecated: boolean;
|
||||||
|
deprecationReason: string | null;
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# scripts/run-integration.sh
|
|
||||||
|
|
||||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
source $DIR/set-env-test.sh
|
|
||||||
|
|
||||||
npx nx database:reset
|
|
||||||
npx nx jest --config ./test/jest-e2e.json
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# scripts/set-env-test.sh
|
|
||||||
|
|
||||||
# Get script's directory
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
# Construct the absolute path of .env file in the project root directory
|
|
||||||
ENV_PATH="${SCRIPT_DIR}/../.env.test"
|
|
||||||
|
|
||||||
# Check if the file exists
|
|
||||||
if [ -f "${ENV_PATH}" ]; then
|
|
||||||
echo "🔵 - Loading environment variables from "${ENV_PATH}"..."
|
|
||||||
# Export env vars
|
|
||||||
while IFS= read -r line || [ -n "$line" ]; do
|
|
||||||
if echo "$line" | grep -F = &>/dev/null
|
|
||||||
then
|
|
||||||
varname=$(echo "$line" | cut -d '=' -f 1)
|
|
||||||
varvalue=$(echo "$line" | cut -d '=' -f 2- | cut -d '#' -f 1)
|
|
||||||
export "$varname"="$varvalue"
|
|
||||||
fi
|
|
||||||
done < <(grep -v '^#' "${ENV_PATH}")
|
|
||||||
else
|
|
||||||
echo "Error: ${ENV_PATH} does not exist."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -26,23 +26,15 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
|||||||
import { ModulesModule } from 'src/modules/modules.module';
|
import { ModulesModule } from 'src/modules/modules.module';
|
||||||
|
|
||||||
import { CoreEngineModule } from './engine/core-modules/core-engine.module';
|
import { CoreEngineModule } from './engine/core-modules/core-engine.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
// Nest.js devtools, use devtools.nestjs.com to debug
|
|
||||||
// DevtoolsModule.registerAsync({
|
|
||||||
// useFactory: (environmentService: EnvironmentService) => ({
|
|
||||||
// http: environmentService.get('DEBUG_MODE'),
|
|
||||||
// port: environmentService.get('DEBUG_PORT'),
|
|
||||||
// }),
|
|
||||||
// inject: [EnvironmentService],
|
|
||||||
// }),
|
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
|
envFilePath: process.env.NODE_ENV === 'test' ? '.env.test' : '.env',
|
||||||
}),
|
}),
|
||||||
GraphQLModule.forRootAsync<YogaDriverConfig>({
|
GraphQLModule.forRootAsync<YogaDriverConfig>({
|
||||||
driver: YogaDriver,
|
driver: YogaDriver,
|
||||||
imports: [CoreEngineModule, GraphQLConfigModule],
|
imports: [GraphQLConfigModule],
|
||||||
useClass: GraphQLConfigService,
|
useClass: GraphQLConfigService,
|
||||||
}),
|
}),
|
||||||
TwentyORMModule,
|
TwentyORMModule,
|
||||||
|
|||||||
@@ -2,18 +2,24 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
|||||||
|
|
||||||
import { config } from 'dotenv';
|
import { config } from 'dotenv';
|
||||||
import { DataSource, DataSourceOptions } from 'typeorm';
|
import { DataSource, DataSourceOptions } from 'typeorm';
|
||||||
config();
|
config({ path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env' });
|
||||||
|
|
||||||
|
const isJest = process.argv.some((arg) => arg.includes('jest'));
|
||||||
|
|
||||||
export const typeORMCoreModuleOptions: TypeOrmModuleOptions = {
|
export const typeORMCoreModuleOptions: TypeOrmModuleOptions = {
|
||||||
url: process.env.PG_DATABASE_URL,
|
url: process.env.PG_DATABASE_URL,
|
||||||
type: 'postgres',
|
type: 'postgres',
|
||||||
logging: ['error'],
|
logging: ['error'],
|
||||||
schema: 'core',
|
schema: 'core',
|
||||||
entities: ['dist/src/engine/core-modules/**/*.entity{.ts,.js}'],
|
entities: [
|
||||||
|
`${isJest ? '' : 'dist/'}src/engine/core-modules/**/*.entity{.ts,.js}`,
|
||||||
|
],
|
||||||
synchronize: false,
|
synchronize: false,
|
||||||
migrationsRun: false,
|
migrationsRun: false,
|
||||||
migrationsTableName: '_typeorm_migrations',
|
migrationsTableName: '_typeorm_migrations',
|
||||||
migrations: ['dist/src/database/typeorm/core/migrations/*{.ts,.js}'],
|
migrations: [
|
||||||
|
`${isJest ? '' : 'dist/'}src/database/typeorm/core/migrations/*{.ts,.js}`,
|
||||||
|
],
|
||||||
ssl:
|
ssl:
|
||||||
process.env.PG_SSL_ALLOW_SELF_SIGNED === 'true'
|
process.env.PG_SSL_ALLOW_SELF_SIGNED === 'true'
|
||||||
? {
|
? {
|
||||||
|
|||||||
@@ -2,18 +2,24 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
|||||||
|
|
||||||
import { config } from 'dotenv';
|
import { config } from 'dotenv';
|
||||||
import { DataSource, DataSourceOptions } from 'typeorm';
|
import { DataSource, DataSourceOptions } from 'typeorm';
|
||||||
config();
|
config({ path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env' });
|
||||||
|
|
||||||
|
const isJest = process.argv.some((arg) => arg.includes('jest'));
|
||||||
|
|
||||||
export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = {
|
export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = {
|
||||||
url: process.env.PG_DATABASE_URL,
|
url: process.env.PG_DATABASE_URL,
|
||||||
type: 'postgres',
|
type: 'postgres',
|
||||||
logging: ['error'],
|
logging: ['error'],
|
||||||
schema: 'metadata',
|
schema: 'metadata',
|
||||||
entities: ['dist/src/engine/metadata-modules/**/*.entity{.ts,.js}'],
|
entities: [
|
||||||
|
`${isJest ? '' : 'dist/'}src/engine/metadata-modules/**/*.entity{.ts,.js}`,
|
||||||
|
],
|
||||||
synchronize: false,
|
synchronize: false,
|
||||||
migrationsRun: false,
|
migrationsRun: false,
|
||||||
migrationsTableName: '_typeorm_migrations',
|
migrationsTableName: '_typeorm_migrations',
|
||||||
migrations: ['dist/src/database/typeorm/metadata/migrations/*{.ts,.js}'],
|
migrations: [
|
||||||
|
`${isJest ? '' : 'dist/'}src/database/typeorm/metadata/migrations/*{.ts,.js}`,
|
||||||
|
],
|
||||||
ssl:
|
ssl:
|
||||||
process.env.PG_SSL_ALLOW_SELF_SIGNED === 'true'
|
process.env.PG_SSL_ALLOW_SELF_SIGNED === 'true'
|
||||||
? {
|
? {
|
||||||
@@ -24,6 +30,7 @@ export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = {
|
|||||||
query_timeout: 10000,
|
query_timeout: 10000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const connectionSource = new DataSource(
|
export const connectionSource = new DataSource(
|
||||||
typeORMMetadataModuleOptions as DataSourceOptions,
|
typeORMMetadataModuleOptions as DataSourceOptions,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { config } from 'dotenv';
|
import { config } from 'dotenv';
|
||||||
import { DataSource, DataSourceOptions } from 'typeorm';
|
import { DataSource, DataSourceOptions } from 'typeorm';
|
||||||
config();
|
config({ path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env' });
|
||||||
|
|
||||||
const typeORMRawModuleOptions: DataSourceOptions = {
|
const typeORMRawModuleOptions: DataSourceOptions = {
|
||||||
url: process.env.PG_DATABASE_URL,
|
url: process.env.PG_DATABASE_URL,
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ import { DataSource } from 'typeorm';
|
|||||||
|
|
||||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
|
||||||
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
|
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
|
||||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
|
||||||
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
|
||||||
import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity';
|
import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity';
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||||
|
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
|
import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
|
||||||
import { PostgresCredentials } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
import { PostgresCredentials } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||||
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TypeORMService implements OnModuleInit, OnModuleDestroy {
|
export class TypeORMService implements OnModuleInit, OnModuleDestroy {
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ import { CoreEngineModule } from 'src/engine/core-modules/core-engine.module';
|
|||||||
@Module({
|
@Module({
|
||||||
imports: [CoreEngineModule],
|
imports: [CoreEngineModule],
|
||||||
providers: [],
|
providers: [],
|
||||||
exports: [],
|
exports: [CoreEngineModule],
|
||||||
})
|
})
|
||||||
export class GraphQLConfigModule {}
|
export class GraphQLConfigModule {}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Global, Module } from '@nestjs/common';
|
import { Global, Module } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
|
||||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
|
||||||
import { ConfigurableModuleClass } from 'src/engine/core-modules/environment/environment.module-definition';
|
|
||||||
import { validate } from 'src/engine/core-modules/environment/environment-variables';
|
import { validate } from 'src/engine/core-modules/environment/environment-variables';
|
||||||
|
import { ConfigurableModuleClass } from 'src/engine/core-modules/environment/environment.module-definition';
|
||||||
|
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||||
|
|
||||||
@Global()
|
@Global()
|
||||||
@Module({
|
@Module({
|
||||||
@@ -12,6 +12,7 @@ import { validate } from 'src/engine/core-modules/environment/environment-variab
|
|||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
expandVariables: true,
|
expandVariables: true,
|
||||||
validate,
|
validate,
|
||||||
|
envFilePath: process.env.NODE_ENV === 'test' ? '.env.test' : '.env',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
providers: [EnvironmentService],
|
providers: [EnvironmentService],
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export enum NodeEnvironment {
|
export enum NodeEnvironment {
|
||||||
|
test = 'test',
|
||||||
development = 'development',
|
development = 'development',
|
||||||
production = 'production',
|
production = 'production',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.typ
|
|||||||
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
||||||
import { handleExceptionAndConvertToGraphQLError } from 'src/engine/utils/global-exception-handler.util';
|
import { handleExceptionAndConvertToGraphQLError } from 'src/engine/utils/global-exception-handler.util';
|
||||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||||
|
|
||||||
class GraphqlTokenValidationProxy {
|
class GraphqlTokenValidationProxy {
|
||||||
private tokenService: TokenService;
|
private tokenService: TokenService;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { config } from 'dotenv';
|
import { config } from 'dotenv';
|
||||||
config();
|
config({ path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env' });
|
||||||
|
|
||||||
export function generateFrontConfig(): void {
|
export function generateFrontConfig(): void {
|
||||||
const configObject = {
|
const configObject = {
|
||||||
|
|||||||
67
packages/twenty-server/test/activities.integration-spec.ts
Normal file
67
packages/twenty-server/test/activities.integration-spec.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('activitiesResolver (integration)', () => {
|
||||||
|
it('should find many activities', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query activities {
|
||||||
|
activities {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
title
|
||||||
|
body
|
||||||
|
type
|
||||||
|
reminderAt
|
||||||
|
dueAt
|
||||||
|
completedAt
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
authorId
|
||||||
|
assigneeId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.activities;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const activities = edges[0].node;
|
||||||
|
|
||||||
|
expect(activities).toHaveProperty('title');
|
||||||
|
expect(activities).toHaveProperty('body');
|
||||||
|
expect(activities).toHaveProperty('type');
|
||||||
|
expect(activities).toHaveProperty('reminderAt');
|
||||||
|
expect(activities).toHaveProperty('dueAt');
|
||||||
|
expect(activities).toHaveProperty('completedAt');
|
||||||
|
expect(activities).toHaveProperty('id');
|
||||||
|
expect(activities).toHaveProperty('createdAt');
|
||||||
|
expect(activities).toHaveProperty('updatedAt');
|
||||||
|
expect(activities).toHaveProperty('deletedAt');
|
||||||
|
expect(activities).toHaveProperty('authorId');
|
||||||
|
expect(activities).toHaveProperty('assigneeId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('activityTargetsResolver (integration)', () => {
|
||||||
|
it('should find many activityTargets', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query activityTargets {
|
||||||
|
activityTargets {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
activityId
|
||||||
|
personId
|
||||||
|
companyId
|
||||||
|
opportunityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.activityTargets;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const activityTargets = edges[0].node;
|
||||||
|
|
||||||
|
expect(activityTargets).toHaveProperty('id');
|
||||||
|
expect(activityTargets).toHaveProperty('createdAt');
|
||||||
|
expect(activityTargets).toHaveProperty('updatedAt');
|
||||||
|
expect(activityTargets).toHaveProperty('deletedAt');
|
||||||
|
expect(activityTargets).toHaveProperty('activityId');
|
||||||
|
expect(activityTargets).toHaveProperty('personId');
|
||||||
|
expect(activityTargets).toHaveProperty('companyId');
|
||||||
|
expect(activityTargets).toHaveProperty('opportunityId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
packages/twenty-server/test/api-keys.integration-spec.ts
Normal file
57
packages/twenty-server/test/api-keys.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('apiKeysResolver (integration)', () => {
|
||||||
|
it('should find many apiKeys', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query apiKeys {
|
||||||
|
apiKeys {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
name
|
||||||
|
expiresAt
|
||||||
|
revokedAt
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.apiKeys;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const apiKeys = edges[0].node;
|
||||||
|
|
||||||
|
expect(apiKeys).toHaveProperty('name');
|
||||||
|
expect(apiKeys).toHaveProperty('expiresAt');
|
||||||
|
expect(apiKeys).toHaveProperty('revokedAt');
|
||||||
|
expect(apiKeys).toHaveProperty('id');
|
||||||
|
expect(apiKeys).toHaveProperty('createdAt');
|
||||||
|
expect(apiKeys).toHaveProperty('updatedAt');
|
||||||
|
expect(apiKeys).toHaveProperty('deletedAt');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { INestApplication } from '@nestjs/common';
|
|
||||||
|
|
||||||
import request from 'supertest';
|
|
||||||
|
|
||||||
import { createApp } from './utils/create-app';
|
|
||||||
|
|
||||||
describe('AppController (e2e)', () => {
|
|
||||||
let app: INestApplication;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
[app] = await createApp();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await app.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('/healthz (GET)', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.get('/healthz')
|
|
||||||
.expect(200)
|
|
||||||
.expect((response) => {
|
|
||||||
expect(response.body).toEqual({
|
|
||||||
status: 'ok',
|
|
||||||
info: { database: { status: 'up' } },
|
|
||||||
error: {},
|
|
||||||
details: { database: { status: 'up' } },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
71
packages/twenty-server/test/attachments.integration-spec.ts
Normal file
71
packages/twenty-server/test/attachments.integration-spec.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('attachmentsResolver (integration)', () => {
|
||||||
|
it('should find many attachments', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query attachments {
|
||||||
|
attachments {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
name
|
||||||
|
fullPath
|
||||||
|
type
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
authorId
|
||||||
|
activityId
|
||||||
|
taskId
|
||||||
|
noteId
|
||||||
|
personId
|
||||||
|
companyId
|
||||||
|
opportunityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.attachments;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const attachments = edges[0].node;
|
||||||
|
|
||||||
|
expect(attachments).toHaveProperty('name');
|
||||||
|
expect(attachments).toHaveProperty('fullPath');
|
||||||
|
expect(attachments).toHaveProperty('type');
|
||||||
|
expect(attachments).toHaveProperty('id');
|
||||||
|
expect(attachments).toHaveProperty('createdAt');
|
||||||
|
expect(attachments).toHaveProperty('updatedAt');
|
||||||
|
expect(attachments).toHaveProperty('deletedAt');
|
||||||
|
expect(attachments).toHaveProperty('authorId');
|
||||||
|
expect(attachments).toHaveProperty('activityId');
|
||||||
|
expect(attachments).toHaveProperty('taskId');
|
||||||
|
expect(attachments).toHaveProperty('noteId');
|
||||||
|
expect(attachments).toHaveProperty('personId');
|
||||||
|
expect(attachments).toHaveProperty('companyId');
|
||||||
|
expect(attachments).toHaveProperty('opportunityId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
65
packages/twenty-server/test/audit-logs.integration-spec.ts
Normal file
65
packages/twenty-server/test/audit-logs.integration-spec.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('auditLogsResolver (integration)', () => {
|
||||||
|
it('should find many auditLogs', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query auditLogs {
|
||||||
|
auditLogs {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
name
|
||||||
|
properties
|
||||||
|
context
|
||||||
|
objectName
|
||||||
|
objectMetadataId
|
||||||
|
recordId
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
workspaceMemberId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.auditLogs;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const auditLogs = edges[0].node;
|
||||||
|
|
||||||
|
expect(auditLogs).toHaveProperty('name');
|
||||||
|
expect(auditLogs).toHaveProperty('properties');
|
||||||
|
expect(auditLogs).toHaveProperty('context');
|
||||||
|
expect(auditLogs).toHaveProperty('objectName');
|
||||||
|
expect(auditLogs).toHaveProperty('objectMetadataId');
|
||||||
|
expect(auditLogs).toHaveProperty('recordId');
|
||||||
|
expect(auditLogs).toHaveProperty('id');
|
||||||
|
expect(auditLogs).toHaveProperty('createdAt');
|
||||||
|
expect(auditLogs).toHaveProperty('updatedAt');
|
||||||
|
expect(auditLogs).toHaveProperty('deletedAt');
|
||||||
|
expect(auditLogs).toHaveProperty('workspaceMemberId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
80
packages/twenty-server/test/auth.integration-spec.ts
Normal file
80
packages/twenty-server/test/auth.integration-spec.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
const auth = {
|
||||||
|
email: 'tim@apple.dev',
|
||||||
|
password: 'Applecar2025',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('AuthResolve (integration)', () => {
|
||||||
|
let loginToken: string;
|
||||||
|
|
||||||
|
it('should challenge with email and password', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
mutation Challenge {
|
||||||
|
challenge(email: "${auth.email}", password: "${auth.password}") {
|
||||||
|
loginToken {
|
||||||
|
token
|
||||||
|
expiresAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.challenge;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(data.loginToken).toBeDefined();
|
||||||
|
|
||||||
|
loginToken = data.loginToken.token;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should verify with login token', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
mutation Verify {
|
||||||
|
verify(loginToken: "${loginToken}") {
|
||||||
|
tokens {
|
||||||
|
accessToken {
|
||||||
|
token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.verify;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(data.tokens).toBeDefined();
|
||||||
|
|
||||||
|
const accessToken = data.tokens.accessToken;
|
||||||
|
|
||||||
|
expect(accessToken).toBeDefined();
|
||||||
|
expect(accessToken.token).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
55
packages/twenty-server/test/blocklists.integration-spec.ts
Normal file
55
packages/twenty-server/test/blocklists.integration-spec.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('blocklistsResolver (integration)', () => {
|
||||||
|
it('should find many blocklists', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query blocklists {
|
||||||
|
blocklists {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
handle
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
workspaceMemberId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.blocklists;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const blocklists = edges[0].node;
|
||||||
|
|
||||||
|
expect(blocklists).toHaveProperty('handle');
|
||||||
|
expect(blocklists).toHaveProperty('id');
|
||||||
|
expect(blocklists).toHaveProperty('createdAt');
|
||||||
|
expect(blocklists).toHaveProperty('updatedAt');
|
||||||
|
expect(blocklists).toHaveProperty('deletedAt');
|
||||||
|
expect(blocklists).toHaveProperty('workspaceMemberId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('calendarChannelEventAssociationsResolver (integration)', () => {
|
||||||
|
it('should find many calendarChannelEventAssociations', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query calendarChannelEventAssociations {
|
||||||
|
calendarChannelEventAssociations {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
eventExternalId
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
calendarChannelId
|
||||||
|
calendarEventId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.calendarChannelEventAssociations;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const calendarChannelEventAssociations = edges[0].node;
|
||||||
|
|
||||||
|
expect(calendarChannelEventAssociations).toHaveProperty(
|
||||||
|
'eventExternalId',
|
||||||
|
);
|
||||||
|
expect(calendarChannelEventAssociations).toHaveProperty('id');
|
||||||
|
expect(calendarChannelEventAssociations).toHaveProperty('createdAt');
|
||||||
|
expect(calendarChannelEventAssociations).toHaveProperty('updatedAt');
|
||||||
|
expect(calendarChannelEventAssociations).toHaveProperty('deletedAt');
|
||||||
|
expect(calendarChannelEventAssociations).toHaveProperty(
|
||||||
|
'calendarChannelId',
|
||||||
|
);
|
||||||
|
expect(calendarChannelEventAssociations).toHaveProperty(
|
||||||
|
'calendarEventId',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('calendarChannelsResolver (integration)', () => {
|
||||||
|
it('should find many calendarChannels', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query calendarChannels {
|
||||||
|
calendarChannels {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
handle
|
||||||
|
syncStatus
|
||||||
|
syncStage
|
||||||
|
visibility
|
||||||
|
isContactAutoCreationEnabled
|
||||||
|
contactAutoCreationPolicy
|
||||||
|
isSyncEnabled
|
||||||
|
syncCursor
|
||||||
|
syncStageStartedAt
|
||||||
|
throttleFailureCount
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
connectedAccountId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.calendarChannels;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const calendarChannels = edges[0].node;
|
||||||
|
|
||||||
|
expect(calendarChannels).toHaveProperty('handle');
|
||||||
|
expect(calendarChannels).toHaveProperty('syncStatus');
|
||||||
|
expect(calendarChannels).toHaveProperty('syncStage');
|
||||||
|
expect(calendarChannels).toHaveProperty('visibility');
|
||||||
|
expect(calendarChannels).toHaveProperty(
|
||||||
|
'isContactAutoCreationEnabled',
|
||||||
|
);
|
||||||
|
expect(calendarChannels).toHaveProperty('contactAutoCreationPolicy');
|
||||||
|
expect(calendarChannels).toHaveProperty('isSyncEnabled');
|
||||||
|
expect(calendarChannels).toHaveProperty('syncCursor');
|
||||||
|
expect(calendarChannels).toHaveProperty('syncStageStartedAt');
|
||||||
|
expect(calendarChannels).toHaveProperty('throttleFailureCount');
|
||||||
|
expect(calendarChannels).toHaveProperty('id');
|
||||||
|
expect(calendarChannels).toHaveProperty('createdAt');
|
||||||
|
expect(calendarChannels).toHaveProperty('updatedAt');
|
||||||
|
expect(calendarChannels).toHaveProperty('deletedAt');
|
||||||
|
expect(calendarChannels).toHaveProperty('connectedAccountId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('calendarEventParticipantsResolver (integration)', () => {
|
||||||
|
it('should find many calendarEventParticipants', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query calendarEventParticipants {
|
||||||
|
calendarEventParticipants {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
handle
|
||||||
|
displayName
|
||||||
|
isOrganizer
|
||||||
|
responseStatus
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
calendarEventId
|
||||||
|
personId
|
||||||
|
workspaceMemberId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.calendarEventParticipants;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const calendarEventParticipants = edges[0].node;
|
||||||
|
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('handle');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('displayName');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('isOrganizer');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('responseStatus');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('id');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('createdAt');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('updatedAt');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('deletedAt');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('calendarEventId');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('personId');
|
||||||
|
expect(calendarEventParticipants).toHaveProperty('workspaceMemberId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
packages/twenty-server/test/comments.integration-spec.ts
Normal file
57
packages/twenty-server/test/comments.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('commentsResolver (integration)', () => {
|
||||||
|
it('should find many comments', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query comments {
|
||||||
|
comments {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
body
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
authorId
|
||||||
|
activityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.comments;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const comments = edges[0].node;
|
||||||
|
|
||||||
|
expect(comments).toHaveProperty('body');
|
||||||
|
expect(comments).toHaveProperty('id');
|
||||||
|
expect(comments).toHaveProperty('createdAt');
|
||||||
|
expect(comments).toHaveProperty('updatedAt');
|
||||||
|
expect(comments).toHaveProperty('deletedAt');
|
||||||
|
expect(comments).toHaveProperty('authorId');
|
||||||
|
expect(comments).toHaveProperty('activityId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
67
packages/twenty-server/test/companies.integration-spec.ts
Normal file
67
packages/twenty-server/test/companies.integration-spec.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('companiesResolver (integration)', () => {
|
||||||
|
it('should find many companies', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query companies {
|
||||||
|
companies {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
name
|
||||||
|
employees
|
||||||
|
idealCustomerProfile
|
||||||
|
position
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
accountOwnerId
|
||||||
|
tagline
|
||||||
|
workPolicy
|
||||||
|
visaSponsorship
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.companies;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const companies = edges[0].node;
|
||||||
|
|
||||||
|
expect(companies).toHaveProperty('name');
|
||||||
|
expect(companies).toHaveProperty('employees');
|
||||||
|
expect(companies).toHaveProperty('idealCustomerProfile');
|
||||||
|
expect(companies).toHaveProperty('position');
|
||||||
|
expect(companies).toHaveProperty('id');
|
||||||
|
expect(companies).toHaveProperty('createdAt');
|
||||||
|
expect(companies).toHaveProperty('updatedAt');
|
||||||
|
expect(companies).toHaveProperty('deletedAt');
|
||||||
|
expect(companies).toHaveProperty('accountOwnerId');
|
||||||
|
expect(companies).toHaveProperty('tagline');
|
||||||
|
expect(companies).toHaveProperty('workPolicy');
|
||||||
|
expect(companies).toHaveProperty('visaSponsorship');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
import { INestApplication } from '@nestjs/common';
|
|
||||||
|
|
||||||
import request from 'supertest';
|
|
||||||
|
|
||||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
|
||||||
|
|
||||||
import { createApp } from './utils/create-app';
|
|
||||||
|
|
||||||
describe('CompanyResolver (e2e)', () => {
|
|
||||||
let app: INestApplication;
|
|
||||||
let companyId: string | undefined;
|
|
||||||
|
|
||||||
const authGuardMock = { canActivate: (): any => true };
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
[app] = await createApp({
|
|
||||||
moduleBuilderHook: (moduleBuilder) =>
|
|
||||||
moduleBuilder.overrideGuard(WorkspaceAuthGuard).useValue(authGuardMock),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await app.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a company', () => {
|
|
||||||
const queryData = {
|
|
||||||
query: `
|
|
||||||
mutation CreateOneCompany($data: CompanyCreateInput!) {
|
|
||||||
createOneCompany(data: $data) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
domainName
|
|
||||||
address {
|
|
||||||
addressCity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
data: {
|
|
||||||
name: 'New Company',
|
|
||||||
domainName: 'new-company.com',
|
|
||||||
address: { addressCity: 'Paris' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post('/graphql')
|
|
||||||
.send(queryData)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
const data = res.body.data.createOneCompany;
|
|
||||||
|
|
||||||
companyId = data.id;
|
|
||||||
|
|
||||||
expect(data).toBeDefined();
|
|
||||||
expect(data).toHaveProperty('id');
|
|
||||||
expect(data).toHaveProperty('name', 'New Company');
|
|
||||||
expect(data).toHaveProperty('domainName', 'new-company.com');
|
|
||||||
expect(data).toHaveProperty('address', { addressCity: 'Paris' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should find many companies', () => {
|
|
||||||
const queryData = {
|
|
||||||
query: `
|
|
||||||
query FindManyCompany {
|
|
||||||
findManyCompany {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
domainName
|
|
||||||
address {
|
|
||||||
addressCity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post('/graphql')
|
|
||||||
.send(queryData)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
const data = res.body.data.findManyCompany;
|
|
||||||
|
|
||||||
expect(data).toBeDefined();
|
|
||||||
expect(Array.isArray(data)).toBe(true);
|
|
||||||
expect(data.length).toBeGreaterThan(0);
|
|
||||||
|
|
||||||
const company = data.find((c) => c.id === companyId);
|
|
||||||
|
|
||||||
expect(company).toBeDefined();
|
|
||||||
expect(company).toHaveProperty('id');
|
|
||||||
expect(company).toHaveProperty('name', 'New Company');
|
|
||||||
expect(company).toHaveProperty('domainName', 'new-company.com');
|
|
||||||
expect(company).toHaveProperty('address', { addressCity: 'Paris' });
|
|
||||||
|
|
||||||
// Check if we have access to ressources outside of our workspace
|
|
||||||
const instagramCompany = data.find((c) => c.name === 'Instagram');
|
|
||||||
|
|
||||||
expect(instagramCompany).toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should find unique company', () => {
|
|
||||||
const queryData = {
|
|
||||||
query: `
|
|
||||||
query FindUniqueCompany($where: CompanyWhereUniqueInput!) {
|
|
||||||
findUniqueCompany(where: $where) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
domainName
|
|
||||||
address {
|
|
||||||
addressCity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: companyId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post('/graphql')
|
|
||||||
.send(queryData)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
const data = res.body.data.findUniqueCompany;
|
|
||||||
|
|
||||||
expect(data).toBeDefined();
|
|
||||||
expect(data).toHaveProperty('id');
|
|
||||||
expect(data).toHaveProperty('name', 'New Company');
|
|
||||||
expect(data).toHaveProperty('domainName', 'new-company.com');
|
|
||||||
expect(data).toHaveProperty('address', { addressCity: 'Paris' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not find unique company (forbidden because outside workspace)', () => {
|
|
||||||
const queryData = {
|
|
||||||
query: `
|
|
||||||
query FindUniqueCompany($where: CompanyWhereUniqueInput!) {
|
|
||||||
findUniqueCompany(where: $where) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
domainName
|
|
||||||
address {
|
|
||||||
addressCity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: 'twenty-dev-a674fa6c-1455-4c57-afaf-dd5dc086361e',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post('/graphql')
|
|
||||||
.send(queryData)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
const errors = res.body.errors;
|
|
||||||
const error = errors?.[0];
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
expect(error.message).toBe('Forbidden resource');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update a company', () => {
|
|
||||||
const queryData = {
|
|
||||||
query: `
|
|
||||||
mutation UpdateOneCompany($where: CompanyWhereUniqueInput!, $data: CompanyUpdateInput!) {
|
|
||||||
updateOneCompany(data: $data, where: $where) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
domainName
|
|
||||||
address {
|
|
||||||
addressCity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: companyId,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
name: 'Updated Company',
|
|
||||||
domainName: 'updated-company.com',
|
|
||||||
address: { addressCity: 'Updated City' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post('/graphql')
|
|
||||||
.send(queryData)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
const data = res.body.data.updateOneCompany;
|
|
||||||
|
|
||||||
expect(data).toBeDefined();
|
|
||||||
expect(data).toHaveProperty('id');
|
|
||||||
expect(data).toHaveProperty('name', 'Updated Company');
|
|
||||||
expect(data).toHaveProperty('domainName', 'updated-company.com');
|
|
||||||
expect(data).toHaveProperty('address', { addressCity: 'Updated City' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not update a company (forbidden because outside workspace)', () => {
|
|
||||||
const queryData = {
|
|
||||||
query: `
|
|
||||||
mutation UpdateOneCompany($where: CompanyWhereUniqueInput!, $data: CompanyUpdateInput!) {
|
|
||||||
updateOneCompany(data: $data, where: $where) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
domainName
|
|
||||||
address {
|
|
||||||
addressCity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: 'twenty-dev-a674fa6c-1455-4c57-afaf-dd5dc086361e',
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
name: 'Updated Instagram',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post('/graphql')
|
|
||||||
.send(queryData)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
const errors = res.body.errors;
|
|
||||||
const error = errors?.[0];
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
expect(error.message).toBe('Forbidden resource');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should delete a company', () => {
|
|
||||||
const queryData = {
|
|
||||||
query: `
|
|
||||||
mutation DeleteManyCompany($ids: [String!]) {
|
|
||||||
deleteManyCompany(where: {id: {in: $ids}}) {
|
|
||||||
count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
ids: [companyId],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post('/graphql')
|
|
||||||
.send(queryData)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
const data = res.body.data.deleteManyCompany;
|
|
||||||
|
|
||||||
companyId = undefined;
|
|
||||||
|
|
||||||
expect(data).toBeDefined();
|
|
||||||
expect(data).toHaveProperty('count', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not delete a company (forbidden because outside workspace)', () => {
|
|
||||||
const queryData = {
|
|
||||||
query: `
|
|
||||||
mutation DeleteManyCompany($ids: [String!]) {
|
|
||||||
deleteManyCompany(where: {id: {in: $ids}}) {
|
|
||||||
count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
ids: ['twenty-dev-a674fa6c-1455-4c57-afaf-dd5dc086361e'],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post('/graphql')
|
|
||||||
.send(queryData)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
const errors = res.body.errors;
|
|
||||||
const error = errors?.[0];
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
expect(error.message).toBe('Forbidden resource');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
48
packages/twenty-server/test/company.integration-spec.ts
Normal file
48
packages/twenty-server/test/company.integration-spec.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const graphqlClient = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('CompanyResolver (integration)', () => {
|
||||||
|
it('should find many companies', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query Companies {
|
||||||
|
companies {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return graphqlClient
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.companies;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const company = edges[0].node;
|
||||||
|
|
||||||
|
expect(company).toBeDefined();
|
||||||
|
expect(company).toHaveProperty('id');
|
||||||
|
expect(company).toHaveProperty('name');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('connectedAccountsResolver (integration)', () => {
|
||||||
|
it('should find many connectedAccounts', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query connectedAccounts {
|
||||||
|
connectedAccounts {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
handle
|
||||||
|
provider
|
||||||
|
accessToken
|
||||||
|
refreshToken
|
||||||
|
lastSyncHistoryId
|
||||||
|
authFailedAt
|
||||||
|
handleAliases
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
accountOwnerId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.connectedAccounts;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const connectedAccounts = edges[0].node;
|
||||||
|
|
||||||
|
expect(connectedAccounts).toHaveProperty('handle');
|
||||||
|
expect(connectedAccounts).toHaveProperty('provider');
|
||||||
|
expect(connectedAccounts).toHaveProperty('accessToken');
|
||||||
|
expect(connectedAccounts).toHaveProperty('refreshToken');
|
||||||
|
expect(connectedAccounts).toHaveProperty('lastSyncHistoryId');
|
||||||
|
expect(connectedAccounts).toHaveProperty('authFailedAt');
|
||||||
|
expect(connectedAccounts).toHaveProperty('handleAliases');
|
||||||
|
expect(connectedAccounts).toHaveProperty('id');
|
||||||
|
expect(connectedAccounts).toHaveProperty('createdAt');
|
||||||
|
expect(connectedAccounts).toHaveProperty('updatedAt');
|
||||||
|
expect(connectedAccounts).toHaveProperty('deletedAt');
|
||||||
|
expect(connectedAccounts).toHaveProperty('accountOwnerId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
67
packages/twenty-server/test/favorites.integration-spec.ts
Normal file
67
packages/twenty-server/test/favorites.integration-spec.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('favoritesResolver (integration)', () => {
|
||||||
|
it('should find many favorites', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query favorites {
|
||||||
|
favorites {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
position
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
workspaceMemberId
|
||||||
|
personId
|
||||||
|
companyId
|
||||||
|
opportunityId
|
||||||
|
taskId
|
||||||
|
noteId
|
||||||
|
viewId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.favorites;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const favorites = edges[0].node;
|
||||||
|
|
||||||
|
expect(favorites).toHaveProperty('position');
|
||||||
|
expect(favorites).toHaveProperty('id');
|
||||||
|
expect(favorites).toHaveProperty('createdAt');
|
||||||
|
expect(favorites).toHaveProperty('updatedAt');
|
||||||
|
expect(favorites).toHaveProperty('deletedAt');
|
||||||
|
expect(favorites).toHaveProperty('workspaceMemberId');
|
||||||
|
expect(favorites).toHaveProperty('personId');
|
||||||
|
expect(favorites).toHaveProperty('companyId');
|
||||||
|
expect(favorites).toHaveProperty('opportunityId');
|
||||||
|
expect(favorites).toHaveProperty('taskId');
|
||||||
|
expect(favorites).toHaveProperty('noteId');
|
||||||
|
expect(favorites).toHaveProperty('viewId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"moduleFileExtensions": ["js", "json", "ts"],
|
|
||||||
"rootDir": ".",
|
|
||||||
"testEnvironment": "node",
|
|
||||||
"testRegex": ".e2e-spec.ts$",
|
|
||||||
"setupFilesAfterEnv": ["<rootDir>/utils/setup-tests.ts"],
|
|
||||||
"moduleNameMapper": {
|
|
||||||
"^src/(.*)": "<rootDir>/../src/$1",
|
|
||||||
"^test/(.*)": "<rootDir>/$1"
|
|
||||||
},
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('messageChannelMessageAssociationsResolver (integration)', () => {
|
||||||
|
it('should find many messageChannelMessageAssociations', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query messageChannelMessageAssociations {
|
||||||
|
messageChannelMessageAssociations {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
createdAt
|
||||||
|
messageExternalId
|
||||||
|
messageThreadExternalId
|
||||||
|
direction
|
||||||
|
id
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
messageChannelId
|
||||||
|
messageId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.messageChannelMessageAssociations;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const messageChannelMessageAssociations = edges[0].node;
|
||||||
|
|
||||||
|
expect(messageChannelMessageAssociations).toHaveProperty('createdAt');
|
||||||
|
expect(messageChannelMessageAssociations).toHaveProperty(
|
||||||
|
'messageExternalId',
|
||||||
|
);
|
||||||
|
expect(messageChannelMessageAssociations).toHaveProperty(
|
||||||
|
'messageThreadExternalId',
|
||||||
|
);
|
||||||
|
expect(messageChannelMessageAssociations).toHaveProperty('direction');
|
||||||
|
expect(messageChannelMessageAssociations).toHaveProperty('id');
|
||||||
|
expect(messageChannelMessageAssociations).toHaveProperty('updatedAt');
|
||||||
|
expect(messageChannelMessageAssociations).toHaveProperty('deletedAt');
|
||||||
|
expect(messageChannelMessageAssociations).toHaveProperty(
|
||||||
|
'messageChannelId',
|
||||||
|
);
|
||||||
|
expect(messageChannelMessageAssociations).toHaveProperty('messageId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('messageChannelsResolver (integration)', () => {
|
||||||
|
it('should find many messageChannels', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query messageChannels {
|
||||||
|
messageChannels {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
visibility
|
||||||
|
handle
|
||||||
|
type
|
||||||
|
isContactAutoCreationEnabled
|
||||||
|
contactAutoCreationPolicy
|
||||||
|
excludeNonProfessionalEmails
|
||||||
|
excludeGroupEmails
|
||||||
|
isSyncEnabled
|
||||||
|
syncCursor
|
||||||
|
syncedAt
|
||||||
|
syncStatus
|
||||||
|
syncStage
|
||||||
|
syncStageStartedAt
|
||||||
|
throttleFailureCount
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
connectedAccountId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.messageChannels;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const messageChannels = edges[0].node;
|
||||||
|
|
||||||
|
expect(messageChannels).toHaveProperty('visibility');
|
||||||
|
expect(messageChannels).toHaveProperty('handle');
|
||||||
|
expect(messageChannels).toHaveProperty('type');
|
||||||
|
expect(messageChannels).toHaveProperty(
|
||||||
|
'isContactAutoCreationEnabled',
|
||||||
|
);
|
||||||
|
expect(messageChannels).toHaveProperty('contactAutoCreationPolicy');
|
||||||
|
expect(messageChannels).toHaveProperty(
|
||||||
|
'excludeNonProfessionalEmails',
|
||||||
|
);
|
||||||
|
expect(messageChannels).toHaveProperty('excludeGroupEmails');
|
||||||
|
expect(messageChannels).toHaveProperty('isSyncEnabled');
|
||||||
|
expect(messageChannels).toHaveProperty('syncCursor');
|
||||||
|
expect(messageChannels).toHaveProperty('syncedAt');
|
||||||
|
expect(messageChannels).toHaveProperty('syncStatus');
|
||||||
|
expect(messageChannels).toHaveProperty('syncStage');
|
||||||
|
expect(messageChannels).toHaveProperty('syncStageStartedAt');
|
||||||
|
expect(messageChannels).toHaveProperty('throttleFailureCount');
|
||||||
|
expect(messageChannels).toHaveProperty('id');
|
||||||
|
expect(messageChannels).toHaveProperty('createdAt');
|
||||||
|
expect(messageChannels).toHaveProperty('updatedAt');
|
||||||
|
expect(messageChannels).toHaveProperty('deletedAt');
|
||||||
|
expect(messageChannels).toHaveProperty('connectedAccountId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('messageParticipantsResolver (integration)', () => {
|
||||||
|
it('should find many messageParticipants', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query messageParticipants {
|
||||||
|
messageParticipants {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
role
|
||||||
|
handle
|
||||||
|
displayName
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
messageId
|
||||||
|
personId
|
||||||
|
workspaceMemberId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.messageParticipants;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const messageParticipants = edges[0].node;
|
||||||
|
|
||||||
|
expect(messageParticipants).toHaveProperty('role');
|
||||||
|
expect(messageParticipants).toHaveProperty('handle');
|
||||||
|
expect(messageParticipants).toHaveProperty('displayName');
|
||||||
|
expect(messageParticipants).toHaveProperty('id');
|
||||||
|
expect(messageParticipants).toHaveProperty('createdAt');
|
||||||
|
expect(messageParticipants).toHaveProperty('updatedAt');
|
||||||
|
expect(messageParticipants).toHaveProperty('deletedAt');
|
||||||
|
expect(messageParticipants).toHaveProperty('messageId');
|
||||||
|
expect(messageParticipants).toHaveProperty('personId');
|
||||||
|
expect(messageParticipants).toHaveProperty('workspaceMemberId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('messageThreadsResolver (integration)', () => {
|
||||||
|
it('should find many messageThreads', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query messageThreads {
|
||||||
|
messageThreads {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.messageThreads;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const messageThreads = edges[0].node;
|
||||||
|
|
||||||
|
expect(messageThreads).toHaveProperty('id');
|
||||||
|
expect(messageThreads).toHaveProperty('createdAt');
|
||||||
|
expect(messageThreads).toHaveProperty('updatedAt');
|
||||||
|
expect(messageThreads).toHaveProperty('deletedAt');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "20202020-a838-4fa9-b59b-96409b9a1c30",
|
|
||||||
"firstName": "Tim",
|
|
||||||
"lastName": "Apple",
|
|
||||||
"email": "tim@apple.dev",
|
|
||||||
"locale": "en",
|
|
||||||
"passwordHash": "$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6",
|
|
||||||
"avatarUrl": null
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
59
packages/twenty-server/test/note-targets.integration-spec.ts
Normal file
59
packages/twenty-server/test/note-targets.integration-spec.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('noteTargetsResolver (integration)', () => {
|
||||||
|
it('should find many noteTargets', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query noteTargets {
|
||||||
|
noteTargets {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
noteId
|
||||||
|
personId
|
||||||
|
companyId
|
||||||
|
opportunityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.noteTargets;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const noteTargets = edges[0].node;
|
||||||
|
|
||||||
|
expect(noteTargets).toHaveProperty('id');
|
||||||
|
expect(noteTargets).toHaveProperty('createdAt');
|
||||||
|
expect(noteTargets).toHaveProperty('updatedAt');
|
||||||
|
expect(noteTargets).toHaveProperty('deletedAt');
|
||||||
|
expect(noteTargets).toHaveProperty('noteId');
|
||||||
|
expect(noteTargets).toHaveProperty('personId');
|
||||||
|
expect(noteTargets).toHaveProperty('companyId');
|
||||||
|
expect(noteTargets).toHaveProperty('opportunityId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
packages/twenty-server/test/notes.integration-spec.ts
Normal file
57
packages/twenty-server/test/notes.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('notesResolver (integration)', () => {
|
||||||
|
it('should find many notes', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query notes {
|
||||||
|
notes {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
position
|
||||||
|
title
|
||||||
|
body
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.notes;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const notes = edges[0].node;
|
||||||
|
|
||||||
|
expect(notes).toHaveProperty('position');
|
||||||
|
expect(notes).toHaveProperty('title');
|
||||||
|
expect(notes).toHaveProperty('body');
|
||||||
|
expect(notes).toHaveProperty('id');
|
||||||
|
expect(notes).toHaveProperty('createdAt');
|
||||||
|
expect(notes).toHaveProperty('updatedAt');
|
||||||
|
expect(notes).toHaveProperty('deletedAt');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
75
packages/twenty-server/test/objects.integration-spec.ts
Normal file
75
packages/twenty-server/test/objects.integration-spec.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('objectsResolver (integration)', () => {
|
||||||
|
it('should find many objects', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query objects {
|
||||||
|
objects {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
dataSourceId
|
||||||
|
nameSingular
|
||||||
|
namePlural
|
||||||
|
labelSingular
|
||||||
|
labelPlural
|
||||||
|
description
|
||||||
|
icon
|
||||||
|
isCustom
|
||||||
|
isRemote
|
||||||
|
isActive
|
||||||
|
isSystem
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
labelIdentifierFieldMetadataId
|
||||||
|
imageIdentifierFieldMetadataId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.objects;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const objects = edges[0].node;
|
||||||
|
|
||||||
|
expect(objects).toHaveProperty('id');
|
||||||
|
expect(objects).toHaveProperty('dataSourceId');
|
||||||
|
expect(objects).toHaveProperty('nameSingular');
|
||||||
|
expect(objects).toHaveProperty('namePlural');
|
||||||
|
expect(objects).toHaveProperty('labelSingular');
|
||||||
|
expect(objects).toHaveProperty('labelPlural');
|
||||||
|
expect(objects).toHaveProperty('description');
|
||||||
|
expect(objects).toHaveProperty('icon');
|
||||||
|
expect(objects).toHaveProperty('isCustom');
|
||||||
|
expect(objects).toHaveProperty('isRemote');
|
||||||
|
expect(objects).toHaveProperty('isActive');
|
||||||
|
expect(objects).toHaveProperty('isSystem');
|
||||||
|
expect(objects).toHaveProperty('createdAt');
|
||||||
|
expect(objects).toHaveProperty('updatedAt');
|
||||||
|
expect(objects).toHaveProperty('labelIdentifierFieldMetadataId');
|
||||||
|
expect(objects).toHaveProperty('imageIdentifierFieldMetadataId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('opportunitiesResolver (integration)', () => {
|
||||||
|
it('should find many opportunities', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query opportunities {
|
||||||
|
opportunities {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
name
|
||||||
|
closeDate
|
||||||
|
stage
|
||||||
|
position
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
pointOfContactId
|
||||||
|
companyId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.opportunities;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const opportunities = edges[0].node;
|
||||||
|
|
||||||
|
expect(opportunities).toHaveProperty('name');
|
||||||
|
expect(opportunities).toHaveProperty('closeDate');
|
||||||
|
expect(opportunities).toHaveProperty('stage');
|
||||||
|
expect(opportunities).toHaveProperty('position');
|
||||||
|
expect(opportunities).toHaveProperty('id');
|
||||||
|
expect(opportunities).toHaveProperty('createdAt');
|
||||||
|
expect(opportunities).toHaveProperty('updatedAt');
|
||||||
|
expect(opportunities).toHaveProperty('deletedAt');
|
||||||
|
expect(opportunities).toHaveProperty('pointOfContactId');
|
||||||
|
expect(opportunities).toHaveProperty('companyId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
71
packages/twenty-server/test/people.integration-spec.ts
Normal file
71
packages/twenty-server/test/people.integration-spec.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('peopleResolver (integration)', () => {
|
||||||
|
it('should find many people', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query people {
|
||||||
|
people {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
jobTitle
|
||||||
|
phone
|
||||||
|
city
|
||||||
|
avatarUrl
|
||||||
|
position
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
companyId
|
||||||
|
intro
|
||||||
|
whatsapp
|
||||||
|
workPrefereance
|
||||||
|
performanceRating
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.people;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const people = edges[0].node;
|
||||||
|
|
||||||
|
expect(people).toHaveProperty('jobTitle');
|
||||||
|
expect(people).toHaveProperty('phone');
|
||||||
|
expect(people).toHaveProperty('city');
|
||||||
|
expect(people).toHaveProperty('avatarUrl');
|
||||||
|
expect(people).toHaveProperty('position');
|
||||||
|
expect(people).toHaveProperty('id');
|
||||||
|
expect(people).toHaveProperty('createdAt');
|
||||||
|
expect(people).toHaveProperty('updatedAt');
|
||||||
|
expect(people).toHaveProperty('deletedAt');
|
||||||
|
expect(people).toHaveProperty('companyId');
|
||||||
|
expect(people).toHaveProperty('intro');
|
||||||
|
expect(people).toHaveProperty('whatsapp');
|
||||||
|
expect(people).toHaveProperty('workPrefereance');
|
||||||
|
expect(people).toHaveProperty('performanceRating');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('serverlessFunctionsResolver (integration)', () => {
|
||||||
|
it('should find many serverlessFunctions', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query serverlessFunctions {
|
||||||
|
serverlessFunctions {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
sourceCodeHash
|
||||||
|
runtime
|
||||||
|
latestVersion
|
||||||
|
syncStatus
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.serverlessFunctions;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const serverlessFunctions = edges[0].node;
|
||||||
|
|
||||||
|
expect(serverlessFunctions).toHaveProperty('id');
|
||||||
|
expect(serverlessFunctions).toHaveProperty('name');
|
||||||
|
expect(serverlessFunctions).toHaveProperty('description');
|
||||||
|
expect(serverlessFunctions).toHaveProperty('sourceCodeHash');
|
||||||
|
expect(serverlessFunctions).toHaveProperty('runtime');
|
||||||
|
expect(serverlessFunctions).toHaveProperty('latestVersion');
|
||||||
|
expect(serverlessFunctions).toHaveProperty('syncStatus');
|
||||||
|
expect(serverlessFunctions).toHaveProperty('createdAt');
|
||||||
|
expect(serverlessFunctions).toHaveProperty('updatedAt');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
59
packages/twenty-server/test/task-targets.integration-spec.ts
Normal file
59
packages/twenty-server/test/task-targets.integration-spec.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('taskTargetsResolver (integration)', () => {
|
||||||
|
it('should find many taskTargets', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query taskTargets {
|
||||||
|
taskTargets {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
taskId
|
||||||
|
personId
|
||||||
|
companyId
|
||||||
|
opportunityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.taskTargets;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const taskTargets = edges[0].node;
|
||||||
|
|
||||||
|
expect(taskTargets).toHaveProperty('id');
|
||||||
|
expect(taskTargets).toHaveProperty('createdAt');
|
||||||
|
expect(taskTargets).toHaveProperty('updatedAt');
|
||||||
|
expect(taskTargets).toHaveProperty('deletedAt');
|
||||||
|
expect(taskTargets).toHaveProperty('taskId');
|
||||||
|
expect(taskTargets).toHaveProperty('personId');
|
||||||
|
expect(taskTargets).toHaveProperty('companyId');
|
||||||
|
expect(taskTargets).toHaveProperty('opportunityId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
63
packages/twenty-server/test/tasks.integration-spec.ts
Normal file
63
packages/twenty-server/test/tasks.integration-spec.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('tasksResolver (integration)', () => {
|
||||||
|
it('should find many tasks', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query tasks {
|
||||||
|
tasks {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
position
|
||||||
|
title
|
||||||
|
body
|
||||||
|
dueAt
|
||||||
|
status
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
assigneeId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.tasks;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const tasks = edges[0].node;
|
||||||
|
|
||||||
|
expect(tasks).toHaveProperty('position');
|
||||||
|
expect(tasks).toHaveProperty('title');
|
||||||
|
expect(tasks).toHaveProperty('body');
|
||||||
|
expect(tasks).toHaveProperty('dueAt');
|
||||||
|
expect(tasks).toHaveProperty('status');
|
||||||
|
expect(tasks).toHaveProperty('id');
|
||||||
|
expect(tasks).toHaveProperty('createdAt');
|
||||||
|
expect(tasks).toHaveProperty('updatedAt');
|
||||||
|
expect(tasks).toHaveProperty('deletedAt');
|
||||||
|
expect(tasks).toHaveProperty('assigneeId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('timelineActivitiesResolver (integration)', () => {
|
||||||
|
it('should find many timelineActivities', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query timelineActivities {
|
||||||
|
timelineActivities {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
happensAt
|
||||||
|
name
|
||||||
|
properties
|
||||||
|
linkedRecordCachedName
|
||||||
|
linkedRecordId
|
||||||
|
linkedObjectMetadataId
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
workspaceMemberId
|
||||||
|
personId
|
||||||
|
companyId
|
||||||
|
opportunityId
|
||||||
|
noteId
|
||||||
|
taskId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.timelineActivities;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const timelineActivities = edges[0].node;
|
||||||
|
|
||||||
|
expect(timelineActivities).toHaveProperty('happensAt');
|
||||||
|
expect(timelineActivities).toHaveProperty('name');
|
||||||
|
expect(timelineActivities).toHaveProperty('properties');
|
||||||
|
expect(timelineActivities).toHaveProperty('linkedRecordCachedName');
|
||||||
|
expect(timelineActivities).toHaveProperty('linkedRecordId');
|
||||||
|
expect(timelineActivities).toHaveProperty('linkedObjectMetadataId');
|
||||||
|
expect(timelineActivities).toHaveProperty('id');
|
||||||
|
expect(timelineActivities).toHaveProperty('createdAt');
|
||||||
|
expect(timelineActivities).toHaveProperty('updatedAt');
|
||||||
|
expect(timelineActivities).toHaveProperty('deletedAt');
|
||||||
|
expect(timelineActivities).toHaveProperty('workspaceMemberId');
|
||||||
|
expect(timelineActivities).toHaveProperty('personId');
|
||||||
|
expect(timelineActivities).toHaveProperty('companyId');
|
||||||
|
expect(timelineActivities).toHaveProperty('opportunityId');
|
||||||
|
expect(timelineActivities).toHaveProperty('noteId');
|
||||||
|
expect(timelineActivities).toHaveProperty('taskId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||||
import { Test, TestingModule, TestingModuleBuilder } from '@nestjs/testing';
|
import { Test, TestingModule, TestingModuleBuilder } from '@nestjs/testing';
|
||||||
|
|
||||||
import mockUser from 'test/mock-data/user.json';
|
|
||||||
import mockWorkspace from 'test/mock-data/workspace.json';
|
|
||||||
import { RequestHandler } from 'express';
|
|
||||||
|
|
||||||
import { AppModule } from 'src/app.module';
|
import { AppModule } from 'src/app.module';
|
||||||
|
|
||||||
interface TestingModuleCreatePreHook {
|
interface TestingModuleCreatePreHook {
|
||||||
@@ -19,14 +15,14 @@ export type TestingAppCreatePreHook = (
|
|||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets basic e2e testing module of app
|
* Sets basic integration testing module of app
|
||||||
*/
|
*/
|
||||||
export const createApp = async (
|
export const createApp = async (
|
||||||
config: {
|
config: {
|
||||||
moduleBuilderHook?: TestingModuleCreatePreHook;
|
moduleBuilderHook?: TestingModuleCreatePreHook;
|
||||||
appInitHook?: TestingAppCreatePreHook;
|
appInitHook?: TestingAppCreatePreHook;
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<[NestExpressApplication, TestingModule]> => {
|
): Promise<NestExpressApplication> => {
|
||||||
let moduleBuilder: TestingModuleBuilder = Test.createTestingModule({
|
let moduleBuilder: TestingModuleBuilder = Test.createTestingModule({
|
||||||
imports: [AppModule],
|
imports: [AppModule],
|
||||||
});
|
});
|
||||||
@@ -36,21 +32,14 @@ export const createApp = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const moduleFixture: TestingModule = await moduleBuilder.compile();
|
const moduleFixture: TestingModule = await moduleBuilder.compile();
|
||||||
|
|
||||||
const app = moduleFixture.createNestApplication<NestExpressApplication>();
|
const app = moduleFixture.createNestApplication<NestExpressApplication>();
|
||||||
|
|
||||||
if (config.appInitHook) {
|
if (config.appInitHook) {
|
||||||
await config.appInitHook(app);
|
await config.appInitHook(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockAuthHandler: RequestHandler = (req, _res, next) => {
|
await app.init();
|
||||||
req.user = {
|
|
||||||
user: mockUser,
|
|
||||||
workspace: mockWorkspace,
|
|
||||||
};
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
app.use(mockAuthHandler);
|
return app;
|
||||||
|
|
||||||
return [await app.init(), moduleFixture];
|
|
||||||
};
|
};
|
||||||
|
|||||||
16
packages/twenty-server/test/utils/setup-test.ts
Normal file
16
packages/twenty-server/test/utils/setup-test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import 'tsconfig-paths/register';
|
||||||
|
import { JestConfigWithTsJest } from 'ts-jest';
|
||||||
|
|
||||||
|
import { createApp } from './create-app';
|
||||||
|
|
||||||
|
export default async (_, projectConfig: JestConfigWithTsJest) => {
|
||||||
|
const app = await createApp({});
|
||||||
|
|
||||||
|
if (!projectConfig.globals) {
|
||||||
|
throw new Error('No globals found in project config');
|
||||||
|
}
|
||||||
|
|
||||||
|
await app.listen(projectConfig.globals.APP_PORT);
|
||||||
|
|
||||||
|
global.app = app;
|
||||||
|
};
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
global.beforeEach(() => {
|
|
||||||
// resetDb();
|
|
||||||
});
|
|
||||||
5
packages/twenty-server/test/utils/teardown-test.ts
Normal file
5
packages/twenty-server/test/utils/teardown-test.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import 'tsconfig-paths/register';
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
global.app.close();
|
||||||
|
};
|
||||||
61
packages/twenty-server/test/view-fields.integration-spec.ts
Normal file
61
packages/twenty-server/test/view-fields.integration-spec.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('viewFieldsResolver (integration)', () => {
|
||||||
|
it('should find many viewFields', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query viewFields {
|
||||||
|
viewFields {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
fieldMetadataId
|
||||||
|
isVisible
|
||||||
|
size
|
||||||
|
position
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
viewId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.viewFields;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const viewFields = edges[0].node;
|
||||||
|
|
||||||
|
expect(viewFields).toHaveProperty('fieldMetadataId');
|
||||||
|
expect(viewFields).toHaveProperty('isVisible');
|
||||||
|
expect(viewFields).toHaveProperty('size');
|
||||||
|
expect(viewFields).toHaveProperty('position');
|
||||||
|
expect(viewFields).toHaveProperty('id');
|
||||||
|
expect(viewFields).toHaveProperty('createdAt');
|
||||||
|
expect(viewFields).toHaveProperty('updatedAt');
|
||||||
|
expect(viewFields).toHaveProperty('deletedAt');
|
||||||
|
expect(viewFields).toHaveProperty('viewId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
61
packages/twenty-server/test/view-filters.integration-spec.ts
Normal file
61
packages/twenty-server/test/view-filters.integration-spec.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('viewFiltersResolver (integration)', () => {
|
||||||
|
it('should find many viewFilters', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query viewFilters {
|
||||||
|
viewFilters {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
fieldMetadataId
|
||||||
|
operand
|
||||||
|
value
|
||||||
|
displayValue
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
viewId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.viewFilters;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const viewFilters = edges[0].node;
|
||||||
|
|
||||||
|
expect(viewFilters).toHaveProperty('fieldMetadataId');
|
||||||
|
expect(viewFilters).toHaveProperty('operand');
|
||||||
|
expect(viewFilters).toHaveProperty('value');
|
||||||
|
expect(viewFilters).toHaveProperty('displayValue');
|
||||||
|
expect(viewFilters).toHaveProperty('id');
|
||||||
|
expect(viewFilters).toHaveProperty('createdAt');
|
||||||
|
expect(viewFilters).toHaveProperty('updatedAt');
|
||||||
|
expect(viewFilters).toHaveProperty('deletedAt');
|
||||||
|
expect(viewFilters).toHaveProperty('viewId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
packages/twenty-server/test/view-sorts.integration-spec.ts
Normal file
57
packages/twenty-server/test/view-sorts.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('viewSortsResolver (integration)', () => {
|
||||||
|
it('should find many viewSorts', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query viewSorts {
|
||||||
|
viewSorts {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
fieldMetadataId
|
||||||
|
direction
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
viewId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.viewSorts;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const viewSorts = edges[0].node;
|
||||||
|
|
||||||
|
expect(viewSorts).toHaveProperty('fieldMetadataId');
|
||||||
|
expect(viewSorts).toHaveProperty('direction');
|
||||||
|
expect(viewSorts).toHaveProperty('id');
|
||||||
|
expect(viewSorts).toHaveProperty('createdAt');
|
||||||
|
expect(viewSorts).toHaveProperty('updatedAt');
|
||||||
|
expect(viewSorts).toHaveProperty('deletedAt');
|
||||||
|
expect(viewSorts).toHaveProperty('viewId');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
67
packages/twenty-server/test/views.integration-spec.ts
Normal file
67
packages/twenty-server/test/views.integration-spec.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('viewsResolver (integration)', () => {
|
||||||
|
it('should find many views', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query views {
|
||||||
|
views {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
position
|
||||||
|
name
|
||||||
|
objectMetadataId
|
||||||
|
type
|
||||||
|
key
|
||||||
|
icon
|
||||||
|
kanbanFieldMetadataId
|
||||||
|
isCompact
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.views;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const views = edges[0].node;
|
||||||
|
|
||||||
|
expect(views).toHaveProperty('position');
|
||||||
|
expect(views).toHaveProperty('name');
|
||||||
|
expect(views).toHaveProperty('objectMetadataId');
|
||||||
|
expect(views).toHaveProperty('type');
|
||||||
|
expect(views).toHaveProperty('key');
|
||||||
|
expect(views).toHaveProperty('icon');
|
||||||
|
expect(views).toHaveProperty('kanbanFieldMetadataId');
|
||||||
|
expect(views).toHaveProperty('isCompact');
|
||||||
|
expect(views).toHaveProperty('id');
|
||||||
|
expect(views).toHaveProperty('createdAt');
|
||||||
|
expect(views).toHaveProperty('updatedAt');
|
||||||
|
expect(views).toHaveProperty('deletedAt');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
packages/twenty-server/test/webhooks.integration-spec.ts
Normal file
57
packages/twenty-server/test/webhooks.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('webhooksResolver (integration)', () => {
|
||||||
|
it('should find many webhooks', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query webhooks {
|
||||||
|
webhooks {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
targetUrl
|
||||||
|
operation
|
||||||
|
description
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.webhooks;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const webhooks = edges[0].node;
|
||||||
|
|
||||||
|
expect(webhooks).toHaveProperty('targetUrl');
|
||||||
|
expect(webhooks).toHaveProperty('operation');
|
||||||
|
expect(webhooks).toHaveProperty('description');
|
||||||
|
expect(webhooks).toHaveProperty('id');
|
||||||
|
expect(webhooks).toHaveProperty('createdAt');
|
||||||
|
expect(webhooks).toHaveProperty('updatedAt');
|
||||||
|
expect(webhooks).toHaveProperty('deletedAt');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import request from 'supertest';
|
||||||
|
|
||||||
|
const client = request(`http://localhost:${APP_PORT}`);
|
||||||
|
|
||||||
|
describe('workspaceMembersResolver (integration)', () => {
|
||||||
|
it('should find many workspaceMembers', () => {
|
||||||
|
const queryData = {
|
||||||
|
query: `
|
||||||
|
query workspaceMembers {
|
||||||
|
workspaceMembers {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
colorScheme
|
||||||
|
avatarUrl
|
||||||
|
locale
|
||||||
|
timeZone
|
||||||
|
dateFormat
|
||||||
|
timeFormat
|
||||||
|
userEmail
|
||||||
|
userId
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return client
|
||||||
|
.post('/graphql')
|
||||||
|
.set('Authorization', `Bearer ${ACCESS_TOKEN}`)
|
||||||
|
.send(queryData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.data).toBeDefined();
|
||||||
|
expect(res.body.errors).toBeUndefined();
|
||||||
|
})
|
||||||
|
.expect((res) => {
|
||||||
|
const data = res.body.data.workspaceMembers;
|
||||||
|
|
||||||
|
expect(data).toBeDefined();
|
||||||
|
expect(Array.isArray(data.edges)).toBe(true);
|
||||||
|
|
||||||
|
const edges = data.edges;
|
||||||
|
|
||||||
|
if (edges.length > 0) {
|
||||||
|
const workspaceMembers = edges[0].node;
|
||||||
|
|
||||||
|
expect(workspaceMembers).toHaveProperty('id');
|
||||||
|
expect(workspaceMembers).toHaveProperty('colorScheme');
|
||||||
|
expect(workspaceMembers).toHaveProperty('avatarUrl');
|
||||||
|
expect(workspaceMembers).toHaveProperty('locale');
|
||||||
|
expect(workspaceMembers).toHaveProperty('timeZone');
|
||||||
|
expect(workspaceMembers).toHaveProperty('dateFormat');
|
||||||
|
expect(workspaceMembers).toHaveProperty('timeFormat');
|
||||||
|
expect(workspaceMembers).toHaveProperty('userEmail');
|
||||||
|
expect(workspaceMembers).toHaveProperty('userId');
|
||||||
|
expect(workspaceMembers).toHaveProperty('createdAt');
|
||||||
|
expect(workspaceMembers).toHaveProperty('updatedAt');
|
||||||
|
expect(workspaceMembers).toHaveProperty('deletedAt');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"src/*": ["packages/twenty-server/src/*"],
|
"src/*": ["packages/twenty-server/src/*"],
|
||||||
"test/*": ["packages/twenty-server/test/*"],
|
"test/*": ["packages/twenty-server/test/*"],
|
||||||
"twenty-emails": ["packages/twenty-emails/src/index.ts"]
|
"twenty-emails": ["packages/twenty-emails/dist"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
|
|||||||
5
packages/twenty-server/tsconfig.scripts.json
Normal file
5
packages/twenty-server/tsconfig.scripts.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"include": ["scripts/**/*.ts"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user