// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { API } from '@aws-amplify/api';
import { I18n } from '@aws-amplify/core';
import { queryByAttribute, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import ConnectionForm from '../connection/ConnectionForm';
import {
ConnectionControl,
CreatedBy,
GetConnectionResponse,
ListGreengrassCoreDevicesResponse,
MachineProtocol,
OsiPiAuthMode,
OsPlatform
} from '../../util/types';
import { API_NAME } from '../../util/utils';
// Mock API
const mockAPI = {
get: jest.fn(),
post: jest.fn()
};
jest.mock('@aws-amplify/api');
API.get = mockAPI.get;
API.post = mockAPI.post;
const getById = queryByAttribute.bind(null, 'id');
const opcDaResponse: GetConnectionResponse = {
connectionName: 'mock-connection-id',
greengrassCoreDeviceName: 'mock-greengrass-device-name',
protocol: MachineProtocol.OPCDA,
sendDataToIoTSiteWise: true,
sendDataToIoTTopic: true,
sendDataToKinesisDataStreams: true,
sendDataToTimestream: true,
sendDataToHistorian: true,
siteName: 'mock-site',
area: 'mock-area',
process: 'mock-process',
machineName: 'mock-machine',
logLevel: undefined,
opcDa: {
machineIp: '1.2.3.4',
serverName: 'mock-opcda-server-name',
iterations: 3,
interval: 3,
listTags: ['listTag1.*', 'listTag2.*'],
tags: ['tag1', 'tag2']
}
};
const opcUaResponse: GetConnectionResponse = {
connectionName: 'mock-connection-id',
greengrassCoreDeviceName: 'mock-greengrass-device-name',
protocol: MachineProtocol.OPCUA,
sendDataToIoTSiteWise: true,
sendDataToIoTTopic: true,
sendDataToKinesisDataStreams: true,
sendDataToTimestream: true,
sendDataToHistorian: true,
siteName: 'mock-site',
area: 'mock-area',
process: 'mock-process',
machineName: 'mock-machine',
logLevel: undefined,
opcUa: {
machineIp: '1.2.3.4',
serverName: 'mock-opcua-server-name'
}
};
const osiPiResponse: GetConnectionResponse = {
connectionName: 'mock-connection-id',
greengrassCoreDeviceName: 'mock-greengrass-device-name',
protocol: MachineProtocol.OSIPI,
sendDataToIoTSiteWise: true,
sendDataToIoTTopic: true,
sendDataToKinesisDataStreams: true,
sendDataToTimestream: true,
sendDataToHistorian: true,
siteName: 'mock-site',
area: 'mock-area',
process: 'mock-process',
machineName: 'mock-machine',
logLevel: undefined,
osiPi: {
apiUrl: 'https://osiPiServer/piwebapi',
verifySSL: true,
serverName: 'mock-server',
authMode: OsiPiAuthMode.BASIC,
username: 'mock-user',
password: 'mock-password',
requestFrequency: 5,
catchupFrequency: 0.1,
maxRequestDuration: 60,
queryOffset: 0,
tags: ['tag1', 'tag2']
}
};
const greengrassCoreDevicesResponse: ListGreengrassCoreDevicesResponse = {
greengrassCoreDevices: [
{ name: 'mock-greengrass-1', createdBy: CreatedBy.SYSTEM, numberOfConnections: 0, osPlatform: OsPlatform.LINUX },
{ name: 'mock-greengrass-2', createdBy: CreatedBy.USER, numberOfConnections: 1, osPlatform: OsPlatform.LINUX },
{ name: 'mock-greengrass-3', createdBy: CreatedBy.SYSTEM, numberOfConnections: 2, osPlatform: OsPlatform.LINUX },
{ name: 'mock-greengrass-4', createdBy: CreatedBy.USER, numberOfConnections: 3, osPlatform: OsPlatform.LINUX }
]
};
const error = { errorMessage: 'Failure' };
beforeEach(() => {
mockAPI.get.mockReset();
mockAPI.post.mockReset();
});
test('renders the connection form component for the new connection', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
const connectionForm = render(
} />
);
expect(connectionForm.container).toMatchSnapshot();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('renders the connection form component for the new connection when getting Greengrass core devices fails', async () => {
mockAPI.get.mockRejectedValueOnce(error);
render(
} />
);
expect(await screen.findByText(JSON.stringify(error))).not.toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('renders the connection form component for the update OPC DA connection', async () => {
mockAPI.get.mockResolvedValueOnce(opcDaResponse);
const connectionForm = render(
} />
);
const connectionName = (await screen.findByPlaceholderText(
I18n.get('placeholder.connection.name')
)) as HTMLInputElement;
expect(connectionName.value).toEqual('mock-connection-id');
expect(connectionForm.container).toMatchSnapshot();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(
API_NAME,
`/connections/${encodeURIComponent(opcDaResponse.connectionName)}`,
{}
);
});
test('renders the connection form component for the update OPC UA connection', async () => {
mockAPI.get.mockResolvedValueOnce(opcUaResponse);
const connectionForm = render(
} />
);
const connectionName = (await screen.findByPlaceholderText(
I18n.get('placeholder.connection.name')
)) as HTMLInputElement;
expect(connectionName.value).toEqual('mock-connection-id');
expect(connectionForm.container).toMatchSnapshot();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(
API_NAME,
`/connections/${encodeURIComponent(opcUaResponse.connectionName)}`,
{}
);
});
test('renders the connection form component for the update connection when getting a connection fails', async () => {
mockAPI.get.mockRejectedValueOnce(error);
render(
} />
);
expect(await screen.findByText(JSON.stringify(error))).not.toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(
API_NAME,
`/connections/${encodeURIComponent(opcUaResponse.connectionName)}`,
{}
);
});
test('tests cancel button', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
render(
} />
);
expect(screen.queryByText(I18n.get('cancel'))).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('cancel')));
expect(screen.queryByText(I18n.get('cancel'))).toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('tests handleValueChange function - OPC DA iterations for general change', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
const { getByPlaceholderText } = render(
} />
);
const input = getByPlaceholderText(I18n.get('placeholder.iterations'));
userEvent.type(input, '1');
expect((input as HTMLInputElement).value).toEqual('1');
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('tests handleValueChange function - sendDataToIoTSiteWise for checkbox change', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
render(
} />
);
userEvent.click(screen.getByLabelText(I18n.get('iot.sitewise')));
expect((document.getElementById('sendDataToIoTSiteWise') as HTMLInputElement).checked).toBeTruthy();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('tests handleValueChange function - sendDataToIoTTopic for checkbox change', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
render(
} />
);
userEvent.click(screen.getByLabelText(I18n.get('iot.topic')));
expect((document.getElementById('sendDataToIoTTopic') as HTMLInputElement).checked).toBeTruthy();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('tests handleValueChange function - sendDataToKinesisDataStreams for checkbox change', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
render(
} />
);
userEvent.click(screen.getByLabelText(I18n.get('kinesis.data.streams')));
expect((document.getElementById('sendDataToKinesisDataStreams') as HTMLInputElement).checked).toBeFalsy();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('tests handleValueChange function - sendDataToTimestream for checkbox change', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
render(
} />
);
userEvent.click(screen.getByLabelText(I18n.get('timestream')));
expect((document.getElementById('sendDataToTimestream') as HTMLInputElement).checked).toBeTruthy();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('tests handleValueChange function - sendDataToHistorian for checkbox change', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
render(
} />
);
userEvent.click(screen.getByLabelText(I18n.get('historian')));
expect((document.getElementById('sendDataToHistorian') as HTMLInputElement).checked).toBeTruthy();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('test handleValueChange function - OPC UA machineIp for OPC UA data change', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
const connectionForm = render(
} />
);
userEvent.selectOptions(getById(connectionForm.container, 'protocol') as HTMLElement, MachineProtocol.OPCUA);
expect(await screen.findByText(I18n.get('port'))).not.toBeNull();
const input = connectionForm.getByPlaceholderText(I18n.get('placeholder.machine.ip'));
userEvent.type(input, '1.2.3.4');
expect((input as HTMLInputElement).value).toEqual('1.2.3.4');
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('tests handleConnection function - OPC DA with errors', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
render(
} />
);
userEvent.click(screen.getByText(I18n.get('create')));
expect(await screen.findByText(/invalid.greengrass.core.device.name/)).not.toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('tests handleConnection function - OPC DA without errors', async () => {
mockAPI.get.mockResolvedValueOnce(opcDaResponse);
mockAPI.post.mockResolvedValueOnce({ connectionName: opcDaResponse.connectionName });
render(
} />
);
const connectionName = (await screen.findByPlaceholderText(
I18n.get('placeholder.connection.name')
)) as HTMLInputElement;
expect(connectionName.value).toEqual('mock-connection-id');
expect(await screen.findByText(I18n.get('update'))).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('update')));
expect(await screen.findByText(/info.message.update.connection.confirm/)).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('confirm')));
expect(await screen.findByText(/info.message.background.running/)).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('ok')));
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(
API_NAME,
`/connections/${encodeURIComponent(opcDaResponse.connectionName)}`,
{}
);
expect(mockAPI.post).toHaveBeenCalledTimes(1);
const copiedResponse = {
...opcDaResponse,
control: ConnectionControl.UPDATE
};
delete copiedResponse.opcDaListTags;
delete copiedResponse.opcDaTags;
expect(mockAPI.post).toHaveBeenCalledWith(API_NAME, '/connections', {
body: copiedResponse
});
});
test('tests handleConnection function - OPC DA with update failure', async () => {
mockAPI.get.mockResolvedValueOnce(opcDaResponse);
mockAPI.post.mockRejectedValueOnce({ message: 'POST failure' });
render(
} />
);
const connectionName = (await screen.findByPlaceholderText(
I18n.get('placeholder.connection.name')
)) as HTMLInputElement;
expect(connectionName.value).toEqual('mock-connection-id');
expect(await screen.findByText(I18n.get('update'))).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('update')));
expect(await screen.findByText(/info.message.update.connection.confirm/)).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('confirm')));
expect(await screen.findByText(/error.message.update.connection/)).not.toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(
API_NAME,
`/connections/${encodeURIComponent(opcDaResponse.connectionName)}`,
{}
);
expect(mockAPI.post).toHaveBeenCalledTimes(1);
const copiedResponse = {
...opcDaResponse,
control: ConnectionControl.UPDATE
};
delete copiedResponse.opcDaListTags;
delete copiedResponse.opcDaTags;
expect(mockAPI.post).toHaveBeenCalledWith(API_NAME, '/connections', {
body: copiedResponse
});
});
test('tests handleConnection function - OPC UA with errors', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse);
const connectionForm = render(
} />
);
userEvent.selectOptions(getById(connectionForm.container, 'protocol') as HTMLElement, MachineProtocol.OPCUA);
expect(await screen.findByText(I18n.get('port'))).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('create')));
expect(await screen.findByText(/invalid.greengrass.core.device.name/)).not.toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
});
test('tests handleConnection function - OPC UA with duplicated server name error when creating', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse).mockResolvedValueOnce({ key: 'mock' });
const connectionForm = render(
} />
);
userEvent.selectOptions(getById(connectionForm.container, 'protocol') as HTMLElement, MachineProtocol.OPCUA);
expect(await screen.findByText(I18n.get('server.name'))).not.toBeNull();
const input = connectionForm.getByPlaceholderText(I18n.get('placeholder.opcua.server.name'));
userEvent.type(input, 'duplicate');
expect((input as HTMLInputElement).value).toEqual('duplicate');
userEvent.click(screen.getByText(I18n.get('create')));
expect(await screen.findByText(/invalid.duplicated.server.name/)).not.toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(2);
expect(mockAPI.get).toHaveBeenNthCalledWith(1, API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
expect(mockAPI.get).toHaveBeenNthCalledWith(2, API_NAME, `/sitewise/${encodeURIComponent('duplicate')}`, {});
});
test('tests handleConnection function - OPC UA with duplicated server name error when updating', async () => {
mockAPI.get
.mockResolvedValueOnce(opcUaResponse) // GET /connections/:connectionName
.mockResolvedValueOnce({ key: 'mock' }) // GET /sitewise
.mockResolvedValueOnce({
opcUa: {
serverName: 'different-server'
}
}); // GET /connections/:connectionName
render(
} />
);
const connectionName = (await screen.findByPlaceholderText(
I18n.get('placeholder.connection.name')
)) as HTMLInputElement;
expect(connectionName.value).toEqual('mock-connection-id');
expect(await screen.findByText(I18n.get('update'))).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('update')));
expect(await screen.findByText(/invalid.duplicated.server.name/)).not.toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(3);
expect(mockAPI.get).toHaveBeenNthCalledWith(
1,
API_NAME,
`/connections/${encodeURIComponent(opcUaResponse.connectionName)}`,
{}
);
expect(mockAPI.get).toHaveBeenNthCalledWith(
2,
API_NAME,
`/sitewise/${encodeURIComponent(opcUaResponse.opcUa?.serverName as string)}`,
{}
);
expect(mockAPI.get).toHaveBeenNthCalledWith(
3,
API_NAME,
`/connections/${encodeURIComponent(opcUaResponse.connectionName)}`,
{}
);
});
test('tests handleConnection function - OPC UA failure to get server name', async () => {
mockAPI.get.mockResolvedValueOnce(greengrassCoreDevicesResponse).mockRejectedValueOnce(error);
const connectionForm = render(
} />
);
userEvent.selectOptions(getById(connectionForm.container, 'protocol') as HTMLElement, MachineProtocol.OPCUA);
expect(await screen.findByText(I18n.get('server.name'))).not.toBeNull();
const input = connectionForm.getByPlaceholderText(I18n.get('placeholder.opcua.server.name'));
userEvent.type(input, 'duplicate');
expect((input as HTMLInputElement).value).toEqual('duplicate');
userEvent.click(screen.getByText(I18n.get('create')));
expect(await screen.findByText(JSON.stringify(error))).not.toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(2);
expect(mockAPI.get).toHaveBeenNthCalledWith(1, API_NAME, '/greengrass', {
queryStringParameters: { nextToken: undefined }
});
expect(mockAPI.get).toHaveBeenNthCalledWith(2, API_NAME, `/sitewise/${encodeURIComponent('duplicate')}`, {});
});
test('tests handleConnection function - OSI PI without errors', async () => {
mockAPI.get.mockResolvedValueOnce(osiPiResponse);
mockAPI.post.mockResolvedValueOnce({ connectionName: osiPiResponse.connectionName });
render(
} />
);
const connectionName = (await screen.findByPlaceholderText(
I18n.get('placeholder.connection.name')
)) as HTMLInputElement;
expect(connectionName.value).toEqual('mock-connection-id');
expect(await screen.findByText(I18n.get('update'))).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('update')));
expect(await screen.findByText(/info.message.update.connection.confirm/)).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('confirm')));
expect(await screen.findByText(/info.message.background.running/)).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('ok')));
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(
API_NAME,
`/connections/${encodeURIComponent(osiPiResponse.connectionName)}`,
{}
);
expect(mockAPI.post).toHaveBeenCalledTimes(1);
const copiedResponse = {
...osiPiResponse,
control: ConnectionControl.UPDATE
};
delete copiedResponse.osiPiTags;
expect(mockAPI.post).toHaveBeenCalledWith(API_NAME, '/connections', {
body: copiedResponse
});
});
test('tests handleConnection function - OSI PI with update failure', async () => {
mockAPI.get.mockResolvedValueOnce(osiPiResponse);
mockAPI.post.mockRejectedValueOnce({ message: 'POST failure' });
render(
} />
);
const connectionName = (await screen.findByPlaceholderText(
I18n.get('placeholder.connection.name')
)) as HTMLInputElement;
expect(connectionName.value).toEqual('mock-connection-id');
expect(await screen.findByText(I18n.get('update'))).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('update')));
expect(await screen.findByText(/info.message.update.connection.confirm/)).not.toBeNull();
userEvent.click(screen.getByText(I18n.get('confirm')));
expect(await screen.findByText(/error.message.update.connection/)).not.toBeNull();
expect(mockAPI.get).toHaveBeenCalledTimes(1);
expect(mockAPI.get).toHaveBeenCalledWith(
API_NAME,
`/connections/${encodeURIComponent(osiPiResponse.connectionName)}`,
{}
);
expect(mockAPI.post).toHaveBeenCalledTimes(1);
const copiedResponse = {
...osiPiResponse,
control: ConnectionControl.UPDATE
};
delete copiedResponse.osiPiTags;
expect(mockAPI.post).toHaveBeenCalledWith(API_NAME, '/connections', {
body: copiedResponse
});
});