Skip to content
16 changes: 13 additions & 3 deletions web/packages/design/src/Alert/Alert.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,23 @@ const kind = props => {
case 'outline-danger':
return {
background: fade(theme.colors.error.main, 0.1),
border: `${theme.radii[1]}px solid ${theme.colors.error.main}`,
border: `${theme.borders[2]} ${theme.colors.error.main}`,
borderRadius: `${theme.radii[3]}px`,
boxShadow: 'none',
justifyContent: 'normal',
};
case 'outline-info':
return {
background: fade(theme.colors.link, 0.1),
border: `${theme.radii[1]}px solid ${theme.colors.link}`,
background: fade(theme.colors.accent.main, 0.1),
border: `${theme.borders[2]} ${theme.colors.accent.main}`,
borderRadius: `${theme.radii[3]}px`,
boxShadow: 'none',
justifyContent: 'normal',
};
case 'outline-warn':
return {
background: fade(theme.colors.warning.main, 0.1),
border: `${theme.borders[2]} ${theme.colors.warning.main}`,
borderRadius: `${theme.radii[3]}px`,
boxShadow: 'none',
justifyContent: 'normal',
Expand Down Expand Up @@ -100,6 +108,7 @@ Alert.propTypes = {
'success',
'outline-danger',
'outline-info',
'outline-warn',
]),
...color.propTypes,
...space.propTypes,
Expand All @@ -121,3 +130,4 @@ export const OutlineDanger = props => (
<Alert kind="outline-danger" {...props} />
);
export const OutlineInfo = props => <Alert kind="outline-info" {...props} />;
export const OutlineWarn = props => <Alert kind="outline-warn" {...props} />;
2 changes: 2 additions & 0 deletions web/packages/design/src/Alert/Alert.story.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@ export const Alerts = () => (
<Alert kind="info">Some informational message</Alert>
<Alert kind="success">This is success</Alert>
<Alert kind="outline-info">Text align it yourself</Alert>
<Alert kind="outline-warn">Text align it yourself</Alert>
<Alert kind="outline-danger">Text align it yourself</Alert>
</Box>
);
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
createDiscoveryConfig,
} from 'teleport/services/discovery';
import useTeleport from 'teleport/useTeleport';
import { splitAwsIamArn } from 'teleport/services/integrations/aws';

import {
AutoEnrollDialog,
Expand Down Expand Up @@ -221,13 +222,11 @@ export function EnrollRdsDatabase() {
if (!requiredVpcsAndSubnets) {
try {
const { spec, name: integrationName } = agentMeta.awsIntegration;
const accountId = spec.roleArn
.split('arn:aws:iam::')[1]
.substring(0, 12);
const { awsAccountId } = splitAwsIamArn(spec.roleArn);
requiredVpcsAndSubnets =
await integrationService.fetchAwsRdsRequiredVpcs(integrationName, {
region: tableData.currRegion,
accountId,
accountId: awsAccountId,
});

setRequiredVpcs(requiredVpcsAndSubnets);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { render, screen, fireEvent } from 'design/utils/testing';
import { render, screen, fireEvent, waitFor } from 'design/utils/testing';
import userEvent from '@testing-library/user-event';

import {
Integration,
Expand All @@ -25,7 +26,7 @@ import {

import { EditAwsOidcIntegrationDialog } from './EditAwsOidcIntegrationDialog';

test('edit without s3 fields', async () => {
test('user acknowledging script was ran when s3 bucket fields are edited', async () => {
render(
<EditAwsOidcIntegrationDialog
close={() => null}
Expand All @@ -36,7 +37,7 @@ test('edit without s3 fields', async () => {
name: 'some-integration-name',
spec: {
roleArn: 'arn:aws:iam::123456789012:role/johndoe',
issuerS3Bucket: '',
issuerS3Bucket: 'test-value',
issuerS3Prefix: '',
},
statusCode: IntegrationStatusCode.Running,
Expand All @@ -45,18 +46,29 @@ test('edit without s3 fields', async () => {
);

// Initial state.
expect(screen.getByText(/required/i)).toBeInTheDocument();
expect(screen.queryByTestId('scriptbox')).not.toBeInTheDocument();
expect(screen.queryByTestId('checkbox')).not.toBeInTheDocument();
expect(
screen.queryByRole('button', { name: /generate command/i })
).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();

// Click on generate command:
// Fill in the s3 prefix field.
fireEvent.change(screen.getByPlaceholderText(/prefix/i), {
target: { value: 'test-value' },
});
await waitFor(() =>
expect(
screen.getByRole('button', { name: /generate command/i })
).toBeEnabled()
);
// When clicking on generate command:
// - script rendered
// - checkbox to confirm user has ran command
// - edit button replaces generate command button
// - save button still disabled
fireEvent.click(screen.getByRole('button', { name: /generate command/i }));
screen.getByRole('button', { name: /edit/i });
userEvent.click(screen.getByRole('button', { name: /generate command/i }));
await screen.findByRole('button', { name: /edit/i });
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
expect(
screen.queryByRole('button', { name: /generate command/i })
Expand All @@ -65,54 +77,153 @@ test('edit without s3 fields', async () => {
expect(screen.getByTestId('scriptbox')).toBeInTheDocument();

// Click on checkbox should enable save button and disable edit button.
fireEvent.click(screen.getByRole('checkbox'));
expect(screen.getByRole('button', { name: /save/i })).toBeEnabled();
userEvent.click(screen.getByRole('checkbox'));
await waitFor(() =>
expect(screen.getByRole('button', { name: /save/i })).toBeEnabled()
);
expect(screen.getByRole('button', { name: /edit/i })).toBeDisabled();

// Unchecking the checkbox should disable save button.
fireEvent.click(screen.getByRole('checkbox'));
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
userEvent.click(screen.getByRole('checkbox'));
await waitFor(() =>
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled()
);

// Click on edit, should replace it with generate command
fireEvent.click(screen.getByRole('button', { name: /edit/i }));
expect(
screen.getByRole('button', { name: /generate command/i })
).toBeEnabled();
userEvent.click(screen.getByRole('button', { name: /edit/i }));
await waitFor(() =>
expect(
screen.getByRole('button', { name: /generate command/i })
).toBeEnabled()
);
});

test('edit with s3 fields', async () => {
test('render warning on save when leaving s3 fields empty', async () => {
const edit = jest.fn(() => Promise.resolve());
render(
<EditAwsOidcIntegrationDialog
close={() => null}
edit={() => null}
integration={integration}
edit={edit}
integration={{
resourceType: 'integration',
kind: IntegrationKind.AwsOidc,
name: 'some-integration-name',
spec: {
roleArn: 'arn:aws:iam::123456789012:role/johndoe',
issuerS3Bucket: '',
issuerS3Prefix: '',
},
statusCode: IntegrationStatusCode.Running,
}}
/>
);

// Initial state.
expect(screen.queryByText(/required/i)).not.toBeInTheDocument();
expect(screen.queryByTestId('scriptbox')).not.toBeInTheDocument();
expect(screen.queryByTestId('checkbox')).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
expect(
screen.queryByRole('button', { name: /generate command/i })
).not.toBeInTheDocument();

// Changing role arn should not render generate command.
// Enable the generate command button by changing a field.
fireEvent.change(screen.getByPlaceholderText(/arn:aws:iam:/i), {
target: { value: 'something else' },
target: { value: 'arn:aws:iam::123456789012:role/someonelse' },
});
expect(screen.getByRole('button', { name: /save/i })).toBeEnabled();
await waitFor(() =>
expect(
screen.getByRole('button', { name: /generate command/i })
).toBeEnabled()
);

expect(screen.queryByTestId('checkbox')).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();

userEvent.click(screen.getByRole('button', { name: /generate command/i }));
await screen.findByRole('button', { name: /edit/i });
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();

userEvent.click(screen.getByTestId('checkbox'));
await waitFor(() =>
expect(screen.getByRole('button', { name: /save/i })).toBeEnabled()
);

// Clicking on save without defining s3 fields, should render
// a warning.
userEvent.click(screen.getByRole('button', { name: /save/i }));
await screen.findByText(/recommended to use an S3 bucket/i);
expect(edit).not.toHaveBeenCalled();

// Canceling and saving should re-render the warning.
userEvent.click(screen.getByRole('button', { name: /cancel/i }));
await screen.findByRole('button', { name: /save/i });

userEvent.click(screen.getByRole('button', { name: /save/i }));
await screen.findByText(/recommended to use an S3 bucket/i);

userEvent.click(screen.getByRole('button', { name: /continue/i }));
await waitFor(() => expect(edit).toHaveBeenCalledTimes(1));
});

test('render warning on save when deleting existing s3 fields', async () => {
const edit = jest.fn(() => Promise.resolve());
render(
<EditAwsOidcIntegrationDialog
close={() => null}
edit={edit}
integration={{
resourceType: 'integration',
kind: IntegrationKind.AwsOidc,
name: 'some-integration-name',
spec: {
roleArn: 'arn:aws:iam::123456789012:role/johndoe',
issuerS3Bucket: 'delete-me',
issuerS3Prefix: 'delete-me',
},
statusCode: IntegrationStatusCode.Running,
}}
/>
);

expect(
screen.queryByRole('button', { name: /generate command/i })
).not.toBeInTheDocument();

// Changing the s3 fields should render generate command.
// Delete the s3 fields.
fireEvent.change(screen.getByPlaceholderText(/bucket/i), {
target: { value: 's3-bucket-something' },
target: { value: '' },
});
fireEvent.click(screen.getByRole('button', { name: /generate command/i }));
fireEvent.change(screen.getByPlaceholderText(/prefix/i), {
target: { value: '' },
});
await waitFor(() =>
expect(
screen.getByRole('button', { name: /generate command/i })
).toBeEnabled()
);

expect(screen.queryByTestId('checkbox')).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();

userEvent.click(screen.getByRole('button', { name: /generate command/i }));
await screen.findByRole('button', { name: /edit/i });
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();

userEvent.click(screen.getByTestId('checkbox'));
await waitFor(() =>
expect(screen.getByRole('button', { name: /save/i })).toBeEnabled()
);

// Test for warning render.
userEvent.click(screen.getByRole('button', { name: /save/i }));
await screen.findByText(/recommended to use an S3 bucket/i);
expect(edit).not.toHaveBeenCalled();
expect(
screen.getByText(/recommended to use an S3 bucket/i)
).toBeInTheDocument();

userEvent.click(screen.getByRole('button', { name: /continue/i }));
await waitFor(() => expect(edit).toHaveBeenCalledTimes(1));
});

test('edit invalid fields', async () => {
Expand All @@ -131,22 +242,18 @@ test('edit invalid fields', async () => {
target: { value: 'role something else' },
});

fireEvent.click(screen.getByRole('button', { name: /save/i }));
expect(screen.getByText(/invalid role ARN format/i)).toBeInTheDocument();
await waitFor(() =>
expect(
screen.getByRole('button', { name: /generate command/i })
).toBeEnabled()
);

// invalid s3 fields
fireEvent.change(screen.getByPlaceholderText(/bucket/i), {
target: { value: '' },
});
fireEvent.change(screen.getByPlaceholderText(/prefix/i), {
target: { value: '' },
});
fireEvent.click(screen.getByRole('button', { name: /generate command/i }));
expect(screen.queryAllByText(/required/i)).toHaveLength(2);
userEvent.click(screen.getByRole('button', { name: /generate command/i }));
await screen.findByText(/invalid role ARN format/i);
});

test('edit submit', async () => {
const mockEditFn = jest.fn();
test('edit submit called with proper fields', async () => {
const mockEditFn = jest.fn(() => Promise.resolve());
render(
<EditAwsOidcIntegrationDialog
close={() => null}
Expand All @@ -170,9 +277,21 @@ test('edit submit', async () => {
target: { value: 'other-prefix' },
});

fireEvent.click(screen.getByRole('button', { name: /generate command/i }));
fireEvent.click(screen.getByRole('checkbox'));
fireEvent.click(screen.getByRole('button', { name: /save/i }));
await waitFor(() =>
expect(
screen.getByRole('button', { name: /generate command/i })
).toBeEnabled()
);

userEvent.click(screen.getByRole('button', { name: /generate command/i }));
await screen.findByRole('button', { name: /edit/i });

userEvent.click(screen.getByTestId('checkbox'));
await waitFor(() =>
expect(screen.getByRole('button', { name: /save/i })).toBeEnabled()
);
userEvent.click(screen.getByRole('button', { name: /save/i }));
await waitFor(() => expect(mockEditFn).toHaveBeenCalledTimes(1));

expect(mockEditFn).toHaveBeenCalledWith({
roleArn: 'arn:aws:iam::123456789011:role/other',
Expand Down
Loading