Move fixture apps from twenty-sdk to twenty-apps/fixtures (#18531)
## Summary - Move 4 test fixture apps from `twenty-sdk/src/cli/__tests__/apps/` to `twenty-apps/fixtures/` with meaningful names (`rich-app` → `postcard-app`, `root-app` → `minimal-app`) - Replace all `from '@/sdk'` imports with `from 'twenty-sdk'` so fixture apps are proper, portable twenty-sdk apps - Remove the fragile `"@/*": ["../../../../../src/*"]` tsconfig hack and replace with standard `"src/*": ["./src/*"]` paths - Create a centralized `fixture-paths.ts` utility in twenty-sdk tests for clean app path resolution ## Why The fixture apps were deeply nested in twenty-sdk's test directory and tightly coupled to its internal source layout via a tsconfig path alias hack. This made them: - Impossible to reuse outside of SDK CLI tests (e.g., for server-side dev seeding with `DevSeederService`) - Fragile — moving any twenty-sdk source file could break the path alias - Poorly discoverable — buried 5 directories deep in test infrastructure Moving them to `twenty-apps/fixtures/` makes them first-class portable apps that can be imported by `twenty-server` for seeding, used in E2E testing, and serve as canonical examples alongside `hello-world`. ## Test plan - [x] All 8 twenty-sdk integration tests pass (3 suites: postcard-app, minimal-app, invalid-app) - [x] Prettier formatting verified on all changed files - [ ] CI should confirm E2E tests also pass (these require a running server) Made with [Cursor](https://cursor.com)
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
generated
|
||||
.twenty
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineApplication } from '@/sdk';
|
||||
import { defineApplication } from 'twenty-sdk';
|
||||
|
||||
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './my.role';
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineRole } from '@/sdk';
|
||||
import { defineRole } from 'twenty-sdk';
|
||||
|
||||
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
|
||||
'9f992b6c-17e9-4381-bab5-b6150b574304';
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineLogicFunction } from '@/sdk';
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
|
||||
export const ADD_NUMBERS_UNIVERSAL_IDENTIFIER =
|
||||
'f9e5589c-e951-4d99-85db-0a305ab53502';
|
||||
+2
-2
@@ -3,13 +3,13 @@
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["../../../../../src/*"]
|
||||
"src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["**/*"],
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineApplication } from '@/sdk';
|
||||
import { defineApplication } from 'twenty-sdk';
|
||||
|
||||
export default defineApplication({
|
||||
universalIdentifier: 'invalid-app-0000-0000-0000-000000000001',
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineObject, FieldType } from '@/sdk';
|
||||
import { defineObject, FieldType } from 'twenty-sdk';
|
||||
|
||||
const DUPLICATE_ID = 'duplicate-id-0000-0000-000000000001';
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineObject, FieldType } from '@/sdk';
|
||||
import { defineObject, FieldType } from 'twenty-sdk';
|
||||
|
||||
const DUPLICATE_ID = 'duplicate-id-0000-0000-000000000001';
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["../../../../../src/*"]
|
||||
"src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["**/*"],
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineApplication } from '@/sdk';
|
||||
import { defineApplication } from 'twenty-sdk';
|
||||
|
||||
export default defineApplication({
|
||||
universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000001',
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineFrontComponent } from '@/sdk';
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
|
||||
export const MyComponent = () => {
|
||||
return (
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineLogicFunction } from '@/sdk';
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
|
||||
const myHandler = () => {
|
||||
return 'my-function-result';
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { FieldType, defineObject } from '@/sdk';
|
||||
import { FieldType, defineObject } from 'twenty-sdk';
|
||||
|
||||
export default defineObject({
|
||||
universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000030',
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineRole } from '@/sdk';
|
||||
import { defineRole } from 'twenty-sdk';
|
||||
|
||||
export default defineRole({
|
||||
universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000040',
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "rich-app",
|
||||
"name": "minimal-app",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
+3
-2
@@ -9,8 +9,9 @@
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["../../../../../src/*"]
|
||||
"src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["**/*"]
|
||||
"include": ["**/*"],
|
||||
"exclude": ["node_modules", "dist", ".twenty"]
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineApplication } from '@/sdk';
|
||||
import { defineApplication } from 'twenty-sdk';
|
||||
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from './src/roles/default-function.role';
|
||||
|
||||
export default defineApplication({
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "root-app",
|
||||
"name": "postcard-app",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
Before Width: | Height: | Size: 570 B After Width: | Height: | Size: 570 B |
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineFrontComponent } from '@/sdk';
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
import { CardDisplay } from '../utils/card-display.component';
|
||||
|
||||
export default defineFrontComponent({
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineFrontComponent } from '@/sdk';
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
import { DEFAULT_NAME, formatGreeting } from '../utils/greeting.util';
|
||||
|
||||
const GreetingComponent = () => {
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineFrontComponent } from '@/sdk';
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
|
||||
export const TestComponent = () => {
|
||||
return (
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
import {
|
||||
POST_CARD_ON_POST_CARD_RECIPIENT_ID,
|
||||
POST_CARD_RECIPIENTS_ON_POST_CARD_ID,
|
||||
} from '@/cli/__tests__/apps/rich-app/src/fields/post-card-recipients-on-post-card.field';
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/post-card-recipient.object';
|
||||
import { defineField, FieldType, OnDeleteAction, RelationType } from '@/sdk';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/post-card.object';
|
||||
} from './post-card-recipients-on-post-card.field';
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
|
||||
import { defineField, FieldType, OnDeleteAction, RelationType } from 'twenty-sdk';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
|
||||
|
||||
export default defineField({
|
||||
universalIdentifier: POST_CARD_ON_POST_CARD_RECIPIENT_ID,
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/post-card-recipient.object';
|
||||
import { defineField, FieldType, RelationType } from '@/sdk';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/post-card.object';
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
|
||||
import { defineField, FieldType, RelationType } from 'twenty-sdk';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
|
||||
|
||||
export const POST_CARD_RECIPIENTS_ON_POST_CARD_ID =
|
||||
'a1a2b3c4-0001-4a7b-8c9d-0e1f2a3b4c5d';
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import { defineField, FieldType, RelationType } from '@/sdk';
|
||||
import { RECIPIENT_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/recipient.object';
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/post-card-recipient.object';
|
||||
import { defineField, FieldType, RelationType } from 'twenty-sdk';
|
||||
import { RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/recipient.object';
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
|
||||
|
||||
export const POST_CARD_RECIPIENTS_ON_RECIPIENT_ID =
|
||||
'a1a2b3c4-0003-4a7b-8c9d-0e1f2a3b4c5d';
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { defineField, FieldType } from '@/sdk';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/post-card.object';
|
||||
import { defineField, FieldType } from 'twenty-sdk';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
|
||||
|
||||
export default defineField({
|
||||
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { defineField, FieldType } from '@/sdk';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/post-card.object';
|
||||
import { defineField, FieldType } from 'twenty-sdk';
|
||||
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
|
||||
|
||||
export default defineField({
|
||||
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
import { defineField, FieldType, RelationType, OnDeleteAction } from '@/sdk';
|
||||
import { RECIPIENT_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/recipient.object';
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '@/cli/__tests__/apps/rich-app/src/objects/post-card-recipient.object';
|
||||
import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk';
|
||||
import { RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/recipient.object';
|
||||
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
|
||||
import {
|
||||
POST_CARD_RECIPIENTS_ON_RECIPIENT_ID,
|
||||
RECIPIENT_ON_POST_CARD_RECIPIENT_ID,
|
||||
} from '@/cli/__tests__/apps/rich-app/src/fields/post-card-recipients-on-recipient.field';
|
||||
} from './post-card-recipients-on-recipient.field';
|
||||
|
||||
export default defineField({
|
||||
universalIdentifier: RECIPIENT_ON_POST_CARD_RECIPIENT_ID,
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineLogicFunction } from '@/sdk';
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import { DEFAULT_NAME, formatGreeting } from '../utils/greeting.util';
|
||||
|
||||
const greetingHandler = () => {
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineLogicFunction } from '@/sdk';
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
|
||||
const handler = async (params: { recipientName: string }) => {
|
||||
return { found: true, name: params.recipientName };
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineLogicFunction } from '@/sdk';
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
|
||||
const handler = async () => {
|
||||
return { processed: true };
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineLogicFunction } from '@/sdk';
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import { testFunction2 } from '../utils/test-function-2.util';
|
||||
|
||||
export default defineLogicFunction({
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineLogicFunction } from '@/sdk';
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import { formatFarewell } from '../utils/greeting.util';
|
||||
|
||||
const handler = () => {
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { defineNavigationMenuItem } from '@/sdk';
|
||||
import { ALL_POST_CARD_RECIPIENTS_VIEW_ID } from '@/cli/__tests__/apps/rich-app/src/views/all-post-card-recipients.view';
|
||||
import { defineNavigationMenuItem } from 'twenty-sdk';
|
||||
import { ALL_POST_CARD_RECIPIENTS_VIEW_ID } from '../views/all-post-card-recipients.view';
|
||||
|
||||
export default defineNavigationMenuItem({
|
||||
universalIdentifier: 'c1a2b3c4-0003-4a7b-8c9d-0e1f2a3b4c5d',
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { defineNavigationMenuItem } from '@/sdk';
|
||||
import { ALL_POST_CARDS_VIEW_ID } from '@/cli/__tests__/apps/rich-app/src/views/all-post-cards.view';
|
||||
import { defineNavigationMenuItem } from 'twenty-sdk';
|
||||
import { ALL_POST_CARDS_VIEW_ID } from '../views/all-post-cards.view';
|
||||
|
||||
export default defineNavigationMenuItem({
|
||||
universalIdentifier: 'c1a2b3c4-0001-4a7b-8c9d-0e1f2a3b4c5d',
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { defineNavigationMenuItem } from '@/sdk';
|
||||
import { ALL_RECIPIENTS_VIEW_ID } from '@/cli/__tests__/apps/rich-app/src/views/all-recipients.view';
|
||||
import { defineNavigationMenuItem } from 'twenty-sdk';
|
||||
import { ALL_RECIPIENTS_VIEW_ID } from '../views/all-recipients.view';
|
||||
|
||||
export default defineNavigationMenuItem({
|
||||
universalIdentifier: 'c1a2b3c4-0002-4a7b-8c9d-0e1f2a3b4c5d',
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineObject, FieldType } from '@/sdk';
|
||||
import { defineObject, FieldType } from 'twenty-sdk';
|
||||
|
||||
export const POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER =
|
||||
'e1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5e';
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineObject, FieldType } from '@/sdk';
|
||||
import { defineObject, FieldType } from 'twenty-sdk';
|
||||
|
||||
enum PostCardStatus {
|
||||
DRAFT = 'DRAFT',
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineObject, FieldType } from '@/sdk';
|
||||
import { defineObject, FieldType } from 'twenty-sdk';
|
||||
|
||||
export const RECIPIENT_UNIVERSAL_IDENTIFIER =
|
||||
'd1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5d';
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { PermissionFlag, defineRole } from '@/sdk';
|
||||
import { PermissionFlag, defineRole } from 'twenty-sdk';
|
||||
import {
|
||||
CONTENT_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
POST_CARD_UNIVERSAL_IDENTIFIER,
|
||||
} from '@/cli/__tests__/apps/rich-app/src/objects/post-card.object';
|
||||
} from '../objects/post-card.object';
|
||||
|
||||
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
|
||||
'b648f87b-1d26-4961-b974-0908fd991061';
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineFrontComponent } from '@/sdk';
|
||||
import { defineFrontComponent } from 'twenty-sdk';
|
||||
|
||||
export const RootComponent = () => {
|
||||
return (
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineLogicFunction } from '@/sdk';
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
|
||||
const rootHandler = () => {
|
||||
return 'root-function-result';
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineObject, FieldType } from '@/sdk';
|
||||
import { defineObject, FieldType } from 'twenty-sdk';
|
||||
|
||||
export default defineObject({
|
||||
universalIdentifier: 'b0b1b2b3-b4b5-4000-8000-000000000001',
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { defineRole } from '@/sdk';
|
||||
import { defineRole } from 'twenty-sdk';
|
||||
|
||||
export default defineRole({
|
||||
universalIdentifier: 'c0c1c2c3-c4c5-4000-8000-000000000001',
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
|
||||
SENT_AT_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
} from '@/cli/__tests__/apps/rich-app/src/objects/post-card-recipient.object';
|
||||
import { defineView } from '@/sdk';
|
||||
} from '../objects/post-card-recipient.object';
|
||||
import { defineView } from 'twenty-sdk';
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
|
||||
export const ALL_POST_CARD_RECIPIENTS_VIEW_ID =
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { defineView } from '@/sdk';
|
||||
import { defineView } from 'twenty-sdk';
|
||||
import {
|
||||
CONTENT_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
POST_CARD_UNIVERSAL_IDENTIFIER,
|
||||
} from '@/cli/__tests__/apps/rich-app/src/objects/post-card.object';
|
||||
} from '../objects/post-card.object';
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
|
||||
export const ALL_POST_CARDS_VIEW_ID = 'b1a2b3c4-0001-4a7b-8c9d-0e1f2a3b4c5d';
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
RECIPIENT_EMAIL_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
RECIPIENT_UNIVERSAL_IDENTIFIER,
|
||||
} from '@/cli/__tests__/apps/rich-app/src/objects/recipient.object';
|
||||
import { defineView } from '@/sdk';
|
||||
} from '../objects/recipient.object';
|
||||
import { defineView } from 'twenty-sdk';
|
||||
import { ViewType } from 'twenty-shared/types';
|
||||
|
||||
export const ALL_RECIPIENTS_VIEW_ID = 'b1a2b3c4-0002-4a7b-8c9d-0e1f2a3b4c5d';
|
||||
+3
-2
@@ -9,8 +9,9 @@
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["../../../../../src/*"]
|
||||
"src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["**/*"]
|
||||
"include": ["**/*"],
|
||||
"exclude": ["node_modules", "dist", ".twenty"]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { resolve } from 'path';
|
||||
|
||||
const FIXTURES_ROOT = resolve(__dirname, '../../../../../twenty-apps/fixtures');
|
||||
|
||||
export const POSTCARD_APP_PATH = resolve(FIXTURES_ROOT, 'postcard-app');
|
||||
export const MINIMAL_APP_PATH = resolve(FIXTURES_ROOT, 'minimal-app');
|
||||
export const INVALID_APP_PATH = resolve(FIXTURES_ROOT, 'invalid-app');
|
||||
export const FUNCTION_EXECUTE_APP_PATH = resolve(
|
||||
FIXTURES_ROOT,
|
||||
'function-execute-app',
|
||||
);
|
||||
+4
-5
@@ -1,12 +1,13 @@
|
||||
import { resolve } from 'path';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
import { appBuild } from '@/cli/public-operations/app-build';
|
||||
import { appUninstall } from '@/cli/public-operations/app-uninstall';
|
||||
import { functionExecute } from '@/cli/public-operations/function-execute';
|
||||
import { ADD_NUMBERS_UNIVERSAL_IDENTIFIER } from '../src/logic-functions/add-numbers.function';
|
||||
import { FUNCTION_EXECUTE_APP_PATH } from '@/cli/__tests__/apps/fixture-paths';
|
||||
|
||||
const APP_PATH = resolve(__dirname, '../');
|
||||
const ADD_NUMBERS_UNIVERSAL_IDENTIFIER = 'f9e5589c-e951-4d99-85db-0a305ab53502';
|
||||
|
||||
const APP_PATH = FUNCTION_EXECUTE_APP_PATH;
|
||||
|
||||
describe('functionExecute E2E', () => {
|
||||
beforeAll(async () => {
|
||||
@@ -18,8 +19,6 @@ describe('functionExecute E2E', () => {
|
||||
);
|
||||
}
|
||||
|
||||
// The server may need a moment to make uploaded files readable
|
||||
// by the execution engine. Retry until the handler becomes available.
|
||||
await vi.waitFor(
|
||||
async () => {
|
||||
const result = await functionExecute({
|
||||
|
||||
+7
-3
@@ -3,14 +3,18 @@ import { OUTPUT_DIR } from 'twenty-shared/application';
|
||||
|
||||
import { runAppDevInProcess } from '@/cli/__tests__/integration/utils/run-app-dev-in-process.util';
|
||||
import { pathExists } from '@/cli/utilities/file/fs-utils';
|
||||
import { INVALID_APP_PATH } from '@/cli/__tests__/apps/fixture-paths';
|
||||
|
||||
const APP_PATH = join(__dirname, '..');
|
||||
const MANIFEST_OUTPUT_PATH = join(APP_PATH, OUTPUT_DIR, 'manifest.json');
|
||||
const MANIFEST_OUTPUT_PATH = join(
|
||||
INVALID_APP_PATH,
|
||||
OUTPUT_DIR,
|
||||
'manifest.json',
|
||||
);
|
||||
|
||||
describe('invalid-app manifest', () => {
|
||||
it('should fail to build manifest due to duplicate universalIdentifier', async () => {
|
||||
const result = await runAppDevInProcess({
|
||||
appPath: APP_PATH,
|
||||
appPath: INVALID_APP_PATH,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
|
||||
+7
-9
@@ -1,15 +1,13 @@
|
||||
import { join } from 'path';
|
||||
|
||||
import { runAppDevInProcess } from '@/cli/__tests__/integration/utils/run-app-dev-in-process.util';
|
||||
import { MINIMAL_APP_PATH } from '@/cli/__tests__/apps/fixture-paths';
|
||||
|
||||
import { defineFrontComponentsTests } from './tests/front-components.tests';
|
||||
import { defineLogicFunctionsTests } from './tests/logic-functions.tests';
|
||||
import { defineManifestTests } from './tests/manifest.tests';
|
||||
|
||||
const APP_PATH = join(__dirname, '../..');
|
||||
|
||||
describe('root-app app:dev', () => {
|
||||
describe('minimal-app app:dev', () => {
|
||||
beforeAll(async () => {
|
||||
const result = await runAppDevInProcess({ appPath: APP_PATH });
|
||||
const result = await runAppDevInProcess({ appPath: MINIMAL_APP_PATH });
|
||||
|
||||
if (!result.success) {
|
||||
const diagnostics = JSON.stringify(
|
||||
@@ -24,7 +22,7 @@ describe('root-app app:dev', () => {
|
||||
}
|
||||
}, 60000);
|
||||
|
||||
defineManifestTests(APP_PATH);
|
||||
defineLogicFunctionsTests(APP_PATH);
|
||||
defineFrontComponentsTests(APP_PATH);
|
||||
defineManifestTests(MINIMAL_APP_PATH);
|
||||
defineLogicFunctionsTests(MINIMAL_APP_PATH);
|
||||
defineFrontComponentsTests(MINIMAL_APP_PATH);
|
||||
});
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { join } from 'path';
|
||||
import { type Manifest } from 'twenty-shared/application';
|
||||
|
||||
import { EXPECTED_MANIFEST } from '@/cli/__tests__/apps/root-app/__integration__/app-dev/expected-manifest';
|
||||
import { EXPECTED_MANIFEST } from '@/cli/__tests__/apps/minimal-app/__integration__/app-dev/expected-manifest';
|
||||
import { normalizeManifestForComparison } from '@/cli/__tests__/integration/utils/normalize-manifest.util';
|
||||
import { pathExists, readJson } from '@/cli/utilities/file/fs-utils';
|
||||
|
||||
+5
-4
@@ -1,14 +1,15 @@
|
||||
import { OUTPUT_DIR } from 'twenty-shared/application';
|
||||
import { existsSync } from 'fs';
|
||||
import { join, resolve } from 'path';
|
||||
import { join } from 'path';
|
||||
import { inspect } from 'util';
|
||||
import { runCliCommand } from '@/cli/__tests__/integration/utils/run-cli-command.util';
|
||||
import { POSTCARD_APP_PATH } from '@/cli/__tests__/apps/fixture-paths';
|
||||
|
||||
inspect.defaultOptions.depth = 10;
|
||||
|
||||
describe('Application: install delete and reinstall rich-app', () => {
|
||||
const applicationName = 'rich-app';
|
||||
const appPath = resolve(__dirname, '../');
|
||||
describe('Application: install delete and reinstall postcard-app', () => {
|
||||
const applicationName = 'postcard-app';
|
||||
const appPath = POSTCARD_APP_PATH;
|
||||
|
||||
beforeAll(async () => {
|
||||
expect(existsSync(appPath)).toBe(true);
|
||||
+5
-7
@@ -1,14 +1,12 @@
|
||||
import { runAppDevInProcess } from '@/cli/__tests__/integration/utils/run-app-dev-in-process.util';
|
||||
import { join } from 'path';
|
||||
import { POSTCARD_APP_PATH } from '@/cli/__tests__/apps/fixture-paths';
|
||||
|
||||
import { defineEntitiesTests } from './tests/entities.tests';
|
||||
import { defineManifestTests } from './tests/manifest.tests';
|
||||
|
||||
const APP_PATH = join(__dirname, '../..');
|
||||
|
||||
describe('rich-app app:dev', () => {
|
||||
describe('postcard-app app:dev', () => {
|
||||
beforeAll(async () => {
|
||||
const result = await runAppDevInProcess({ appPath: APP_PATH });
|
||||
const result = await runAppDevInProcess({ appPath: POSTCARD_APP_PATH });
|
||||
|
||||
if (!result.success) {
|
||||
const diagnostics = JSON.stringify(
|
||||
@@ -23,6 +21,6 @@ describe('rich-app app:dev', () => {
|
||||
}
|
||||
}, 60000);
|
||||
|
||||
defineManifestTests(APP_PATH);
|
||||
defineEntitiesTests(APP_PATH);
|
||||
defineManifestTests(POSTCARD_APP_PATH);
|
||||
defineEntitiesTests(POSTCARD_APP_PATH);
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { appBuild } from '@/cli/public-operations/app-build';
|
||||
import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/utilities/config/current-execution-directory';
|
||||
import { checkSdkVersionCompatibility } from '@/cli/utilities/version/check-sdk-version-compatibility';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export type AppBuildCommandOptions = {
|
||||
@@ -11,6 +12,8 @@ export class AppBuildCommand {
|
||||
async execute(options: AppBuildCommandOptions): Promise<void> {
|
||||
const appPath = options.appPath ?? CURRENT_EXECUTION_DIRECTORY;
|
||||
|
||||
await checkSdkVersionCompatibility(appPath);
|
||||
|
||||
console.log(chalk.blue('Building application...'));
|
||||
console.log(chalk.gray(`App path: ${appPath}`));
|
||||
console.log('');
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/utilities/config/current-execution-directory';
|
||||
import { renderDevUI } from '@/cli/utilities/dev/ui/components/dev-ui';
|
||||
import { DevUiStateManager } from '@/cli/utilities/dev/ui/dev-ui-state-manager';
|
||||
import { DevModeOrchestrator } from '@/cli/utilities/dev/orchestrator/dev-mode-orchestrator';
|
||||
import { OrchestratorState } from '@/cli/utilities/dev/orchestrator/dev-mode-orchestrator-state';
|
||||
import { renderDevUI } from '@/cli/utilities/dev/ui/components/dev-ui';
|
||||
import { DevUiStateManager } from '@/cli/utilities/dev/ui/dev-ui-state-manager';
|
||||
import { checkSdkVersionCompatibility } from '@/cli/utilities/version/check-sdk-version-compatibility';
|
||||
|
||||
export type AppDevOptions = {
|
||||
appPath?: string;
|
||||
@@ -25,6 +26,8 @@ export class AppDevCommand {
|
||||
async execute(options: AppDevOptions): Promise<void> {
|
||||
const appPath = options.appPath ?? CURRENT_EXECUTION_DIRECTORY;
|
||||
|
||||
await checkSdkVersionCompatibility(appPath);
|
||||
|
||||
const orchestratorState = new OrchestratorState({
|
||||
appPath,
|
||||
frontendUrl: process.env.FRONTEND_URL,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { appPublish } from '@/cli/public-operations/app-publish';
|
||||
import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/utilities/config/current-execution-directory';
|
||||
import { checkSdkVersionCompatibility } from '@/cli/utilities/version/check-sdk-version-compatibility';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export type AppPublishCommandOptions = {
|
||||
@@ -12,6 +13,9 @@ export type AppPublishCommandOptions = {
|
||||
export class AppPublishCommand {
|
||||
async execute(options: AppPublishCommandOptions): Promise<void> {
|
||||
const appPath = options.appPath ?? CURRENT_EXECUTION_DIRECTORY;
|
||||
|
||||
await checkSdkVersionCompatibility(appPath);
|
||||
|
||||
const isServerPublish = !!options.server;
|
||||
|
||||
console.log(
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'path';
|
||||
|
||||
import { pathExists } from '@/cli/utilities/file/fs-utils';
|
||||
import chalk from 'chalk';
|
||||
import sdkPackageJson from '../../../../package.json';
|
||||
|
||||
const getAppDeclaredSdkVersion = async (
|
||||
appPath: string,
|
||||
): Promise<string | undefined> => {
|
||||
const pkgPath = join(appPath, 'package.json');
|
||||
|
||||
if (!(await pathExists(pkgPath))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await readFile(pkgPath, 'utf-8');
|
||||
const pkg = JSON.parse(content) as {
|
||||
dependencies?: Record<string, string>;
|
||||
devDependencies?: Record<string, string>;
|
||||
};
|
||||
|
||||
return (
|
||||
pkg.dependencies?.['twenty-sdk'] ??
|
||||
pkg.devDependencies?.['twenty-sdk'] ??
|
||||
undefined
|
||||
);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// Extracts the major version number from a semver string or range.
|
||||
// Handles: "4.2.0", "^4.2.0", "~4.2.0", ">=4.0.0", "4.x", "4"
|
||||
// Returns null for unparseable values like "latest", "*", "workspace:*"
|
||||
const parseMajorVersion = (versionOrRange: string): number | null => {
|
||||
const match = versionOrRange.match(/(\d+)\./);
|
||||
|
||||
if (match) {
|
||||
return parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
const exactMatch = versionOrRange.match(/^[~^>=<]*(\d+)$/);
|
||||
|
||||
if (exactMatch) {
|
||||
return parseInt(exactMatch[1], 10);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const checkSdkVersionCompatibility = async (
|
||||
appPath: string,
|
||||
): Promise<void> => {
|
||||
const cliVersion = sdkPackageJson.version;
|
||||
const appDeclaredVersion = await getAppDeclaredSdkVersion(appPath);
|
||||
|
||||
if (!cliVersion || !appDeclaredVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cliMajor = parseMajorVersion(cliVersion);
|
||||
const appMajor = parseMajorVersion(appDeclaredVersion);
|
||||
|
||||
if (cliMajor === null || appMajor === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cliMajor !== appMajor) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
`⚠ Version mismatch: your app requires twenty-sdk@${appMajor}.x ` +
|
||||
`but the CLI running is v${cliVersion}. ` +
|
||||
`Major version mismatches may cause unexpected behavior.\n` +
|
||||
` Update with: npm install -g twenty-sdk@${appMajor}`,
|
||||
),
|
||||
);
|
||||
console.warn('');
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user