From 8e95647498d1876c70183fee80b114224511d40d Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Mon, 13 May 2024 13:44:11 -0500 Subject: [PATCH 01/12] remove dest conditionals from policy page --- .../escalation-policies/PolicyStepsCard.tsx | 38 +++++-------------- .../escalation-policies/PolicyStepsQuery.tsx | 24 +----------- 2 files changed, 10 insertions(+), 52 deletions(-) diff --git a/web/src/app/escalation-policies/PolicyStepsCard.tsx b/web/src/app/escalation-policies/PolicyStepsCard.tsx index d3a27d806b..96f78654e8 100644 --- a/web/src/app/escalation-policies/PolicyStepsCard.tsx +++ b/web/src/app/escalation-policies/PolicyStepsCard.tsx @@ -8,19 +8,16 @@ import { Add } from '@mui/icons-material' import { gql, useMutation } from 'urql' import FlatList from '../lists/FlatList' import CreateFAB from '../lists/CreateFAB' -import PolicyStepCreateDialog from './PolicyStepCreateDialog' import PolicyStepCreateDialogDest from './PolicyStepCreateDialogDest' import { useResetURLParams, useURLParam } from '../actions' import DialogTitleWrapper from '../dialogs/components/DialogTitleWrapper' import DialogContentError from '../dialogs/components/DialogContentError' import { useIsWidthDown } from '../util/useWidth' import { reorderList } from '../rotations/util' -import PolicyStepEditDialog from './PolicyStepEditDialog' import PolicyStepDeleteDialog from './PolicyStepDeleteDialog' import PolicyStepEditDialogDest from './PolicyStepEditDialogDest' import OtherActions from '../util/OtherActions' import { renderChips, renderChipsDest, renderDelayMessage } from './stepUtil' -import { useExpFlag } from '../util/useExpFlag' import { Destination, Target } from '../../schema' const mutation = gql` @@ -48,8 +45,6 @@ export type PolicyStepsCardProps = { export default function PolicyStepsCard( props: PolicyStepsCardProps, ): React.ReactNode { - const hasDestTypesFlag = useExpFlag('dest-types') - const isMobile = useIsWidthDown('md') const stepNumParam = 'createStep' const [createStep, setCreateStep] = useURLParam(stepNumParam, false) @@ -122,17 +117,10 @@ export default function PolicyStepsCard( )} {createStep && ( - {hasDestTypesFlag ? ( - - ) : ( - - )} + )} @@ -206,19 +194,11 @@ export default function PolicyStepsCard( {editStep && ( - {hasDestTypesFlag ? ( - - ) : ( - - )} + )} {deleteStep && ( diff --git a/web/src/app/escalation-policies/PolicyStepsQuery.tsx b/web/src/app/escalation-policies/PolicyStepsQuery.tsx index a6f47ab978..01d57b9f22 100644 --- a/web/src/app/escalation-policies/PolicyStepsQuery.tsx +++ b/web/src/app/escalation-policies/PolicyStepsQuery.tsx @@ -2,26 +2,6 @@ import React from 'react' import { gql, useQuery } from 'urql' import PolicyStepsCard from './PolicyStepsCard' import { GenericError, ObjectNotFound } from '../error-pages' -import { useExpFlag } from '../util/useExpFlag' - -export const policyStepsQuery = gql` - query stepsQuery($id: ID!) { - escalationPolicy(id: $id) { - id - repeat - steps { - id - delayMinutes - stepNumber - targets { - id - name - type - } - } - } - } -` export const policyStepsQueryDest = gql` query stepsQueryDest($id: ID!) { @@ -57,10 +37,8 @@ export const policyStepsQueryDest = gql` ` function PolicyStepsQuery(props: { escalationPolicyID: string }): JSX.Element { - const hasDestTypesFlag = useExpFlag('dest-types') - const [{ data, error }] = useQuery({ - query: hasDestTypesFlag ? policyStepsQueryDest : policyStepsQuery, + query: policyStepsQueryDest, variables: { id: props.escalationPolicyID }, }) From 5faf88218ba946c7599263da17cbfce01e222ada Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Mon, 13 May 2024 14:02:40 -0500 Subject: [PATCH 02/12] add missing name attr --- web/src/app/escalation-policies/PolicyStepFormDest.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/app/escalation-policies/PolicyStepFormDest.tsx b/web/src/app/escalation-policies/PolicyStepFormDest.tsx index 56a43c5fd6..eb5d1266fc 100644 --- a/web/src/app/escalation-policies/PolicyStepFormDest.tsx +++ b/web/src/app/escalation-policies/PolicyStepFormDest.tsx @@ -119,6 +119,7 @@ export default function PolicyStepFormDest( disabled={props.disabled} value={destType} label='Destination Type' + name='dest.type' onChange={(e) => setDestType(e.target.value)} > {types.map((t) => From 68d29e5c9452b9d41b2aed2edb94eed2191dcd0b Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Mon, 13 May 2024 14:02:46 -0500 Subject: [PATCH 03/12] fix step test --- .../cypress/e2e/escalationPolicySteps.cy.ts | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/web/src/cypress/e2e/escalationPolicySteps.cy.ts b/web/src/cypress/e2e/escalationPolicySteps.cy.ts index 03b1207018..7c09ea2421 100644 --- a/web/src/cypress/e2e/escalationPolicySteps.cy.ts +++ b/web/src/cypress/e2e/escalationPolicySteps.cy.ts @@ -44,13 +44,20 @@ function testSteps(screen: ScreenFormat): void { cy.get('button').contains('Create Step').click() } cy.dialogTitle('Create Step') - cy.dialogForm({ schedules: [s1.name, s2.name] }) + cy.dialogForm({ 'dest.type': 'Schedule', 'schedule-id': s1.name }) + cy.dialogClick('Add Action') + cy.dialogForm({ 'schedule-id': s2.name }) + cy.dialogClick('Add Action') - cy.get('button[data-cy="users-step"]').click() - cy.dialogForm({ users: [u1.name, u2.name] }) + cy.dialogForm({ 'dest.type': 'User', 'user-id': u1.name }) + cy.dialogClick('Add Action') + cy.dialogForm({ 'user-id': u2.name }) + cy.dialogClick('Add Action') - cy.get('button[data-cy="rotations-step"]').click() - cy.dialogForm({ rotations: [r1.name, r2.name] }) + cy.dialogForm({ 'dest.type': 'Rotation', 'rotation-id': r1.name }) + cy.dialogClick('Add Action') + cy.dialogForm({ 'rotation-id': r2.name }) + cy.dialogClick('Add Action') cy.dialogForm({ delayMinutes: delay.toString() }) cy.dialogFinish('Submit') @@ -58,12 +65,12 @@ function testSteps(screen: ScreenFormat): void { // verify data integrity cy.get('body').should('contain', 'Notify the following:') cy.get('body').should('contain', 'Step #1:') - cy.get('div[data-cy=rotation-chip]').should('contain', r1.name) - cy.get('div[data-cy=rotation-chip]').should('contain', r2.name) - cy.get('div[data-cy=schedule-chip]').should('contain', s1.name) - cy.get('div[data-cy=schedule-chip]').should('contain', s2.name) - cy.get('div[data-cy=user-chip]').should('contain', u1.name) - cy.get('div[data-cy=user-chip]').should('contain', u2.name) + cy.get('[data-testid=destination-chip]').should('contain', r1.name) + cy.get('[data-testid=destination-chip]').should('contain', r2.name) + cy.get('[data-testid=destination-chip]').should('contain', s1.name) + cy.get('[data-testid=destination-chip]').should('contain', s2.name) + cy.get('[data-testid=destination-chip]').should('contain', u1.name) + cy.get('[data-testid=destination-chip]').should('contain', u2.name) cy.get('body').should( 'contain', `Go back to step #1 after ${delay.toString()} minutes`, From ffb7ca25d4bff5d0db8195a547054dcc45b0330a Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Mon, 13 May 2024 14:23:08 -0500 Subject: [PATCH 04/12] update remaining step tests --- .../cypress/e2e/escalationPolicySteps.cy.ts | 79 +++++++++++-------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/web/src/cypress/e2e/escalationPolicySteps.cy.ts b/web/src/cypress/e2e/escalationPolicySteps.cy.ts index 7c09ea2421..3748875711 100644 --- a/web/src/cypress/e2e/escalationPolicySteps.cy.ts +++ b/web/src/cypress/e2e/escalationPolicySteps.cy.ts @@ -89,8 +89,11 @@ function testSteps(screen: ScreenFormat): void { cy.get('button').contains('Create Step').click() } cy.dialogTitle('Create Step') - cy.get('button[data-cy="users-step"]').click() - cy.dialogForm({ users: [u1.name, u2.name] }) + + cy.dialogForm({ 'dest.type': 'User', 'user-id': u1.name }) + cy.dialogClick('Add Action') + cy.dialogForm({ 'user-id': u2.name }) + cy.dialogClick('Add Action') }) it('should edit a step', () => { @@ -108,16 +111,18 @@ function testSteps(screen: ScreenFormat): void { cy.dialogTitle('Edit Step') cy.dialogForm({ - schedules: s1.name, + 'dest.type': 'Schedule', + 'schedule-id': s1.name, delayMinutes: delay.toString(), }) + cy.dialogClick('Add Action') cy.dialogFinish('Submit') // verify data integrity cy.get('body').should('contain', 'Notify the following:') cy.get('body').should('contain', 'Step #1:') - cy.get('div[data-cy=schedule-chip]').should('contain', s1.name) + cy.get('[data-testid=destination-chip]').should('contain', s1.name) cy.get('body').should( 'contain', `Go back to step #1 after ${delay.toString()} minutes`, @@ -136,25 +141,32 @@ function testSteps(screen: ScreenFormat): void { } cy.dialogTitle('Create Step') - // expand slack channels section - cy.get('button[data-cy="slack-channels-step"]').click() - // add slack channels - cy.dialogForm({ slackChannels: ['general', 'foobar'] }) + cy.dialogForm({ + 'dest.type': 'Slack Channel', + 'slack-channel-id': 'general', + }) + cy.dialogClick('Add Action') + cy.dialogForm({ 'slack-channel-id': 'foobar' }) + cy.dialogClick('Add Action') + cy.dialogFinish('Submit') // verify data integrity cy.get('body').should('contain', 'Notify the following:') cy.get('body').should('contain', 'Step #1:') - cy.get('div[data-cy=slack-chip]').should('contain', '#general') - cy.get('div[data-cy=slack-chip]').should('contain', '#foobar') + cy.get('[data-testid=destination-chip]').should('contain', '#general') + cy.get('[data-testid=destination-chip]').should('contain', '#foobar') // verify clickability cy.window().then((win) => { cy.stub(win, 'open').as('slackRedirect') }) - cy.get('div[data-cy=slack-chip][data-clickable=true]').first().click() - cy.get('@slackRedirect').should('be.called') + cy.get('a[data-testid=destination-chip]').should( + 'have.attr', + 'target', + '_blank', + ) // open edit step dialog cy.get('ul[data-cy=steps-list] :nth-child(2) li') @@ -163,19 +175,19 @@ function testSteps(screen: ScreenFormat): void { cy.dialogTitle('Edit Step') - // expand slack channels section - cy.get('button[data-cy="slack-channels-step"]').click() - // delete foobar channel - cy.get('input[name=slackChannels]').multiRemoveByLabel('#foobar') + cy.get('[role=dialog] a[data-testid=destination-chip]') + .contains('a', '#foobar') + .find('[data-testid=CancelIcon]') + .click() cy.dialogFinish('Submit') // verify data integrity cy.get('body').should('contain', 'Notify the following:') cy.get('body').should('contain', 'Step #1:') - cy.get('div[data-cy=slack-chip]').should('contain', '#general') - cy.get('div[data-cy=slack-chip]').should('not.contain', '#foobar') + cy.get('[data-testid=destination-chip]').should('contain', '#general') + cy.get('[data-testid=destination-chip]').should('not.contain', '#foobar') }) it('should delete a step', () => { @@ -275,25 +287,23 @@ testScreen('Webhook Support', (screen: ScreenFormat) => { } cy.dialogTitle('Create Step') - // expand webhook section - cy.get('button[data-cy="webhook-step"]').click() - // add webhooks cy.dialogForm({ - webhooks: 'https://webhook.site', + 'dest.type': 'Webhook', + 'webhook-url': 'https://webhook.site', }) - cy.get('button[data-cy="add-webhook"]').click() + cy.dialogClick('Add Action') cy.dialogForm({ - webhooks: 'https://example.com', + 'webhook-url': 'https://example.com', }) - cy.get('button[data-cy="add-webhook"]').click() + cy.dialogClick('Add Action') cy.dialogFinish('Submit') // verify data integrity cy.get('body').should('contain', 'Notify the following:') cy.get('body').should('contain', 'Step #1:') - cy.get('div[data-cy=webhook-chip]').should('contain', 'webhook.site') - cy.get('div[data-cy=webhook-chip]').should('contain', 'example.com') + cy.get('[data-testid=destination-chip]').should('contain', 'webhook.site') + cy.get('[data-testid=destination-chip]').should('contain', 'example.com') // open edit step dialog cy.get('ul[data-cy=steps-list] :nth-child(2) li') @@ -302,18 +312,21 @@ testScreen('Webhook Support', (screen: ScreenFormat) => { cy.dialogTitle('Edit Step') - // expand webhook section - cy.get('button[data-cy="webhook-step"]').click() - // delete webhook.site webhook - cy.get('[data-testid=CancelIcon]').first().click() + cy.get('[role=dialog] [data-testid=destination-chip]') + .contains('[data-testid=destination-chip]', 'webhook.site') + .find('[data-testid=CancelIcon]') + .click() cy.dialogFinish('Submit') // verify data integrity cy.get('body').should('contain', 'Notify the following:') cy.get('body').should('contain', 'Step #1:') - cy.get('div[data-cy=webhook-chip]').should('contain', 'example.com') - cy.get('div[data-cy=webhook-chip]').should('not.contain', 'webhook.site') + cy.get('[data-testid=destination-chip]').should('contain', 'example.com') + cy.get('[data-testid=destination-chip]').should( + 'not.contain', + 'webhook.site', + ) }) }) From 22aae31ed76546c94b4184b685a4586f4a27cad8 Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Mon, 13 May 2024 14:36:23 -0500 Subject: [PATCH 05/12] remove unused files/code --- .../PolicyStepCreateDialog.tsx | 82 ----- .../PolicyStepEditDialog.tsx | 69 ---- .../escalation-policies/PolicyStepForm.jsx | 310 ------------------ .../escalation-policies/PolicyStepsCard.tsx | 11 +- web/src/app/escalation-policies/stepUtil.tsx | 80 +---- web/src/app/selection/ChanWebhookSelect.tsx | 90 ----- web/src/app/selection/index.js | 1 - web/src/app/util/Chips.tsx | 123 +------ 8 files changed, 8 insertions(+), 758 deletions(-) delete mode 100644 web/src/app/escalation-policies/PolicyStepCreateDialog.tsx delete mode 100644 web/src/app/escalation-policies/PolicyStepEditDialog.tsx delete mode 100644 web/src/app/escalation-policies/PolicyStepForm.jsx delete mode 100644 web/src/app/selection/ChanWebhookSelect.tsx diff --git a/web/src/app/escalation-policies/PolicyStepCreateDialog.tsx b/web/src/app/escalation-policies/PolicyStepCreateDialog.tsx deleted file mode 100644 index 2d1732e281..0000000000 --- a/web/src/app/escalation-policies/PolicyStepCreateDialog.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useState } from 'react' -import { gql, useMutation } from 'urql' -import { fieldErrors, nonFieldErrors } from '../util/errutil' -import PolicyStepForm from './PolicyStepForm' -import FormDialog from '../dialogs/FormDialog' - -interface Value { - targets?: { - id: string - type: string - name?: null | string - } - delayMinutes: string -} - -const mutation = gql` - mutation ($input: CreateEscalationPolicyStepInput!) { - createEscalationPolicyStep(input: $input) { - id - delayMinutes - targets { - id - name - type - } - } - } -` - -function PolicyStepCreateDialog(props: { - escalationPolicyID: string - onClose: () => void -}): JSX.Element { - const [value, setValue] = useState(null) - const defaultValue = { - targets: [], - delayMinutes: '15', - } - - const [createStepStatus, createStep] = useMutation(mutation) - - const { fetching, error } = createStepStatus - const fieldErrs = fieldErrors(error) - - return ( - - createStep( - { - input: { - escalationPolicyID: props.escalationPolicyID, - delayMinutes: parseInt( - (value && value.delayMinutes) || defaultValue.delayMinutes, - ), - targets: (value && value.targets) || defaultValue.targets, - }, - }, - { additionalTypenames: ['EscalationPolicy'] }, - ).then((result) => { - if (!result.error) { - props.onClose() - } - }) - } - form={ - setValue(value)} - /> - } - /> - ) -} - -export default PolicyStepCreateDialog diff --git a/web/src/app/escalation-policies/PolicyStepEditDialog.tsx b/web/src/app/escalation-policies/PolicyStepEditDialog.tsx deleted file mode 100644 index ea54d33fb9..0000000000 --- a/web/src/app/escalation-policies/PolicyStepEditDialog.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useState } from 'react' -import { gql, useMutation } from 'urql' -import { fieldErrors, nonFieldErrors } from '../util/errutil' -import PolicyStepForm from './PolicyStepForm' -import FormDialog from '../dialogs/FormDialog' -import { UpdateEscalationPolicyStepInput } from '../../schema' - -interface PolicyStepEditDialogProps { - escalationPolicyID: string - onClose: () => void - step: UpdateEscalationPolicyStepInput -} - -const mutation = gql` - mutation ($input: UpdateEscalationPolicyStepInput!) { - updateEscalationPolicyStep(input: $input) - } -` - -function PolicyStepEditDialog(props: PolicyStepEditDialogProps): JSX.Element { - const [value, setValue] = useState( - null, - ) - - const defaultValue = { - targets: props.step?.targets?.map(({ id, type }) => ({ id, type })), - delayMinutes: props.step?.delayMinutes?.toString(), - } - - const [editStepMutationStatus, editStepMutation] = useMutation(mutation) - - const { fetching, error } = editStepMutationStatus - const fieldErrs = fieldErrors(error) - - return ( - - editStepMutation( - { - input: { - id: props.step.id, - delayMinutes: - (value && value.delayMinutes) || defaultValue.delayMinutes, - targets: (value && value.targets) || defaultValue.targets, - }, - }, - { additionalTypenames: ['EscalationPolicy'] }, - ).then((result) => { - if (!result.error) props.onClose() - }) - } - form={ - setValue(value)} - /> - } - /> - ) -} - -export default PolicyStepEditDialog diff --git a/web/src/app/escalation-policies/PolicyStepForm.jsx b/web/src/app/escalation-policies/PolicyStepForm.jsx deleted file mode 100644 index b6c2bee5b7..0000000000 --- a/web/src/app/escalation-policies/PolicyStepForm.jsx +++ /dev/null @@ -1,310 +0,0 @@ -import React, { useState } from 'react' -import { PropTypes as p } from 'prop-types' -import { FormContainer, FormField } from '../forms' -import Badge from '@mui/material/Badge' -import Grid from '@mui/material/Grid' -import Stepper from '@mui/material/Stepper' -import Step from '@mui/material/Step' -import StepButton from '@mui/material/StepButton' -import StepContent from '@mui/material/StepContent' -import Typography from '@mui/material/Typography' -import makeStyles from '@mui/styles/makeStyles' -import { - RotationSelect, - ScheduleSelect, - SlackChannelSelect, - UserSelect, - ChanWebhookSelect, -} from '../selection' - -import { - RotateRight as RotationsIcon, - Today as SchedulesIcon, - Group as UsersIcon, - Webhook as WebhookIcon, -} from '@mui/icons-material' -import { SlackBW as SlackIcon } from '../icons/components/Icons' -import { Config } from '../util/RequireConfig' -import NumberField from '../util/NumberField' -import AppLink from '../util/AppLink' - -const useStyles = makeStyles({ - badge: { - top: -1, - right: -1, - }, - optional: { - float: 'left', - textAlign: 'left', - }, - label: { - paddingRight: '0.4em', - }, -}) - -function PolicyStepForm(props) { - const [step, setStep] = useState(0) - const { disabled, value } = props - const classes = useStyles() - - function handleStepChange(stepChange) { - if (stepChange === step) { - setStep(-1) // close - } else { - setStep(stepChange) // open - } - } - - // takes a list of { id, type } targets and return the ids for a specific type - const getTargetsByType = (type) => (tgts) => - tgts - .filter((t) => t.type === type) // only the list of the current type - .map((t) => t.id) // array of ID strings - - // takes a list of ids and return a list of { id, type } concatted with the new set of specific types - const makeSetTargetType = (curTgts) => (type) => (newTgts) => - curTgts - .filter((t) => t.type !== type) // current targets without any of the current type - .concat(newTgts.map((id) => ({ id, type }))) // add the list of current type to the end - - // then form fields would all point to `targets` but can map values - const setTargetType = makeSetTargetType(value.targets) - - const badgeMeUpScotty = (len, txt) => ( - - {txt} - - ) - - const optionalText = ( - - Optional - - ) - - return ( - - - - - {(cfg) => ( - - - } - optional={optionalText} - onClick={() => handleStepChange(0)} - tabIndex={-1} - > - {badgeMeUpScotty( - getTargetsByType('schedule')(value.targets).length, - 'Add Schedules', - )} - - - - - - {cfg['Slack.Enable'] && ( - - } - optional={optionalText} - onClick={() => handleStepChange(1)} - tabIndex={-1} - > - {badgeMeUpScotty( - getTargetsByType('slackChannel')(value.targets).length, - 'Add Slack Channels', - )} - - - - - - )} - - } - optional={optionalText} - onClick={() => - handleStepChange(cfg['Slack.Enable'] ? 2 : 1) - } - tabIndex={-1} - > - {badgeMeUpScotty( - getTargetsByType('user')(value.targets).length, - 'Add Users', - )} - - - - - - - } - optional={optionalText} - onClick={() => - handleStepChange(cfg['Slack.Enable'] ? 3 : 2) - } - tabIndex={-1} - > - {badgeMeUpScotty( - getTargetsByType('rotation')(value.targets).length, - 'Add Rotations', - )} - - - - - - {cfg['Webhook.Enable'] && ( - - } - optional={optionalText} - onClick={() => - handleStepChange(cfg['Slack.Enable'] ? 4 : 3) - } - tabIndex={-1} - > - {badgeMeUpScotty( - getTargetsByType('chanWebhook')(value.targets).length, - 'Add Webhook', - )} - - - - Webhook Documentation - - } - /> - - - )} - - )} - - - - - - - - ) -} - -PolicyStepForm.propTypes = { - value: p.shape({ - targets: p.arrayOf( - p.shape({ id: p.string.isRequired, type: p.string.isRequired }), - ), - delayMinutes: p.string.isRequired, - }).isRequired, - - errors: p.arrayOf( - p.shape({ - field: p.oneOf(['targets', 'delayMinutes']).isRequired, - message: p.string.isRequired, - }), - ), - - disabled: p.bool, - onChange: p.func, -} - -export default PolicyStepForm diff --git a/web/src/app/escalation-policies/PolicyStepsCard.tsx b/web/src/app/escalation-policies/PolicyStepsCard.tsx index 96f78654e8..0555555f1e 100644 --- a/web/src/app/escalation-policies/PolicyStepsCard.tsx +++ b/web/src/app/escalation-policies/PolicyStepsCard.tsx @@ -17,8 +17,8 @@ import { reorderList } from '../rotations/util' import PolicyStepDeleteDialog from './PolicyStepDeleteDialog' import PolicyStepEditDialogDest from './PolicyStepEditDialogDest' import OtherActions from '../util/OtherActions' -import { renderChips, renderChipsDest, renderDelayMessage } from './stepUtil' -import { Destination, Target } from '../../schema' +import { renderChipsDest, renderDelayMessage } from './stepUtil' +import { Destination } from '../../schema' const mutation = gql` mutation UpdateEscalationPolicyMutation( @@ -32,8 +32,7 @@ type StepInfo = { id: string delayMinutes: number stepNumber: number - actions?: Destination[] - targets: Target[] + actions: Destination[] } export type PolicyStepsCardProps = { @@ -154,9 +153,7 @@ export default function PolicyStepsCard( ) as unknown as string, // needed to work around MUI incorrect types subText: ( - {step.actions - ? renderChipsDest(step.actions) - : renderChips(step)} + {renderChipsDest(step.actions)} {renderDelayMessage( step, idx, diff --git a/web/src/app/escalation-policies/stepUtil.tsx b/web/src/app/escalation-policies/stepUtil.tsx index 8cfa3dca04..d9a08d2455 100644 --- a/web/src/app/escalation-policies/stepUtil.tsx +++ b/web/src/app/escalation-policies/stepUtil.tsx @@ -1,33 +1,11 @@ -import React, { ComponentType, ReactElement, ReactNode } from 'react' +import React, { ReactElement, ReactNode } from 'react' import { sortBy } from 'lodash' import Chip from '@mui/material/Chip' import Grid from '@mui/material/Grid' import Typography from '@mui/material/Typography' -import { - RotationChip, - ScheduleChip, - UserChip, - SlackChip, - WebhookChip, -} from '../util/Chips' -import { Destination, Target } from '../../schema' +import { Destination } from '../../schema' import DestinationChip from '../util/DestinationChip' -interface Step { - id: string - delayMinutes: number - targets: Target[] -} - -interface HasID { - id: string -} - -export function getStepNumber(stepID: string, steps: HasID[]): number { - const sids = steps.map((s) => s.id) - return sids.indexOf(stepID) + 1 -} - export function renderChipsDest(_a: Destination[]): ReactElement { const actions = sortBy(_a.slice(), ['type', 'displayInfo.text']) if (!actions || actions.length === 0) { @@ -49,64 +27,12 @@ export function renderChipsDest(_a: Destination[]): ReactElement { ) } -/* - * Renders the mui chips for each target on the step - */ -export function renderChips({ targets: _t }: Step): ReactElement { - // copy and sort by type then name - const targets = sortBy(_t.slice(), ['type', 'name']) - - if (!targets || targets.length === 0) { - return - } - - const items = targets.map((tgt) => { - const tgtChip = ( - Chip: ComponentType<{ id: string; label: string }>, - ): JSX.Element => - - let chip = null - switch (tgt.type) { - case 'user': - chip = tgtChip(UserChip) - break - case 'schedule': - chip = tgtChip(ScheduleChip) - break - case 'rotation': - chip = tgtChip(RotationChip) - break - case 'slackChannel': - case 'notificationChannel': - chip = tgtChip(SlackChip) - break - case 'chanWebhook': - chip = tgtChip(WebhookChip) - break - } - - if (chip) { - return ( - - {chip} - - ) - } - }) - - return ( - - {items} - - ) -} - /* * Renders the delay message, dependent on if the escalation policy * repeats, and if the message is rendering on the last step */ export function renderDelayMessage( - step: Step, + step: { delayMinutes: number }, idx: number, repeat: number, isLastStep: boolean, diff --git a/web/src/app/selection/ChanWebhookSelect.tsx b/web/src/app/selection/ChanWebhookSelect.tsx deleted file mode 100644 index 1c706078a3..0000000000 --- a/web/src/app/selection/ChanWebhookSelect.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { Add } from '@mui/icons-material' -import { Chip, Grid, TextField } from '@mui/material' -import React, { useState } from 'react' -import { WebhookChip } from '../util/Chips' - -type ChanWebhookSelectProps = { - value: Array - onChange: (value: Array) => void -} - -function isValidURL(str: string): boolean { - try { - // eslint-disable-next-line no-new - new URL(str) - return true - } catch { - return false - } -} - -export const ChanWebhookSelect = ( - props: ChanWebhookSelectProps, -): JSX.Element => { - const [newURL, setNewURL] = useState('') - const { value, onChange } = props - - const selected = props.value.map((v) => { - return ( - - onChange(value.filter((f) => f !== v))} - /> - - ) - }) - - return ( - - - {props.value.length ? selected : ''} - - - { - setNewURL(e.target.value.trim()) - }} - error={ - (!isValidURL(newURL) && newURL.length > 0) || value.includes(newURL) - } - helperText={ - !isValidURL(newURL) && newURL.length > 0 - ? 'Must be a valid URL.' - : value.includes(newURL) - ? 'Must be a new URL.' - : '' - } - placeholder='https://example.com/...' - InputProps={{ - endAdornment: ( - } - onClick={() => { - if ( - !newURL || - !isValidURL(newURL) || - value.indexOf(newURL) > -1 - ) - return - - onChange([...value, newURL]) - setNewURL('') - }} - /> - ), - }} - /> - - - ) -} diff --git a/web/src/app/selection/index.js b/web/src/app/selection/index.js index fc33eb3753..a260bf159a 100644 --- a/web/src/app/selection/index.js +++ b/web/src/app/selection/index.js @@ -7,4 +7,3 @@ export * from './SlackChannelSelect' export * from './SlackUserGroupSelect' export * from './TimeZoneSelect' export * from './UserSelect' -export * from './ChanWebhookSelect' diff --git a/web/src/app/util/Chips.tsx b/web/src/app/util/Chips.tsx index c83b9764d2..4e3f83a88f 100644 --- a/web/src/app/util/Chips.tsx +++ b/web/src/app/util/Chips.tsx @@ -2,16 +2,8 @@ import React from 'react' import Chip, { ChipProps } from '@mui/material/Chip' import { useLocation } from 'wouter' import { useQuery, gql } from 'urql' -import { - RotateRight as RotationIcon, - Today as ScheduleIcon, - Webhook as WebhookIcon, -} from '@mui/icons-material' -import Avatar from '@mui/material/Avatar' -import { UserAvatar, ServiceAvatar } from './avatars' -import { SlackBW } from '../icons' -import { Query } from '../../schema' +import { ServiceAvatar } from './avatars' const serviceQuery = gql` query service($id: ID!) { @@ -54,116 +46,3 @@ export function ServiceChip(props: WithID): JSX.Element { /> ) } - -export function UserChip(props: WithID): JSX.Element { - const { id, ...rest } = props - const [, navigate] = useLocation() - - return ( - } - onClick={() => navigate(`/users/${id}`)} - {...rest} - /> - ) -} - -export function RotationChip(props: WithID): JSX.Element { - const { id, ...rest } = props - const [, navigate] = useLocation() - - return ( - - - - } - onClick={() => navigate(`/rotations/${id}`)} - {...rest} - /> - ) -} - -export function ScheduleChip(props: WithID): JSX.Element { - const { id, ...rest } = props - const [, navigate] = useLocation() - - return ( - - - - } - onClick={() => navigate(`/schedules/${id}`)} - {...rest} - /> - ) -} - -export function SlackChip(props: WithID): JSX.Element { - const { id: channelID, ...rest } = props - - const query = gql` - query ($id: ID!) { - slackChannel(id: $id) { - id - teamID - } - } - ` - - const [{ data, error }] = useQuery({ - query, - variables: { id: channelID }, - requestPolicy: 'cache-first', - }) - const teamID = data?.slackChannel?.teamID - - if (error) { - console.error(`Error querying slackChannel ${channelID}:`, error) - } - - const clickable = Boolean(channelID && teamID) - if (clickable) { - rest.onClick = () => - window.open( - `https://slack.com/app_redirect?channel=${channelID}&team=${teamID}`, - ) - } - - return ( - - - - } - {...rest} - /> - ) -} - -export function WebhookChip(props: WithID): JSX.Element { - const { id: urlStr, ...rest } = props - - const url = new URL(urlStr) - return ( - - - - } - label={url.hostname} - {...rest} - /> - ) -} From 2f19c08b267f6b9d6d78bd8cd208f70e55e07631 Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Mon, 13 May 2024 14:37:50 -0500 Subject: [PATCH 06/12] rename Chip -> ServiceChip --- .../alerts/CreateAlertDialog/StepContent/CreateAlertConfirm.tsx | 2 +- .../CreateAlertDialog/StepContent/CreateAlertServiceSelect.tsx | 2 +- web/src/app/util/{Chips.tsx => ServiceChip.tsx} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename web/src/app/util/{Chips.tsx => ServiceChip.tsx} (100%) diff --git a/web/src/app/alerts/CreateAlertDialog/StepContent/CreateAlertConfirm.tsx b/web/src/app/alerts/CreateAlertDialog/StepContent/CreateAlertConfirm.tsx index 2fc13843bd..beca0b3e01 100644 --- a/web/src/app/alerts/CreateAlertDialog/StepContent/CreateAlertConfirm.tsx +++ b/web/src/app/alerts/CreateAlertDialog/StepContent/CreateAlertConfirm.tsx @@ -1,7 +1,7 @@ import React, { ReactNode } from 'react' import { Typography, Grid, Divider } from '@mui/material' import makeStyles from '@mui/styles/makeStyles' -import { ServiceChip } from '../../../util/Chips' +import { ServiceChip } from '../../../util/ServiceChip' import { FormField } from '../../../forms' import Markdown from '../../../util/Markdown' diff --git a/web/src/app/alerts/CreateAlertDialog/StepContent/CreateAlertServiceSelect.tsx b/web/src/app/alerts/CreateAlertDialog/StepContent/CreateAlertServiceSelect.tsx index b48b2e1afa..228c09ca27 100644 --- a/web/src/app/alerts/CreateAlertDialog/StepContent/CreateAlertServiceSelect.tsx +++ b/web/src/app/alerts/CreateAlertDialog/StepContent/CreateAlertServiceSelect.tsx @@ -20,7 +20,7 @@ import makeStyles from '@mui/styles/makeStyles' import ServiceLabelFilterContainer from '../../../services/ServiceFilterContainer' import { Search as SearchIcon } from '@mui/icons-material' import { FavoriteIcon } from '../../../util/SetFavoriteButton' -import { ServiceChip } from '../../../util/Chips' +import { ServiceChip } from '../../../util/ServiceChip' import AddIcon from '@mui/icons-material/Add' import _ from 'lodash' import getServiceFilters from '../../../util/getServiceFilters' diff --git a/web/src/app/util/Chips.tsx b/web/src/app/util/ServiceChip.tsx similarity index 100% rename from web/src/app/util/Chips.tsx rename to web/src/app/util/ServiceChip.tsx From 324427f4356efb4cf70f11806b3f16f585706762 Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Mon, 13 May 2024 14:54:21 -0500 Subject: [PATCH 07/12] update favorites test --- web/src/cypress/e2e/favorites.cy.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/web/src/cypress/e2e/favorites.cy.ts b/web/src/cypress/e2e/favorites.cy.ts index d6f4ad11d3..fe18bf6467 100644 --- a/web/src/cypress/e2e/favorites.cy.ts +++ b/web/src/cypress/e2e/favorites.cy.ts @@ -114,8 +114,9 @@ function testFavorites(screen: ScreenFormat): void { cy.get('button').contains('Create Step').click() } - cy.get('[data-cy="rotations-step"]').click() - return cy.get('input[name=rotations]') + cy.dialogForm({ 'dest.type': 'Rotation' }) + + return cy.get('input[name=rotation-id]') }, ) @@ -135,7 +136,9 @@ function testFavorites(screen: ScreenFormat): void { } else { cy.get('button').contains('Create Step').click() } - return cy.get('input[name=schedules]') + + cy.dialogForm({ 'dest.type': 'Schedule' }) + return cy.get('input[name=schedule-id]') }, ) @@ -166,8 +169,9 @@ function testFavorites(screen: ScreenFormat): void { } else { cy.get('button').contains('Create Step').click() } - cy.get('[data-cy="users-step"]').click() - return cy.get('input[name=users]') + + cy.dialogForm({ 'dest.type': 'User' }) + return cy.get('input[name=user-id]') }, ) } From 1fb7e0912e4ff485a72f8619002b7b82264f75dd Mon Sep 17 00:00:00 2001 From: Forfold Date: Tue, 14 May 2024 13:42:58 -0700 Subject: [PATCH 08/12] remove console.log --- web/src/app/escalation-policies/PolicyStepEditDialogDest.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/src/app/escalation-policies/PolicyStepEditDialogDest.tsx b/web/src/app/escalation-policies/PolicyStepEditDialogDest.tsx index 211ac13d9d..4ecfd27d8a 100644 --- a/web/src/app/escalation-policies/PolicyStepEditDialogDest.tsx +++ b/web/src/app/escalation-policies/PolicyStepEditDialogDest.tsx @@ -73,8 +73,7 @@ function PolicyStepEditDialogDest( // - actions field has it's own validation // - errors on existing actions are not handled specially, and just display in the dialog (i.e., duplicates) // - the delay field has no validation, and is automatically clamped to the min/max values by the backend - const [a, errs] = splitErrorsByPath(editStepStatus.error, []) - console.log(a, errs, editStepStatus.error) + const [, errs] = splitErrorsByPath(editStepStatus.error, []) return ( Date: Tue, 14 May 2024 13:43:17 -0700 Subject: [PATCH 09/12] add checks after each add action --- .../cypress/e2e/escalationPolicySteps.cy.ts | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/web/src/cypress/e2e/escalationPolicySteps.cy.ts b/web/src/cypress/e2e/escalationPolicySteps.cy.ts index 3748875711..9cc67478b5 100644 --- a/web/src/cypress/e2e/escalationPolicySteps.cy.ts +++ b/web/src/cypress/e2e/escalationPolicySteps.cy.ts @@ -46,18 +46,42 @@ function testSteps(screen: ScreenFormat): void { cy.dialogTitle('Create Step') cy.dialogForm({ 'dest.type': 'Schedule', 'schedule-id': s1.name }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + s1.name, + ) cy.dialogForm({ 'schedule-id': s2.name }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + s2.name, + ) cy.dialogForm({ 'dest.type': 'User', 'user-id': u1.name }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + u1.name, + ) cy.dialogForm({ 'user-id': u2.name }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + u2.name, + ) cy.dialogForm({ 'dest.type': 'Rotation', 'rotation-id': r1.name }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + r1.name, + ) cy.dialogForm({ 'rotation-id': r2.name }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + r2.name, + ) cy.dialogForm({ delayMinutes: delay.toString() }) cy.dialogFinish('Submit') @@ -116,6 +140,10 @@ function testSteps(screen: ScreenFormat): void { delayMinutes: delay.toString(), }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + s1.name, + ) cy.dialogFinish('Submit') @@ -130,7 +158,7 @@ function testSteps(screen: ScreenFormat): void { }) }) - it('should add, click, and remove a slack channel', () => { + it.only('should add, click, and remove a slack channel', () => { cy.updateConfig({ Slack: { Enable: true } }) cy.reload() @@ -147,8 +175,17 @@ function testSteps(screen: ScreenFormat): void { 'slack-channel-id': 'general', }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + '#general', + ) + cy.dialogForm({ 'slack-channel-id': 'foobar' }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + '#foobar', + ) cy.dialogFinish('Submit') @@ -180,6 +217,9 @@ function testSteps(screen: ScreenFormat): void { .contains('a', '#foobar') .find('[data-testid=CancelIcon]') .click() + cy.get('div[role="dialog"] [data-testid=destination-chip]') + .contains('#foobar') + .should('not.exist') cy.dialogFinish('Submit') From 8b16d2921686a9f7b64fed91df5179ed50210253 Mon Sep 17 00:00:00 2001 From: Forfold Date: Tue, 14 May 2024 13:44:13 -0700 Subject: [PATCH 10/12] remove .only --- web/src/cypress/e2e/escalationPolicySteps.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/cypress/e2e/escalationPolicySteps.cy.ts b/web/src/cypress/e2e/escalationPolicySteps.cy.ts index 9cc67478b5..7fe3958943 100644 --- a/web/src/cypress/e2e/escalationPolicySteps.cy.ts +++ b/web/src/cypress/e2e/escalationPolicySteps.cy.ts @@ -158,7 +158,7 @@ function testSteps(screen: ScreenFormat): void { }) }) - it.only('should add, click, and remove a slack channel', () => { + it('should add, click, and remove a slack channel', () => { cy.updateConfig({ Slack: { Enable: true } }) cy.reload() From 2b59895e1d202bb7b8dd7680a1b39a85f831c5b7 Mon Sep 17 00:00:00 2001 From: Forfold Date: Wed, 15 May 2024 08:36:22 -0700 Subject: [PATCH 11/12] update material select tests --- web/src/cypress/e2e/escalationPolicies.cy.ts | 22 ++- web/src/cypress/e2e/materialSelect.cy.ts | 193 +++++++------------ 2 files changed, 92 insertions(+), 123 deletions(-) diff --git a/web/src/cypress/e2e/escalationPolicies.cy.ts b/web/src/cypress/e2e/escalationPolicies.cy.ts index ffde25d149..1e8790cc9a 100644 --- a/web/src/cypress/e2e/escalationPolicies.cy.ts +++ b/web/src/cypress/e2e/escalationPolicies.cy.ts @@ -42,11 +42,13 @@ function testEP(screen: ScreenFormat): void { cy.dialogFinish('Cancel') }) - it(`should create an EP when submitted`, () => { + it('should create an EP when submitted', () => { const name = 'SM EP ' + c.word({ length: 8 }) const description = c.word({ length: 10 }) const repeat = c.integer({ min: 0, max: 5 }).toString() + cy.visit('escalation-policies') + if (screen === 'mobile') { cy.pageFab() } else { @@ -55,9 +57,23 @@ function testEP(screen: ScreenFormat): void { cy.dialogTitle('Create Escalation Policy') cy.dialogForm({ name, description, repeat }) - cy.dialogFinish('Submit') - // should be on details page + // Clear repeat field + cy.dialogForm({ repeat: '' }) + cy.get('[role=dialog] #dialog-form input[name="repeat"]').should( + 'not.have.value', + repeat, + ) + + // Click out of repeat field - last known value is used + cy.get('[role=dialog] #dialog-form').click() + cy.get('[role=dialog] #dialog-form input[name="repeat"]').should( + 'have.value', + repeat, + ) + + // Should be on details page + cy.dialogFinish('Submit') cy.get('body').should('contain', name).should('contain', description) }) }) diff --git a/web/src/cypress/e2e/materialSelect.cy.ts b/web/src/cypress/e2e/materialSelect.cy.ts index 32df6e136f..8097cb0d9e 100644 --- a/web/src/cypress/e2e/materialSelect.cy.ts +++ b/web/src/cypress/e2e/materialSelect.cy.ts @@ -1,132 +1,85 @@ -import { Chance } from 'chance' import { testScreen } from '../support/e2e' import users from '../fixtures/users.json' -const c = new Chance() function testMaterialSelect(screen: ScreenFormat): void { - it('should display options with punctuation', () => { - cy.createRotation().then((r) => { - const u = users[3] - cy.visit(`rotations/${r.id}`) - if (screen === 'mobile') { - cy.pageFab() - } else { - cy.get('button').contains('Add User').click() - } - cy.dialogTitle('Add User') - cy.get('input[name=users]').click() - cy.focused().type(u.name.replace('.', ' ')) - cy.get('div[role=presentation]').contains(u.name) + let rot: Rotation + beforeEach(() => { + cy.createRotation().then((r: Rotation) => { + rot = r }) }) - describe('Clear Optional Fields', () => { - describe('Escalation Policy Steps', () => { - let ep: EP - beforeEach(() => { - cy.createEP().then((e: EP) => { - ep = e - return cy.visit(`escalation-policies/${ep.id}`) - }) - }) - - it('should clear fields and not reset with last values', () => { - const u1 = users[0] - const u2 = users[1] - - if (screen === 'mobile') { - cy.pageFab() - } else { - cy.get('button').contains('Create Step').click() - } - cy.dialogTitle('Create Step') - - // populate users - cy.get('button[data-cy="users-step"]').click() - cy.dialogForm({ users: [u1.name, u2.name] }) - - // clear field - cy.dialogForm({ users: '' }) - cy.get(`input[name="users"]`) - .should('not.contain', u1.name) - .should('not.contain', u2.name) - - // unfocus - cy.get(`input[name="users"]`).blur() - cy.get(`input[name="users"]`) - .should('not.contain', u1.name) - .should('not.contain', u2.name) - - cy.dialogFinish('Submit') - }) - }) + it('should display options with punctuation', () => { + const u = users[3] + cy.visit(`rotations/${rot.id}`) + + if (screen === 'mobile') { + cy.pageFab() + } else { + cy.get('button').contains('Add User').click() + } + + cy.dialogTitle('Add User') + cy.get('input[name=users]').click() + cy.focused().type(u.name.replace('.', ' ')) + cy.get('div[role=presentation]').contains(u.name) }) - describe('Clear Required Fields', () => { - describe('Escalation Policy', () => { - it('should clear EP repeat count, reset with default value', () => { - const defaultVal = '3' - cy.visit('escalation-policies') - - if (screen === 'mobile') { - cy.pageFab() - } else { - cy.get('button').contains('Create Escalation Policy').click() - } - - cy.dialogTitle('Create Escalation Policy') - - // Clears field - cy.dialogForm({ repeat: '' }) - cy.get('[role=dialog] #dialog-form input[name="repeat"]') - .should('not.have.value', defaultVal) - .blur() - - // Default value returns - cy.get('[role=dialog] #dialog-form').click() - cy.get('[role=dialog] #dialog-form input[name="repeat"]').should( - 'have.value', - defaultVal, - ) - - cy.dialogFinish('Cancel') - }) - - it('should clear EP repeat count, reset with last value', () => { - const name = 'SM EP ' + c.word({ length: 7 }) - const description = c.word({ length: 9 }) - const repeat = c.integer({ min: 0, max: 5 }).toString() - - cy.visit('escalation-policies') - - if (screen === 'mobile') { - cy.pageFab() - } else { - cy.get('button').contains('Create Escalation Policy').click() - } - - cy.dialogTitle('Create Escalation Policy') - cy.dialogForm({ name, description, repeat }) - - // Clears field - cy.dialogForm({ repeat: '' }) - cy.get('[role=dialog] #dialog-form input[name="repeat"]').should( - 'not.have.value', - repeat, - ) - - // Last value returns - cy.get('[role=dialog] #dialog-form').click() - cy.get('[role=dialog] #dialog-form input[name="repeat"]').should( - 'have.value', - repeat, - ) - - // Should be on details page - cy.dialogFinish('Submit') - cy.get('body').should('contain', name).should('contain', description) - }) - }) + it('should clear optional chips with multiple=true', () => { + const u1 = users[0] + const u2 = users[1] + cy.visit(`rotations/${rot.id}`) + + if (screen === 'mobile') { + cy.pageFab() + } else { + cy.get('button').contains('Add User').click() + } + + cy.dialogTitle('Add User') + + // populate users + cy.dialogForm({ users: [u1.name, u2.name] }) + + // clear field + cy.dialogForm({ users: '' }) + cy.get(`input[name="users"]`) + .should('not.contain', u1.name) + .should('not.contain', u2.name) + + // unfocus + cy.get(`input[name="users"]`).blur() + cy.get(`input[name="users"]`) + .should('not.contain', u1.name) + .should('not.contain', u2.name) + }) + + it('should clear required fields and reset with default value', () => { + const defaultVal = '3' + cy.visit('escalation-policies') + + if (screen === 'mobile') { + cy.pageFab() + } else { + cy.get('button').contains('Create Escalation Policy').click() + } + + cy.dialogTitle('Create Escalation Policy') + + // Clears field + cy.dialogForm({ repeat: '' }) + cy.get('[role=dialog] #dialog-form input[name="repeat"]') + .should('not.have.value', defaultVal) + .blur() + + // Default value returns + cy.get('[role=dialog] #dialog-form').click() + cy.get('[role=dialog] #dialog-form input[name="repeat"]').should( + 'have.value', + defaultVal, + ) + + cy.dialogFinish('Cancel') }) } From 3f55aae9fa7807a7c834bcca55a99c05974f3d47 Mon Sep 17 00:00:00 2001 From: Forfold Date: Wed, 15 May 2024 11:19:13 -0700 Subject: [PATCH 12/12] add checks for webhook create --- web/src/cypress/e2e/escalationPolicySteps.cy.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/src/cypress/e2e/escalationPolicySteps.cy.ts b/web/src/cypress/e2e/escalationPolicySteps.cy.ts index 7fe3958943..861e404b39 100644 --- a/web/src/cypress/e2e/escalationPolicySteps.cy.ts +++ b/web/src/cypress/e2e/escalationPolicySteps.cy.ts @@ -333,10 +333,20 @@ testScreen('Webhook Support', (screen: ScreenFormat) => { 'webhook-url': 'https://webhook.site', }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + 'webhook.site', + ) + cy.dialogForm({ 'webhook-url': 'https://example.com', }) cy.dialogClick('Add Action') + cy.get('div[role="dialog"] [data-testid=destination-chip]').should( + 'contain', + 'example.com', + ) + cy.dialogFinish('Submit') // verify data integrity