From b87318d66413792afbe3034e65ceaf4c064af493 Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Wed, 19 Nov 2025 12:15:16 -0800 Subject: [PATCH 1/3] fix(aci): remove email targetDisplay from POST request --- .../components/automationFormData.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/static/app/views/automations/components/automationFormData.tsx b/static/app/views/automations/components/automationFormData.tsx index 602cd4cc5ca628..66fdc509d6bae1 100644 --- a/static/app/views/automations/components/automationFormData.tsx +++ b/static/app/views/automations/components/automationFormData.tsx @@ -1,6 +1,6 @@ import type {FieldValue} from 'sentry/components/forms/model'; import {t} from 'sentry/locale'; -import type {Action} from 'sentry/types/workflowEngine/actions'; +import {ActionType, type Action} from 'sentry/types/workflowEngine/actions'; import type {Automation, NewAutomation} from 'sentry/types/workflowEngine/automations'; import type {DataCondition} from 'sentry/types/workflowEngine/dataConditions'; import {actionNodesMap} from 'sentry/views/automations/components/actionNodes'; @@ -35,8 +35,18 @@ const stripSubfilterId = (subfilter: any) => { return subfilterWithoutId; }; -const stripActionId = (action: any) => { +const stripActionFields = (action: any) => { const {id: _id, ...actionWithoutId} = action; + + // Strip targetDisplay from email action config + if (action.type === ActionType.EMAIL && action.config) { + const {targetDisplay: _targetDisplay, ...configWithoutTargetDisplay} = action.config; + return { + ...actionWithoutId, + config: configWithoutTargetDisplay, + }; + } + return actionWithoutId; }; @@ -45,7 +55,7 @@ const stripDataConditionGroupId = (group: any) => { return { ...groupWithoutId, conditions: group.conditions?.map(stripDataConditionId) || [], - actions: group.actions?.map(stripActionId) || [], + actions: group.actions?.map(stripActionFields) || [], }; }; From be26acb48572f2acc62af136768da61b82a303af Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Wed, 19 Nov 2025 15:42:37 -0800 Subject: [PATCH 2/3] attempted test --- static/app/components/selectMembers/index.tsx | 4 +- .../automations/components/actions/email.tsx | 2 +- .../components/automationFormData.tsx | 2 +- static/app/views/automations/new.spec.tsx | 48 ++++++++++++++++++- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/static/app/components/selectMembers/index.tsx b/static/app/components/selectMembers/index.tsx index 6b85552f4c20d1..277c45df84a7ad 100644 --- a/static/app/components/selectMembers/index.tsx +++ b/static/app/components/selectMembers/index.tsx @@ -32,6 +32,7 @@ type Props = { onChange: (value: any) => any; organization: Organization; value: any; + ariaLabel?: string; disabled?: boolean; onInputChange?: (value: any) => any; placeholder?: string; @@ -174,7 +175,7 @@ class SelectMembers extends Component { }; render() { - const {placeholder, styles} = this.props; + const {placeholder, styles, ariaLabel} = this.props; // If memberList is still loading we need to disable a placeholder Select, // otherwise `react-select` will call `loadOptions` and prematurely load @@ -185,6 +186,7 @@ class SelectMembers extends Component { return ( , filterText: string) => option?.data?.searchKey?.indexOf(filterText) > -1 } diff --git a/static/app/views/automations/components/actions/email.tsx b/static/app/views/automations/components/actions/email.tsx index 822c465e6df18f..6aa793df930adc 100644 --- a/static/app/views/automations/components/actions/email.tsx +++ b/static/app/views/automations/components/actions/email.tsx @@ -130,7 +130,7 @@ function IdentifierField() { return ( { return subfilterWithoutId; }; -const stripActionFields = (action: any) => { +const stripActionFields = (action: Action) => { const {id: _id, ...actionWithoutId} = action; // Strip targetDisplay from email action config diff --git a/static/app/views/automations/new.spec.tsx b/static/app/views/automations/new.spec.tsx index d995be094db98c..eb2161cf39c9ca 100644 --- a/static/app/views/automations/new.spec.tsx +++ b/static/app/views/automations/new.spec.tsx @@ -32,6 +32,11 @@ describe('AutomationNewSettings', () => { handlerGroup: ActionGroup.NOTIFICATION, integrations: [{id: '1', name: 'My Slack Workspace'}], }), + ActionHandlerFixture({ + type: ActionType.EMAIL, + handlerGroup: ActionGroup.NOTIFICATION, + integrations: [], + }), ], }); @@ -67,7 +72,28 @@ describe('AutomationNewSettings', () => { MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/users/`, method: 'GET', - body: [], + body: [ + { + id: '1', + name: 'Moo Deng', + email: 'moo.deng@sentry.io', + }, + ], + }); + + // Users endpoint fetched by AutomationBuilder for member selectors + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/members/`, + method: 'GET', + body: [ + { + user: { + id: '1', + name: 'Moo Deng', + email: 'moo.deng@sentry.io', + }, + }, + ], }); // Detectors list used by EditConnectedMonitors inside the form @@ -107,6 +133,17 @@ describe('AutomationNewSettings', () => { await selectEvent.select(screen.getByRole('textbox', {name: 'Add action'}), 'Slack'); await userEvent.type(screen.getByRole('textbox', {name: 'Target'}), '#alerts'); + // Add an email action + await selectEvent.select( + screen.getByRole('textbox', {name: 'Add action'}), + 'Notify on preferred channel' + ); + await selectEvent.select( + screen.getByRole('textbox', {name: 'Notification target type'}), + 'Member' + ); + await selectEvent.select(screen.getByRole('textbox', {name: 'User'}), 'Moo Deng'); + // Submit the form await userEvent.click(screen.getByRole('button', {name: 'Create Alert'})); @@ -148,6 +185,15 @@ describe('AutomationNewSettings', () => { data: {}, status: 'active', }, + { + type: 'email', + config: { + targetType: 'user', + targetIdentifier: '1', + }, + data: {}, + status: 'active', + }, ], }, ], From d3a8b9c0a65f378efce1c98ad4a0fd04ed5d4e4c Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Thu, 20 Nov 2025 10:07:12 -0800 Subject: [PATCH 3/3] fix test --- static/app/views/automations/new.spec.tsx | 29 +++++++---------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/static/app/views/automations/new.spec.tsx b/static/app/views/automations/new.spec.tsx index eb2161cf39c9ca..f54d37a183ed56 100644 --- a/static/app/views/automations/new.spec.tsx +++ b/static/app/views/automations/new.spec.tsx @@ -1,5 +1,7 @@ import {AutomationFixture} from 'sentry-fixture/automations'; +import {MemberFixture} from 'sentry-fixture/member'; import {OrganizationFixture} from 'sentry-fixture/organization'; +import {UserFixture} from 'sentry-fixture/user'; import { ActionHandlerFixture, DataConditionHandlerFixture, @@ -18,6 +20,9 @@ import AutomationNewSettings from 'sentry/views/automations/new'; describe('AutomationNewSettings', () => { const organization = OrganizationFixture({features: ['workflow-engine-ui']}); + const mockMember = MemberFixture({ + user: UserFixture({id: '1', name: 'Moo Deng', email: 'moo.deng@sentry.io'}), + }); beforeEach(() => { MockApiClient.clearMockResponses(); @@ -68,32 +73,16 @@ describe('AutomationNewSettings', () => { ], }); - // Users endpoint fetched by AutomationBuilder for member selectors + // Users/members endpoints fetched by AutomationBuilder for member selectors MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/users/`, method: 'GET', - body: [ - { - id: '1', - name: 'Moo Deng', - email: 'moo.deng@sentry.io', - }, - ], + body: [mockMember], }); - - // Users endpoint fetched by AutomationBuilder for member selectors MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/members/`, method: 'GET', - body: [ - { - user: { - id: '1', - name: 'Moo Deng', - email: 'moo.deng@sentry.io', - }, - }, - ], + body: [mockMember], }); // Detectors list used by EditConnectedMonitors inside the form @@ -152,7 +141,7 @@ describe('AutomationNewSettings', () => { expect.anything(), expect.objectContaining({ data: { - name: 'Notify #alerts via Slack', + name: 'Notify #alerts via Slack, Notify Moo Deng', triggers: { logicType: 'any-short', conditions: [