diff --git a/package.json b/package.json index d4ba905..b17c0d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@trufflehq/cli", - "version": "0.6.1", + "version": "0.6.7", "description": "The Truffle Developer Platform CLI", "main": "dist/cli.mjs", "bin": { diff --git a/src/constants/app-config-schema.ts b/src/constants/app-config-schema.ts index 74357b1..912a570 100644 --- a/src/constants/app-config-schema.ts +++ b/src/constants/app-config-schema.ts @@ -19,12 +19,21 @@ export const OPERATION_TYPES = [ 'webhook', 'workflow', 'exchange', + 'apply-powerup', // TODO: support these // 'conditional', - // 'apply-powerup', ] as const; export type OperationType = (typeof OPERATION_TYPES)[number]; +export const ASSET_PARTICIPANT_ENTITY_TYPES = [ + 'user', + 'org-member', + 'org', + 'company', +] as const; +export type AssetParticipantEntityType = + (typeof ASSET_PARTICIPANT_ENTITY_TYPES)[number]; + const COUNTABLE_SCHEMA = Joi.object({ slug: Joi.string().required(), name: Joi.string().optional(), @@ -63,35 +72,108 @@ export const EMBED_SCHEMA = Joi.object({ minTruffleVersion: Joi.string().optional(), maxTruffleVersion: Joi.string().optional(), deviceType: Joi.string().valid('desktop', 'mobile').optional(), + parentQuerySelector: Joi.string().optional(), status: Joi.string() .valid('published', 'experimental', 'disabled') .optional(), }); -export const ASSET_SCHEMA = Joi.object({ - path: Joi.string().required(), - quantity: Joi.number().required(), +const POWERUP_SCHEMA = Joi.object({ + slug: Joi.string().required(), + name: Joi.string().optional(), + data: Joi.object().optional(), + imageFileReference: Joi.object().optional(), }); +const ASSET_PARTICIPANT_TEMPLATE_SCHEMA = Joi.object({ + entityType: Joi.string() + .valid(...ASSET_PARTICIPANT_ENTITY_TYPES) + .required(), + entityId: Joi.string().required(), + share: Joi.number().required(), +}); + +const ASSET_TEMPLATE_SCHEMA = Joi.object({ + entityType: Joi.string().valid('countable', 'fiat'), + entityId: Joi.string(), + entityPath: Joi.string(), + count: Joi.alternatives() + .try( + Joi.number().required(), + Joi.string().valid('{{USE_PROVIDED}}').required(), + ) + .required(), + metadata: Joi.object().optional(), + senders: Joi.array().items(ASSET_PARTICIPANT_TEMPLATE_SCHEMA).required(), + receivers: Joi.array().items(ASSET_PARTICIPANT_TEMPLATE_SCHEMA).required(), +}) + // this makes it so that either entityId and entityType is required or entityPath is required + .with('entityId', 'entityType') + .xor('entityPath', 'entityId'); + export const ACTION_SCHEMA = Joi.object({ operation: Joi.string() .valid(...OPERATION_TYPES) .required(), + + name: Joi.string().optional(), + + // webhook inputs url: Joi.when('operation', { is: 'webhook', then: Joi.string().required(), }), - actions: Joi.when('operation', { - is: 'workflow', - then: Joi.array().items(Joi.link('#actionSchema')).required(), + data: Joi.when('operation', { + is: 'webhook', + then: Joi.alternatives() + .try(Joi.object(), Joi.string().valid('{{USE_PROVIDED}}')) + .optional(), }), + // workflow inputs strategy: Joi.when('operation', { is: 'workflow', then: Joi.string().valid('sequential', 'parallel').required(), }), + actions: Joi.when('operation', { + is: 'workflow', + then: Joi.array().items(Joi.link('#actionSchema')).required(), + }), + + // exchange inputs + assets: Joi.when('operation', { + is: 'exchange', + then: Joi.alternatives() + .try( + Joi.string().valid('{{USE_SECURE_PROVIDED}}').required(), + Joi.array().items(ASSET_TEMPLATE_SCHEMA).required(), + ) + .required(), + }), + + // apply-pwerup inputs + powerup: Joi.when('operation', { + is: 'apply-powerup', + then: Joi.alternatives().try(Joi.string(), POWERUP_SCHEMA).required(), + }), + + targetType: Joi.when('operation', { + is: 'apply-powerup', + then: Joi.string().required(), + }), + + targetId: Joi.when('operation', { + is: 'apply-powerup', + then: Joi.string().required(), + }), + + ttlSeconds: Joi.when('operation', { + is: 'apply-powerup', + then: Joi.number().required(), + }), + inputsTemplate: Joi.object().optional(), isDirectExecutionAllowed: Joi.boolean().optional(), @@ -108,9 +190,7 @@ export const PRODUCT_VARIANT_SCHEMA = Joi.object({ name: Joi.string().optional(), price: Joi.number().required(), description: Joi.string().optional(), - action: ACTION_SCHEMA.optional(), - assets: Joi.array().items(ASSET_SCHEMA).optional(), }); @@ -131,4 +211,8 @@ export const APP_CONFIG_SCHEMA = Joi.object({ countables: Joi.array().items(COUNTABLE_SCHEMA).optional(), products: Joi.array().items(PRODUCT_SCHEMA).optional(), actions: Joi.array().items(ACTION_WITH_SLUG_SCHEMA).optional(), + powerups: Joi.array().items(POWERUP_SCHEMA).optional(), + postInstallAction: Joi.alternatives() + .try(Joi.string(), ACTION_SCHEMA) + .optional(), }); diff --git a/src/types/mt-app-config.ts b/src/types/mt-app-config.ts index ea14d1b..0b20b04 100644 --- a/src/types/mt-app-config.ts +++ b/src/types/mt-app-config.ts @@ -9,6 +9,8 @@ export interface MothertreeAppConfig { countables: MothertreeCountableConfig[]; products: MothertreeProductConfig[]; productVariants: MothertreeProductVariantConfig[]; + powerups: MothertreePowerupConfig[]; + postInstallActionPath?: string; } export interface MothertreeEmbedConfig { @@ -96,3 +98,10 @@ export interface MothertreeAssetParticipantTemplate { entityId: string | '{{USE_PROVIDED}}' | '{{USE_USER_ID}}' | '{{USE_ORG_ID}}'; share: number; } + +export interface MothertreePowerupConfig { + slug: string; + name?: string; + data?: object; + imageFileReference?: object; +} diff --git a/src/util/app-config.ts b/src/util/app-config.ts index 6c18f70..d413edd 100644 --- a/src/util/app-config.ts +++ b/src/util/app-config.ts @@ -44,6 +44,10 @@ export function makeLocalActionPath(actionSlug: string) { return `./_Action/${actionSlug}`; } +export function makeLocalPowerupPath(powerupSlug: string) { + return `./_Powerup/${powerupSlug}`; +} + type ActionConfig = NonNullable[number]; type EmbeddedActionConfig = Omit; @@ -54,40 +58,71 @@ export function convertActionConfigsToMothertreeActionConfigs( mtAppConfig.actions.push( ...actionConfigs.map((actionConfig) => { let inputsTemplate: Record; - if (actionConfig.operation === 'workflow') { - inputsTemplate = { - // if the action is a workflow, we need to create a new action for each sub-action - // and add the sub-action paths to the inputsTemplate - actionPaths: actionConfig.actions.map( - // a sub action could either be an action path or an embedded action - (subAction: EmbeddedActionConfig | string, subActionIdx) => { - // if the sub-action is a string, it's an action path, so just return it - if (typeof subAction === 'string') { - return subAction; - } - - // if the sub-action is an embedded action, we need to create a new action - const slug = `${actionConfig.slug}-step-${subActionIdx}`; - - // add the sub-action to the actions array - convertActionConfigsToMothertreeActionConfigs( - [{ ...subAction, slug }], - mtAppConfig, - ); - - return makeLocalActionPath(slug); - }, - ), - - // pass along the strategy - strategy: actionConfig.strategy, - }; - } else { - // if the action is not a workflow, we can just fill in url and assets - inputsTemplate = { - ...actionConfig.inputsTemplate, - ..._.pick(actionConfig, ['url', 'assets']), - }; + switch (actionConfig.operation) { + case 'workflow': { + inputsTemplate = { + // if the action is a workflow, we need to create a new action for each sub-action + // and add the sub-action paths to the inputsTemplate + actionPaths: actionConfig.actions.map( + // a sub action could either be an action path or an embedded action + (subAction: EmbeddedActionConfig | string, subActionIdx) => { + // if the sub-action is a string, it's an action path, so just return it + if (typeof subAction === 'string') { + return subAction; + } + + // if the sub-action is an embedded action, we need to create a new action + const slug = `${actionConfig.slug}-step-${subActionIdx}`; + + // add the sub-action to the actions array + convertActionConfigsToMothertreeActionConfigs( + [{ ...subAction, slug }], + mtAppConfig, + ); + + return makeLocalActionPath(slug); + }, + ), + + // pass along the strategy + strategy: actionConfig.strategy, + }; + break; + } + + case 'apply-powerup': { + // actionConfig.powerup is either a string or an object containing a powerup config + let powerupPath: string = ''; + if (typeof actionConfig.powerup === 'string') { + powerupPath = actionConfig.powerup; + } else { + mtAppConfig.powerups.push(actionConfig.powerup); + powerupPath = makeLocalPowerupPath(actionConfig.powerup.slug); + } + + inputsTemplate = { + powerupPath: powerupPath, + targetType: actionConfig.targetType, + targetId: actionConfig.targetId, + ttlSeconds: actionConfig.ttlSeconds, + }; + break; + } + + case 'webhook': { + inputsTemplate = { + url: actionConfig.url, + data: actionConfig.data, + }; + break; + } + + case 'exchange': { + inputsTemplate = { + assets: actionConfig.assets, + }; + break; + } } return { @@ -180,7 +215,8 @@ export function convertProductConfigsToMothertreeProductAndVariantConfigs( // tbh I don't know why we have to cast this } as MothertreeAssetParticipantTemplate, ], - // TODO: if we want, we can change this to do rev/share split with org, us, and dev + // TODO: if we want, we can change this to do rev/share split with org, us, and dev... + // this might not be the place to do that though receivers: [ { entityType: 'org', @@ -213,8 +249,9 @@ export function convertAppConfigToMothertreeConfig(appConfig: AppConfig) { name: appConfig.name, cliVersion: appConfig.cliVersion, embeds: appConfig.embeds ?? [], - actions: [], countables: appConfig.countables ?? [], + powerups: appConfig.powerups ?? [], + actions: [], products: [], productVariants: [], }; @@ -226,6 +263,30 @@ export function convertAppConfigToMothertreeConfig(appConfig: AppConfig) { ); } + if (typeof appConfig.postInstallAction === 'string') { + mtAppConfig.postInstallActionPath = appConfig.postInstallAction; + } + // if postInstallAction is an object, we need to convert it to a MothertreeActionConfig, + // and add it to the actions array, and set the postInstallActionPath + else if (appConfig.postInstallAction != null) { + // add a slug to the action config + const postInstallAction = { + ...appConfig.postInstallAction, + slug: 'post-install-action', + }; + + // add the action to the actions array + convertActionConfigsToMothertreeActionConfigs( + [postInstallAction], + mtAppConfig, + ); + + // set the postInstallActionPath + mtAppConfig.postInstallActionPath = makeLocalActionPath( + postInstallAction.slug, + ); + } + if (appConfig.products) { convertProductConfigsToMothertreeProductAndVariantConfigs( appConfig.products, diff --git a/tests/unit/__snapshots__/app-config-schema.test.ts.snap b/tests/unit/__snapshots__/app-config-schema.test.ts.snap index 2f396f1..e052614 100644 --- a/tests/unit/__snapshots__/app-config-schema.test.ts.snap +++ b/tests/unit/__snapshots__/app-config-schema.test.ts.snap @@ -2,6 +2,34 @@ exports[`app-config-schema > ACTION_SCHEMA > should throw an error if a sub-action is invalid 1`] = `[ValidationError: "actions[0].url" is required]`; +exports[`app-config-schema > ACTION_SCHEMA > should throw an error if an apply-powerup action is invalid 1`] = ` +[ValidationError: { + "operation": "apply-powerup", + "targetType": "chat", + "targetId": "chatId", + "ttlSeconds": 60, + "powerup" [1]: -- missing -- +} + +[1] "powerup" is required] +`; + +exports[`app-config-schema > ACTION_SCHEMA > should throw an error if data is an invalid string 1`] = `[ValidationError: "data" must be one of [object, {{USE_PROVIDED}}]]`; + +exports[`app-config-schema > APP_CONFIG_SCHEMA > should throw an error if postInstallAction is invalid 1`] = ` +[ValidationError: { + "path": "@truffle/test-app", + "name": "test-app", + "cliVersion": "1.0.0", + "postInstallAction": { + "operation": "webhook", + "url" [1]: -- missing -- + } +} + +[1] "postInstallAction.url" is required] +`; + exports[`app-config-schema > EMBED_SCHEMA > should throw an error if contentPageType is invalid 1`] = ` [ValidationError: { "slug": "test-embed", diff --git a/tests/unit/__snapshots__/app-config.test.ts.snap b/tests/unit/__snapshots__/app-config.test.ts.snap index f536c4c..f126565 100644 --- a/tests/unit/__snapshots__/app-config.test.ts.snap +++ b/tests/unit/__snapshots__/app-config.test.ts.snap @@ -1,12 +1,88 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`app-config > convertActionConfigsToMothertreeActionConfigs > should convert a basic apply-powerup action with a powerup object 1`] = ` +{ + "actions": [ + { + "inputsTemplate": { + "powerupPath": "./_Powerup/test-powerup", + "targetId": "test-target-id", + "targetType": "test-target-type", + "ttlSeconds": 60, + }, + "isDirectExecutionAllowed": false, + "operation": "apply-powerup", + "slug": "test-action", + }, + ], + "powerups": [ + { + "name": "Test Powerup", + "slug": "test-powerup", + }, + ], +} +`; + +exports[`app-config > convertActionConfigsToMothertreeActionConfigs > should convert a basic apply-powerup action with a powerup path 1`] = ` +[ + { + "inputsTemplate": { + "powerupPath": "@truffle/app/_Powerup/test-powerup", + "targetId": "test-target-id", + "targetType": "test-target-type", + "ttlSeconds": 60, + }, + "isDirectExecutionAllowed": false, + "operation": "apply-powerup", + "slug": "test-action", + }, +] +`; + +exports[`app-config > convertActionConfigsToMothertreeActionConfigs > should convert a basic exchange action 1`] = ` +[ + { + "inputsTemplate": { + "assets": [ + { + "path": "./_Asset/test-asset", + "quantity": 1, + }, + ], + }, + "isDirectExecutionAllowed": false, + "operation": "exchange", + "slug": "test-action", + }, +] +`; + exports[`app-config > convertActionConfigsToMothertreeActionConfigs > should convert a basic webhook action 1`] = ` [ { "inputsTemplate": { + "data": undefined, + "url": "https://example.com", + }, + "isDirectExecutionAllowed": false, + "operation": "webhook", + "slug": "test-action", + }, +] +`; + +exports[`app-config > convertActionConfigsToMothertreeActionConfigs > should convert a basic webhook action with data 1`] = ` +[ + { + "inputsTemplate": { + "data": { + "some": "data", + }, "url": "https://example.com", }, - "operation": "test-operation", + "isDirectExecutionAllowed": false, + "operation": "webhook", "slug": "test-action", }, ] @@ -16,9 +92,11 @@ exports[`app-config > convertActionConfigsToMothertreeActionConfigs > should con [ { "inputsTemplate": { + "data": undefined, "url": "https://example.com", }, - "operation": "test-operation", + "isDirectExecutionAllowed": false, + "operation": "webhook", "slug": "test-action-step-1", }, { @@ -29,6 +107,7 @@ exports[`app-config > convertActionConfigsToMothertreeActionConfigs > should con ], "strategy": "sequential", }, + "isDirectExecutionAllowed": false, "operation": "workflow", "slug": "test-action", }, @@ -40,8 +119,10 @@ exports[`app-config > convertAppConfigToMothertreeConfig > should convert a basi "actions": [ { "inputsTemplate": { + "data": undefined, "url": "https://example.com", }, + "isDirectExecutionAllowed": false, "operation": "webhook", "slug": "test-action", }, @@ -71,13 +152,14 @@ exports[`app-config > convertAppConfigToMothertreeConfig > should convert a basi ], "name": "test-app", "path": "@truffle/test-app", + "powerups": [], "productVariants": [ { "actionPath": "./_Action/default-exchange-action", "assetTemplates": [ { "count": 100, - "entityPath": "@truffle/tips/_Countable/sparks", + "entityPath": "@truffle/sparks/_Countable/sparks:@truffle", "metadata": {}, "receivers": [ { @@ -112,6 +194,31 @@ exports[`app-config > convertAppConfigToMothertreeConfig > should convert a basi } `; +exports[`app-config > convertAppConfigToMothertreeConfig > should convert a postInstallAction that is defined as an action object 1`] = ` +{ + "actions": [ + { + "inputsTemplate": { + "data": undefined, + "url": "https://example.com", + }, + "isDirectExecutionAllowed": false, + "operation": "webhook", + "slug": "post-install-action", + }, + ], + "cliVersion": "0.0.0", + "countables": [], + "embeds": [], + "name": "test-app", + "path": "@truffle/test-app", + "postInstallActionPath": "./_Action/post-install-action", + "powerups": [], + "productVariants": [], + "products": [], +} +`; + exports[`app-config > convertProductConfigsToMothertreeProductAndVariantConfigs > should convert a basic product 1`] = ` { "actions": [ @@ -129,7 +236,7 @@ exports[`app-config > convertProductConfigsToMothertreeProductAndVariantConfigs "assetTemplates": [ { "count": 100, - "entityPath": "@truffle/tips/_Countable/sparks", + "entityPath": "@truffle/sparks/_Countable/sparks:@truffle", "metadata": {}, "receivers": [ { @@ -176,8 +283,10 @@ exports[`app-config > convertProductConfigsToMothertreeProductAndVariantConfigs }, { "inputsTemplate": { + "data": undefined, "url": "https://example.com", }, + "isDirectExecutionAllowed": false, "operation": "webhook", "slug": "test-product-test-variant-workflow-step-1", }, @@ -189,6 +298,7 @@ exports[`app-config > convertProductConfigsToMothertreeProductAndVariantConfigs ], "strategy": "sequential", }, + "isDirectExecutionAllowed": false, "operation": "workflow", "slug": "test-product-test-variant-workflow", }, @@ -199,7 +309,7 @@ exports[`app-config > convertProductConfigsToMothertreeProductAndVariantConfigs "assetTemplates": [ { "count": 100, - "entityPath": "@truffle/tips/_Countable/sparks", + "entityPath": "@truffle/sparks/_Countable/sparks:@truffle", "metadata": {}, "receivers": [ { diff --git a/tests/unit/app-config-schema.test.ts b/tests/unit/app-config-schema.test.ts index 252d058..2b41a10 100644 --- a/tests/unit/app-config-schema.test.ts +++ b/tests/unit/app-config-schema.test.ts @@ -132,6 +132,41 @@ describe('app-config-schema', () => { }); describe('ACTION_SCHEMA', () => { + it('should validate a webhook action with {{USE_PROVIDED}} data', () => { + const action = { + operation: 'webhook', + url: 'https://example.com/webhook', + data: '{{USE_PROVIDED}}', + }; + + const { error } = schemas.ACTION_SCHEMA.validate(action); + expect(error).toBeUndefined(); + }); + + it('should validate a webhook action with object data', () => { + const action = { + operation: 'webhook', + url: 'https://example.com/webhook', + data: { + some: 'data', + }, + }; + + const { error } = schemas.ACTION_SCHEMA.validate(action); + expect(error).toBeUndefined(); + }); + + it('should throw an error if data is an invalid string', () => { + const action = { + operation: 'webhook', + url: 'https://example.com/webhook', + data: 'invalid', + }; + + const { error } = schemas.ACTION_SCHEMA.validate(action); + expect(error).toMatchSnapshot(); + }); + it('should validate sub-actions in a workflow', () => { const action = { operation: 'workflow', @@ -155,6 +190,7 @@ describe('app-config-schema', () => { it('should throw an error if a sub-action is invalid', () => { const action = { operation: 'workflow', + strategy: 'sequential', actions: [ { operation: 'webhook', @@ -169,5 +205,87 @@ describe('app-config-schema', () => { const { error } = schemas.ACTION_SCHEMA.validate(action); expect(error).toMatchSnapshot(); }); + + it('validate an apply-powerup action with a path', () => { + const action = { + operation: 'apply-powerup', + powerup: '@truffle/test-app/_Powerup/test-powerup', + targetType: 'chat', + targetId: 'chatId', + ttlSeconds: 60, + }; + + Joi.assert(action, schemas.ACTION_SCHEMA); + }); + + it('should validate an apply powerup action with a powerup object', () => { + const action = { + operation: 'apply-powerup', + powerup: { + slug: 'test-powerup', + name: 'Test Powerup', + }, + targetType: 'chat', + targetId: 'chatId', + ttlSeconds: 60, + }; + + Joi.assert(action, schemas.ACTION_SCHEMA); + }); + + it('should throw an error if an apply-powerup action is invalid', () => { + const action = { + operation: 'apply-powerup', + targetType: 'chat', + targetId: 'chatId', + ttlSeconds: 60, + }; + + expect(() => + Joi.assert(action, schemas.ACTION_SCHEMA), + ).toThrowErrorMatchingSnapshot(); + }); + }); + + describe('APP_CONFIG_SCHEMA', () => { + it('should allow a path to be defined for postInstallAction', () => { + const config = { + path: '@truffle/test-app', + name: 'test-app', + cliVersion: '1.0.0', + postInstallAction: '@truffle/app/_Action/test-action', + }; + + Joi.assert(config, schemas.APP_CONFIG_SCHEMA); + }); + + it('should allow an action to be defined for postInstallAction', () => { + const config = { + path: '@truffle/test-app', + name: 'test-app', + cliVersion: '1.0.0', + postInstallAction: { + operation: 'webhook', + url: 'https://example.com/webhook', + }, + }; + + Joi.assert(config, schemas.APP_CONFIG_SCHEMA); + }); + + it('should throw an error if postInstallAction is invalid', () => { + const config = { + path: '@truffle/test-app', + name: 'test-app', + cliVersion: '1.0.0', + postInstallAction: { + operation: 'webhook', + }, + }; + + expect(() => + Joi.assert(config, schemas.APP_CONFIG_SCHEMA), + ).toThrowErrorMatchingSnapshot(); + }); }); }); diff --git a/tests/unit/app-config.test.ts b/tests/unit/app-config.test.ts index 79866b4..318e976 100644 --- a/tests/unit/app-config.test.ts +++ b/tests/unit/app-config.test.ts @@ -62,7 +62,7 @@ describe('app-config', () => { const actionConfigs = [ { slug: 'test-action', - operation: 'test-operation', + operation: 'webhook', url: 'https://example.com', }, ]; @@ -75,6 +75,26 @@ describe('app-config', () => { expect(mtAppConfig.actions).toMatchSnapshot(); }); + it('should convert a basic webhook action with data', () => { + const actionConfigs = [ + { + slug: 'test-action', + operation: 'webhook', + url: 'https://example.com', + data: { + some: 'data', + }, + }, + ]; + const mtAppConfig = { + actions: [], + }; + + convertActionConfigsToMothertreeActionConfigs(actionConfigs, mtAppConfig); + + expect(mtAppConfig.actions).toMatchSnapshot(); + }); + it('should convert a basic workflow action', () => { const actionConfigs = [ { @@ -85,7 +105,7 @@ describe('app-config', () => { './_Action/action-1', { slug: 'action-2', - operation: 'test-operation', + operation: 'webhook', url: 'https://example.com', }, ], @@ -99,6 +119,72 @@ describe('app-config', () => { expect(mtAppConfig.actions).toMatchSnapshot(); }); + + it('should convert a basic exchange action', () => { + const actionConfigs = [ + { + slug: 'test-action', + operation: 'exchange', + assets: [ + { + path: './_Asset/test-asset', + quantity: 1, + }, + ], + }, + ]; + const mtAppConfig = { + actions: [], + }; + + convertActionConfigsToMothertreeActionConfigs(actionConfigs, mtAppConfig); + + expect(mtAppConfig.actions).toMatchSnapshot(); + }); + + it('should convert a basic apply-powerup action with a powerup path', () => { + const actionConfigs = [ + { + slug: 'test-action', + operation: 'apply-powerup', + powerup: '@truffle/app/_Powerup/test-powerup', + targetType: 'test-target-type', + targetId: 'test-target-id', + ttlSeconds: 60, + }, + ]; + const mtAppConfig = { + actions: [], + }; + + convertActionConfigsToMothertreeActionConfigs(actionConfigs, mtAppConfig); + + expect(mtAppConfig.actions).toMatchSnapshot(); + }); + + it('should convert a basic apply-powerup action with a powerup object', () => { + const actionConfigs = [ + { + slug: 'test-action', + operation: 'apply-powerup', + powerup: { + slug: 'test-powerup', + name: 'Test Powerup', + }, + targetType: 'test-target-type', + targetId: 'test-target-id', + ttlSeconds: 60, + }, + ]; + const mtAppConfig = { + powerups: [], + actions: [], + }; + + convertActionConfigsToMothertreeActionConfigs(actionConfigs, mtAppConfig); + + expect(mtAppConfig).toMatchSnapshot(); + }); }); describe('convertProductConfigsToMothertreeProductAndVariantConfigs', () => { @@ -209,5 +295,32 @@ describe('app-config', () => { expect(convertAppConfigToMothertreeConfig(appConfig)).toMatchSnapshot(); }); + + it('should define a postInstallActionPath for a postInstallAction that is defined as a path', () => { + const appConfig = { + path: '@truffle/test-app', + name: 'test-app', + cliVersion: '0.0.0', + postInstallAction: './_Action/post-install', + }; + + expect( + convertAppConfigToMothertreeConfig(appConfig).postInstallActionPath, + ).toBe(appConfig.postInstallAction); + }); + + it('should convert a postInstallAction that is defined as an action object', () => { + const appConfig = { + path: '@truffle/test-app', + name: 'test-app', + cliVersion: '0.0.0', + postInstallAction: { + operation: 'webhook', + url: 'https://example.com', + }, + }; + + expect(convertAppConfigToMothertreeConfig(appConfig)).toMatchSnapshot(); + }); }); });