Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'Manage' tab to Digital Twins page preview #957

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Complete integration tests
  • Loading branch information
vanessa committed Oct 20, 2024
commit 517ad1bf1f67c9d0b80dc9526e20d6b8749430d7
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { useDispatch, useSelector } from 'react-redux';
import { RootState } from 'store/store';
import {
FileState,
getModifiedFiles,
saveAllFiles,
} from '../../../store/file.slice';
import {
Expand Down Expand Up @@ -46,8 +45,7 @@ function ReconfigureDialog({
const [openSaveDialog, setOpenSaveDialog] = useState(false);
const [openCancelDialog, setOpenCancelDialog] = useState(false);
const digitalTwin = useSelector(selectDigitalTwinByName(name));
const files: FileState[] = useSelector((state: RootState) => state.files);
const modifiedFiles = getModifiedFiles(files) || [];
const modifiedFiles = useSelector((state: RootState) => state.files);
const dispatch = useDispatch();

const handleSave = () => setOpenSaveDialog(true);
Expand Down
3 changes: 0 additions & 3 deletions client/src/preview/store/file.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,5 @@ const filesSlice = createSlice({
},
});

export const getModifiedFiles = (files: FileState[]) =>
files.filter((file) => file.isModified);

export const { addOrUpdateFile, saveAllFiles } = filesSlice.actions;
export default filesSlice.reducer;
8 changes: 4 additions & 4 deletions client/test/preview/__mocks__/global_mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ export const mockDigitalTwin: DigitalTwin = {
logError: jest.fn(),
stop: jest.fn(),
delete: jest.fn(),
getDescriptionFiles: jest.fn(),
getLifecycleFiles: jest.fn(),
getConfigFiles: jest.fn(),
getFileContent: jest.fn(),
getDescriptionFiles: jest.fn().mockResolvedValue(['descriptionFile']),
getLifecycleFiles: jest.fn().mockResolvedValue(['lifecycleFile']),
getConfigFiles: jest.fn().mockResolvedValue(['configFile']),
getFileContent: jest.fn().mockResolvedValue('fileContent'),
updateFileContent: jest.fn(),
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import Editor from 'preview/route/digitaltwins/editor/Editor';
import { render, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import {
combineReducers,
configureStore,
getDefaultMiddleware,
} from '@reduxjs/toolkit';
import { mockGitlabInstance } from 'test/preview/__mocks__/global_mocks';
import assetsReducer, { setAssets } from 'preview/store/assets.slice';
import digitalTwinReducer, { setDigitalTwin } from 'preview/store/digitalTwin.slice';
import snackbarSlice from 'preview/store/snackbar.slice';
import fileSlice, { addOrUpdateFile } from 'preview/store/file.slice';
import fileSlice, { FileState, addOrUpdateFile } from 'preview/store/file.slice';
import { Asset } from 'preview/components/asset/Asset';
import * as React from 'react';
import DigitalTwin from 'preview/util/gitlabDigitalTwin';

import { mockGitlabInstance } from 'test/preview/__mocks__/global_mocks';
import { handleFileClick } from 'preview/route/digitaltwins/editor/Sidebar';
describe('Editor', () => {
const preSetItems: Asset[] = [{ name: 'Asset 1', path: 'path/asset1' }];

Expand All @@ -37,7 +37,7 @@
digitalTwin.configFiles = ['config1.json', 'config2.json'];
digitalTwin.lifecycleFiles = ['lifecycle1.txt', 'lifecycle2.txt'];

const setupTest = () => {
const setupTest = async () => {
store.dispatch(setAssets(preSetItems));
store.dispatch(
setDigitalTwin({
Expand All @@ -48,15 +48,17 @@
store.dispatch(addOrUpdateFile(files[0]));
};

beforeEach(() => {
beforeEach(async () => {
setupTest();

React.act(() => {
render(
<Provider store={store}>
<Editor DTName={'Asset 1'} />
</Provider>,
);
await React.act(async () => {
await waitFor(() => {
render(
<Provider store={store}>
<Editor DTName={'Asset 1'} />
</Provider>,
);
});
});
});

Expand All @@ -78,4 +80,76 @@
expect(previewTab).toHaveAttribute('aria-selected', 'true');
expect(editorTab).toHaveAttribute('aria-selected', 'false');
});

it('should update state when a modified file is clicked', async () => {
const setFileName = jest.fn();
const setFileContent = jest.fn();
const setFileType = jest.fn();

const modifiedFiles = [{ name: 'file1.md', content: 'modified content', isModified: true }];

const digitalTwin = new DigitalTwin('Asset 1', mockGitlabInstance);
await React.act(async () => {

Check failure on line 92 in client/test/preview/integration/route/digitaltwins/editor/Editor.test.tsx

View workflow job for this annotation

GitHub Actions / Test react website

'digitalTwin' is already declared in the upper scope on line 36 column 9
store.dispatch(
setDigitalTwin({
assetName: 'Asset 1',
digitalTwin: digitalTwin,
})
);

await handleFileClick('file1.md', digitalTwin, setFileName, setFileContent, setFileType, modifiedFiles);
});

expect(setFileName).toHaveBeenCalledWith('file1.md');
expect(setFileContent).toHaveBeenCalledWith('modified content');
expect(setFileType).toHaveBeenCalledWith('md');
});

it('should fetch file content for an unmodified file', async () => {
const setFileName = jest.fn();
const setFileContent = jest.fn();
const setFileType = jest.fn();

const modifiedFiles: FileState[] = [];

const digitalTwin = new DigitalTwin('Asset 1', mockGitlabInstance);
digitalTwin.getFileContent = jest.fn().mockResolvedValueOnce('Fetched content');

Check failure on line 116 in client/test/preview/integration/route/digitaltwins/editor/Editor.test.tsx

View workflow job for this annotation

GitHub Actions / Test react website

'digitalTwin' is already declared in the upper scope on line 36 column 9

await React.act(async () => {
store.dispatch(
setDigitalTwin({
assetName: 'Asset 1',
digitalTwin: digitalTwin,
})
);
await handleFileClick('file1.md', digitalTwin, setFileName, setFileContent, setFileType, modifiedFiles);
});

expect(setFileName).toHaveBeenCalledWith('file1.md');
expect(setFileContent).toHaveBeenCalledWith('Fetched content');
expect(setFileType).toHaveBeenCalledWith('md');
});

it('should set error message when fetching file content fails', async () => {
const setFileName = jest.fn();
const setFileContent = jest.fn();
const setFileType = jest.fn();

const modifiedFiles: FileState[] = [];

const digitalTwin = new DigitalTwin('Asset 1', mockGitlabInstance);
digitalTwin.getFileContent = jest.fn().mockResolvedValueOnce(null);

Check failure on line 141 in client/test/preview/integration/route/digitaltwins/editor/Editor.test.tsx

View workflow job for this annotation

GitHub Actions / Test react website

'digitalTwin' is already declared in the upper scope on line 36 column 9

await React.act(async () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 3 locations. Consider refactoring.

store.dispatch(
setDigitalTwin({
assetName: 'Asset 1',
digitalTwin: digitalTwin,
})
);
await handleFileClick('file1.md', digitalTwin, setFileName, setFileContent, setFileType, modifiedFiles);
});

expect(setFileContent).toHaveBeenCalledWith('Error fetching file1.md content');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { AnyAction, Store, combineReducers, configureStore, getDefaultMiddleware } from "@reduxjs/toolkit";
import { setDigitalTwin } from "preview/store/digitalTwin.slice";
import DigitalTwin from "preview/util/gitlabDigitalTwin";
import * as React from "react";
import { mockGitlabInstance } from "test/preview/__mocks__/global_mocks";
import { Provider } from "react-redux";
import { render, screen } from "@testing-library/react";
import digitalTwinReducer from "preview/store/digitalTwin.slice";
import fileSlice, { addOrUpdateFile } from "preview/store/file.slice";
import PreviewTab from "preview/route/digitaltwins/editor/PreviewTab";

describe('PreviewTab', () => {

Check failure on line 12 in client/test/preview/integration/route/digitaltwins/editor/PreviewTab.test.tsx

View workflow job for this annotation

GitHub Actions / Test react website

Unexpected any. Specify a different type
let store: Store<any, AnyAction>;

beforeEach(async () => {
await React.act(async () => {
store = configureStore({
reducer: combineReducers({
digitalTwin: digitalTwinReducer,
files: fileSlice,
}),
middleware: getDefaultMiddleware({
serializableCheck: false,
}),
});

const digitalTwin = new DigitalTwin('Asset 1', mockGitlabInstance);
digitalTwin.descriptionFiles = ['file1.md', 'file2.md'];
digitalTwin.configFiles = ['config1.json', 'config2.json'];
digitalTwin.lifecycleFiles = ['lifecycle1.txt', 'lifecycle2.txt'];

store.dispatch(
setDigitalTwin({
assetName: 'Asset 1',
digitalTwin,
}),
);
});
});

afterEach(() => {
jest.clearAllMocks();
});

it('renders Markdown content using md.render', () => {
const markdownContent = '# Heading\nSome **bold** text.';

render(
<Provider store={store}>
<PreviewTab fileContent={markdownContent} fileType="md" />
</Provider>
);
expect(screen.getByText('Heading')).toBeInTheDocument();

expect(screen.getByText((content, element) =>
content.startsWith('Some') && element?.tagName === 'P'
)).toBeInTheDocument();

expect(screen.getByText((content, element) =>
content.startsWith('bold') && element?.tagName === 'STRONG'
)).toBeInTheDocument();
});



it('renders JSON content correctly in Preview tab', async () => {
const jsonFile = { name: 'config.json', content: '{"key": "value"}', isModified: false };

await React.act(async () => {
store.dispatch(addOrUpdateFile(jsonFile));
});

render(
<Provider store={store}>
<PreviewTab fileContent={jsonFile.content} fileType="json" />
</Provider>
);

expect(screen.getByText(/"key"/)).toBeInTheDocument();
expect(screen.getByText(/"value"/)).toBeInTheDocument();
});


it('renders YAML content correctly', () => {
const yamlFile = { name: 'config.yaml', content: 'key: value', isModified: false };

render(
<Provider store={store}>
<PreviewTab fileContent={yamlFile.content} fileType="yaml" />
</Provider>
);

expect(screen.getByText(/key:/)).toBeInTheDocument();
expect(screen.getByText(/value/)).toBeInTheDocument();
});

it('renders Bash content correctly', () => {
const bashFile = { name: 'script.sh', content: 'echo "Hello World"', isModified: false };

render(
<Provider store={store}>
<PreviewTab fileContent={bashFile.content} fileType="sh" />
</Provider>
);

expect(screen.getByText(/echo/)).toBeInTheDocument();
expect(screen.getByText(/"Hello World"/)).toBeInTheDocument();
});
});
Loading
Loading